feature/init_member #1
18
Makefile
18
Makefile
|
@ -47,3 +47,21 @@ build-docker:
|
||||||
docker buildx build -t $(DOCKER_REPO):$(VERSION) --build-arg SSH_PRIVATE_KEY="$(cat ~/.ssh/ed_25519)" .
|
docker buildx build -t $(DOCKER_REPO):$(VERSION) --build-arg SSH_PRIVATE_KEY="$(cat ~/.ssh/ed_25519)" .
|
||||||
rm -rf Dockerfile
|
rm -rf Dockerfile
|
||||||
@echo "Generate core-api files successfully"
|
@echo "Generate core-api files successfully"
|
||||||
|
|
||||||
|
.PHONY: gen-model
|
||||||
|
gen-model: # 建立 rpc 資料庫
|
||||||
|
goctl model mysql ddl -c no -s ./generate/database/mysql/20230529020011_account_table.up.sql --style go_zero -d ./internal/model -i ''
|
||||||
|
goctl model mysql ddl -c no -s ./generate/database/mysql/20230529020011_account_uid_table.up.sql --style go_zero -d ./internal/model -i ''
|
||||||
|
goctl model mysql ddl -c no -s ./generate/database/mysql/20230529020011_user_table.up.sql --style go_zero -d ./internal/model -i ''
|
||||||
|
goctl model mysql ddl -c no -s ./generate/database/mysql/20230719061241_machine_node.up.sql --style go_zero -d ./internal/model -i ''
|
||||||
|
@echo "Generate model files successfully"
|
||||||
|
# 只產生 Model 剩下的要自己撰寫,連欄位名稱也是
|
||||||
|
goctl model mongo -t AutoId -c --dir ./internal/model/mongo --style go_zero
|
||||||
|
|
||||||
|
#goctl model mysql ddl -s ./generate/database/mysql/20240819090248_create_role_permission_table.up.sql --style go_zero -d ./internal/model -i '' (沒cache)
|
||||||
|
#goctl model mysql ddl -c no -s ./generate/database/mysql/20240816014305_create_permission_table.up.sql --style go_zero -d ./internal/model -i '' (有cache)
|
||||||
|
|
||||||
|
#.PHONY: mock-gen
|
||||||
|
#mock-gen: # 建立 mock 資料
|
||||||
|
# mockgen -source=./order.go -destination=../../repository/mock/order.go -package=mock
|
||||||
|
# @echo "Generate model files successfully"
|
|
@ -3,6 +3,11 @@ syntax = "proto3";
|
||||||
package member;
|
package member;
|
||||||
option go_package="./member";
|
option go_package="./member";
|
||||||
|
|
||||||
|
// OKResp
|
||||||
|
message OKResp {}
|
||||||
|
// NoneReq
|
||||||
|
message NoneReq {}
|
||||||
|
|
||||||
// ================ enum ================
|
// ================ enum ================
|
||||||
enum VerifyType {
|
enum VerifyType {
|
||||||
VERIFY_NONE = 0; // 初始(異常)
|
VERIFY_NONE = 0; // 初始(異常)
|
||||||
|
@ -27,11 +32,6 @@ enum MemberStatus {
|
||||||
STATUS_GA = 6; // GA 已綁定
|
STATUS_GA = 6; // GA 已綁定
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Gender {
|
|
||||||
GENDER_NONE = 0; // 初始(未提供)
|
|
||||||
GENDER_MALE = 1; // 男性
|
|
||||||
GENDER_FEMALE = 2; // 女性
|
|
||||||
}
|
|
||||||
// ================ enum ================
|
// ================ enum ================
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,17 +41,6 @@ message Pager {
|
||||||
int64 size=2;
|
int64 size=2;
|
||||||
int64 index=3;
|
int64 index=3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Response {
|
|
||||||
BaseResp status=1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message BaseResp {
|
|
||||||
string code = 1;
|
|
||||||
string message = 2;
|
|
||||||
string error = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================ common ================
|
// ================ common ================
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,8 +70,7 @@ message CreateUserInfoReq {
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetAccountInfoResp {
|
message GetAccountInfoResp {
|
||||||
BaseResp status = 1;
|
CreateLoginUserReq data = 1;
|
||||||
CreateLoginUserReq data = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUserInfoReq 不處理邏輯給不給改,這裡只關新增修改刪除
|
// UpdateUserInfoReq 不處理邏輯給不給改,這裡只關新增修改刪除
|
||||||
|
@ -91,24 +79,18 @@ message UpdateUserInfoReq {
|
||||||
optional string language = 2;
|
optional string language = 2;
|
||||||
optional string currency = 3;
|
optional string currency = 3;
|
||||||
optional string nick_name = 4;
|
optional string nick_name = 4;
|
||||||
optional uint32 gender = 5;
|
optional VerifyType verify_type = 5;
|
||||||
optional int64 birthday = 6;
|
optional AlarmType alarm_type = 6;
|
||||||
optional VerifyType verify_type = 7;
|
optional MemberStatus status = 7;
|
||||||
optional AlarmType alarm_type = 8;
|
|
||||||
optional MemberStatus status = 9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetUIDByAccountReq {
|
message GetUIDByAccountReq {
|
||||||
string account = 1;
|
string account = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UID {
|
|
||||||
string uid = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetUidByAccountResp {
|
message GetUidByAccountResp {
|
||||||
BaseResp status = 1;
|
string uid = 1;
|
||||||
UID data = 2;
|
string account =2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateTokenReq {
|
message UpdateTokenReq {
|
||||||
|
@ -126,8 +108,7 @@ message VerifyCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
message GenerateRefreshCodeResp {
|
message GenerateRefreshCodeResp {
|
||||||
BaseResp status = 1;
|
VerifyCode data = 1;
|
||||||
VerifyCode data = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message VerifyRefreshCodeReq {
|
message VerifyRefreshCodeReq {
|
||||||
|
@ -158,8 +139,7 @@ message UserInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetUserInfoResp {
|
message GetUserInfoResp {
|
||||||
BaseResp status = 1;
|
UserInfo data = 1;
|
||||||
UserInfo data = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListUserInfoReq {
|
message ListUserInfoReq {
|
||||||
|
@ -173,29 +153,28 @@ message ListUserInfoReq {
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListUserInfoResp {
|
message ListUserInfoResp {
|
||||||
BaseResp status = 1;
|
repeated UserInfo data = 1;
|
||||||
repeated UserInfo data = 2;
|
Pager page =2;
|
||||||
Pager page =3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
service Account {
|
service Account {
|
||||||
// CreateUserAccount 建立帳號與密碼 -> 可登入,但可不可以做其他事情看業務流程,也可以只註冊就好
|
// CreateUserAccount 建立帳號與密碼 -> 可登入,但可不可以做其他事情看業務流程,也可以只註冊就好
|
||||||
rpc CreateUserAccount(CreateLoginUserReq) returns(Response);
|
rpc CreateUserAccount(CreateLoginUserReq) returns(OKResp);
|
||||||
// GetUserAccountInfo 取得帳號密碼資料
|
// GetUserAccountInfo 取得帳號密碼資料
|
||||||
rpc GetUserAccountInfo(GetUIDByAccountReq) returns(GetAccountInfoResp);
|
rpc GetUserAccountInfo(GetUIDByAccountReq) returns(GetAccountInfoResp);
|
||||||
// UpdateUserToken 更新密碼
|
// UpdateUserToken 更新密碼
|
||||||
rpc UpdateUserToken(UpdateTokenReq) returns(Response);
|
rpc UpdateUserToken(UpdateTokenReq) returns(OKResp);
|
||||||
// GetUidByAccount 用帳號換取 UID
|
// GetUidByAccount 用帳號換取 UID
|
||||||
rpc GetUidByAccount(GetUIDByAccountReq) returns(GetUidByAccountResp);
|
rpc GetUidByAccount(GetUIDByAccountReq) returns(GetUidByAccountResp);
|
||||||
// BindAccount 綁定帳號 -> account bind to UID
|
// BindAccount 綁定帳號 -> account bind to UID
|
||||||
rpc BindAccount(BindingUserReq) returns(Response);
|
rpc BindAccount(BindingUserReq) returns(OKResp);
|
||||||
// BindUserInfo 初次,綁定 User Info
|
// BindUserInfo 初次,綁定 User Info
|
||||||
rpc BindUserInfo(CreateUserInfoReq) returns(Response);
|
rpc BindUserInfo(CreateUserInfoReq) returns(OKResp);
|
||||||
// UpdateUserInfo 更新 User Info
|
// UpdateUserInfo 更新 User Info
|
||||||
rpc UpdateUserInfo(UpdateUserInfoReq) returns(Response);
|
rpc UpdateUserInfo(UpdateUserInfoReq) returns(OKResp);
|
||||||
// UpdateStatus 修改狀態
|
// UpdateStatus 修改狀態
|
||||||
rpc UpdateStatus(UpdateStatusReq) returns(Response);
|
rpc UpdateStatus(UpdateStatusReq) returns(OKResp);
|
||||||
// GetUserInfo 取得會員資訊
|
// GetUserInfo 取得會員資訊
|
||||||
rpc GetUserInfo(GetUserInfoReq) returns(GetUserInfoResp);
|
rpc GetUserInfo(GetUserInfoReq) returns(GetUserInfoResp);
|
||||||
// ListMember 取得會員列表
|
// ListMember 取得會員列表
|
||||||
|
@ -203,7 +182,7 @@ service Account {
|
||||||
// GenerateRefreshCode 這個帳號驗證碼(十分鐘),通用的
|
// GenerateRefreshCode 這個帳號驗證碼(十分鐘),通用的
|
||||||
rpc GenerateRefreshCode(GenerateRefreshCodeReq) returns(GenerateRefreshCodeResp);
|
rpc GenerateRefreshCode(GenerateRefreshCodeReq) returns(GenerateRefreshCodeResp);
|
||||||
// VerifyRefreshCode 驗證忘記密碼 token
|
// VerifyRefreshCode 驗證忘記密碼 token
|
||||||
rpc VerifyRefreshCode(VerifyRefreshCodeReq) returns(Response);
|
rpc VerifyRefreshCode(VerifyRefreshCodeReq) returns(OKResp);
|
||||||
|
|
||||||
}
|
}
|
||||||
// ================ account ================
|
// ================ account ================
|
12
go.mod
12
go.mod
|
@ -3,12 +3,17 @@ module app-cloudep-member-server
|
||||||
go 1.22.3
|
go 1.22.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
code.30cm.net/digimon/library-go/errors v1.0.1
|
||||||
|
code.30cm.net/digimon/library-go/validator v1.0.0
|
||||||
|
github.com/bwmarrin/snowflake v0.3.0
|
||||||
github.com/zeromicro/go-zero v1.7.0
|
github.com/zeromicro/go-zero v1.7.0
|
||||||
|
go.uber.org/goleak v1.3.0
|
||||||
google.golang.org/grpc v1.65.0
|
google.golang.org/grpc v1.65.0
|
||||||
google.golang.org/protobuf v1.34.2
|
google.golang.org/protobuf v1.34.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
@ -18,11 +23,16 @@ require (
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
github.com/fatih/color v1.17.0 // indirect
|
github.com/fatih/color v1.17.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/swag v0.22.4 // indirect
|
github.com/go-openapi/swag v0.22.4 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
@ -33,6 +43,7 @@ require (
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
@ -65,6 +76,7 @@ require (
|
||||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
go.uber.org/zap v1.24.0 // indirect
|
go.uber.org/zap v1.24.0 // indirect
|
||||||
|
golang.org/x/crypto v0.25.0 // indirect
|
||||||
golang.org/x/net v0.27.0 // indirect
|
golang.org/x/net v0.27.0 // indirect
|
||||||
golang.org/x/oauth2 v0.20.0 // indirect
|
golang.org/x/oauth2 v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
|
|
|
@ -1,7 +1,33 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import "github.com/zeromicro/go-zero/zrpc"
|
import (
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||||
|
"github.com/zeromicro/go-zero/zrpc"
|
||||||
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
zrpc.RpcServerConf
|
zrpc.RpcServerConf
|
||||||
|
// 加上 DB 結構體
|
||||||
|
DB struct {
|
||||||
|
DsnString string
|
||||||
|
}
|
||||||
|
// 快取
|
||||||
|
Cache cache.CacheConf
|
||||||
|
// 密碼加密層數
|
||||||
|
Bcrypt struct {
|
||||||
|
Cost int
|
||||||
|
}
|
||||||
|
// Redis Cluster
|
||||||
|
RedisCluster redis.RedisConf
|
||||||
|
|
||||||
|
Mongo struct {
|
||||||
|
Schema string
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
Database string
|
||||||
|
Collection string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultPageSize = 100
|
||||||
|
DefaultPageIndex = 1
|
||||||
|
Scope = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
const InitAutoId = 1000000
|
||||||
|
const DefaultReferralCodeLen = 8
|
||||||
|
|
||||||
|
var ConvertTable = [...]string{
|
||||||
|
"O", "D", "W", "X", "Y",
|
||||||
|
"G", "B", "C", "H", "E",
|
||||||
|
"F", "A", "Q", "I", "J",
|
||||||
|
"L", "M", "N", "Z", "K",
|
||||||
|
"P", "V", "R", "S", "T",
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import (
|
||||||
|
ers "code.30cm.net/digimon/library-go/errors"
|
||||||
|
"code.30cm.net/digimon/library-go/errors/code"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 12 represents Scope
|
||||||
|
// 100 represents Category
|
||||||
|
// 9 represents Detail error code
|
||||||
|
// full code 12009 只會有 系統以及錯誤碼,category 是給系統判定用的
|
||||||
|
// 目前 Scope 以及分類要系統共用,係向的錯誤各自服務實作就好
|
||||||
|
|
||||||
|
const (
|
||||||
|
UIDOutOfRangeErrorCode = iota + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// UIDOutOfRangeErrorCodeError 20001 Token 簽名錯誤
|
||||||
|
func UIDOutOfRangeErrorCodeError(msg string) *ers.LibError {
|
||||||
|
return ers.NewErr(code.CloudEPMember, code.CatInput, UIDOutOfRangeErrorCode, msg)
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type UIDGenerateUseCase interface {
|
||||||
|
Generate(ctx context.Context) (string, error)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
ers "code.30cm.net/digimon/library-go/errors"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
func TimeoutMiddleware(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
|
||||||
|
|
||||||
|
newCtx, cancelCtx := context.WithTimeout(ctx, defaultTimeout)
|
||||||
|
defer func() {
|
||||||
|
cancelCtx()
|
||||||
|
|
||||||
|
if errors.Is(newCtx.Err(), context.DeadlineExceeded) {
|
||||||
|
err = ers.SystemTimeoutError(info.FullMethod)
|
||||||
|
logx.Errorf("Method: %s, request %v, timeout: %d", info.FullMethod, req, defaultTimeout)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package snowflake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NewNodeError struct {
|
||||||
|
machineNodeID int64
|
||||||
|
startTime time.Time
|
||||||
|
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NewNodeError) Error() string {
|
||||||
|
return fmt.Sprintf("new node fail machineNodeID: %d, startTime: %s, err: %v",
|
||||||
|
e.machineNodeID, e.startTime, e.Err)
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package snowflake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewNodeError_Error(t *testing.T) {
|
||||||
|
startTime, err := time.Parse(time.DateOnly, "2023-07-20")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
machineNodeID int64
|
||||||
|
startTime time.Time
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "success",
|
||||||
|
fields: fields{
|
||||||
|
machineNodeID: 1,
|
||||||
|
startTime: startTime,
|
||||||
|
Err: nil,
|
||||||
|
},
|
||||||
|
want: fmt.Sprintf("new node fail machineNodeID: %d, startTime: %s, err: %v",
|
||||||
|
1, "2023-07-20 00:00:00 +0000 UTC", nil),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := &NewNodeError{
|
||||||
|
machineNodeID: tt.fields.machineNodeID,
|
||||||
|
startTime: tt.fields.startTime,
|
||||||
|
Err: tt.fields.Err,
|
||||||
|
}
|
||||||
|
if got := e.Error(); got != tt.want {
|
||||||
|
t.Errorf("Error() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
###
|
||||||
|
# snowflake
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "yt.com/backend/common.git/snowflake"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 量級超過1024 再來做解決
|
|
@ -0,0 +1,81 @@
|
||||||
|
package snowflake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/snowflake"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
|
// Snowflake provides a way to NewNode for Generate UID.
|
||||||
|
type Snowflake struct {
|
||||||
|
machineNodeID int64
|
||||||
|
|
||||||
|
startTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option is the options type to configure Snowflake.
|
||||||
|
type Option func(*Snowflake)
|
||||||
|
|
||||||
|
// New returns a new Snowflake instance with the provided options.
|
||||||
|
func New(opts ...Option) *Snowflake {
|
||||||
|
s := &Snowflake{
|
||||||
|
// default machine 1
|
||||||
|
machineNodeID: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMachineNodeID adds machineID total 10bit = 1024 machine number.
|
||||||
|
func WithMachineNodeID(machineNodeID int64) Option {
|
||||||
|
return func(snowflake *Snowflake) {
|
||||||
|
snowflake.machineNodeID = machineNodeID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithStartTime adds snowflake start timestamp in milliseconds.
|
||||||
|
func WithStartTime(startTime time.Time) Option {
|
||||||
|
return func(snowflake *Snowflake) {
|
||||||
|
snowflake.startTime = startTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNowDate return nowTodayDate e.g. 2023-07-20 00:00:00 +0000 UTC.
|
||||||
|
func GetNowDate() (time.Time, error) {
|
||||||
|
startTime := time.Now().UTC().Format(time.DateOnly)
|
||||||
|
|
||||||
|
st, err := time.Parse(time.DateOnly, startTime)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, fmt.Errorf("time.Parse failed :%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return st, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNode return snowflake node use Generate UID.
|
||||||
|
func (s *Snowflake) NewNode() (*snowflake.Node, error) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
snowflake.Epoch = s.startTime.UnixMilli()
|
||||||
|
|
||||||
|
node, err := snowflake.NewNode(s.machineNodeID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("snowflake.NewNode, failed :%w",
|
||||||
|
&NewNodeError{
|
||||||
|
machineNodeID: s.machineNodeID,
|
||||||
|
startTime: s.startTime,
|
||||||
|
Err: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return node, nil
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
package snowflake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/goleak"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
leak := flag.Bool("leak", false, "use leak detector")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *leak {
|
||||||
|
goleak.VerifyTestMain(m)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSnowflake(t *testing.T) {
|
||||||
|
st, err := GetNowDate()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
machineNodeID int64
|
||||||
|
startTime time.Time
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *Snowflake
|
||||||
|
|
||||||
|
wantDeepEqualErr bool
|
||||||
|
wantNewNodeErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "success",
|
||||||
|
args: args{
|
||||||
|
machineNodeID: 10,
|
||||||
|
startTime: st,
|
||||||
|
},
|
||||||
|
want: &Snowflake{
|
||||||
|
machineNodeID: 10,
|
||||||
|
startTime: st,
|
||||||
|
},
|
||||||
|
wantDeepEqualErr: false,
|
||||||
|
wantNewNodeErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "failed machine node ID negative number",
|
||||||
|
args: args{
|
||||||
|
machineNodeID: -1,
|
||||||
|
startTime: time.Time{},
|
||||||
|
},
|
||||||
|
want: &Snowflake{
|
||||||
|
machineNodeID: -1,
|
||||||
|
startTime: time.Time{},
|
||||||
|
},
|
||||||
|
wantDeepEqualErr: false,
|
||||||
|
wantNewNodeErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "failed snowflake struct field by machine node ID",
|
||||||
|
args: args{
|
||||||
|
machineNodeID: 10,
|
||||||
|
startTime: st,
|
||||||
|
},
|
||||||
|
want: &Snowflake{
|
||||||
|
machineNodeID: 2,
|
||||||
|
startTime: st,
|
||||||
|
},
|
||||||
|
wantDeepEqualErr: true,
|
||||||
|
wantNewNodeErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "failed snowflake struct field by startTime",
|
||||||
|
args: args{
|
||||||
|
machineNodeID: 2,
|
||||||
|
startTime: st,
|
||||||
|
},
|
||||||
|
want: &Snowflake{
|
||||||
|
machineNodeID: 2,
|
||||||
|
startTime: time.Time{},
|
||||||
|
},
|
||||||
|
wantDeepEqualErr: true,
|
||||||
|
wantNewNodeErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := New(
|
||||||
|
WithMachineNodeID(tt.args.machineNodeID),
|
||||||
|
WithStartTime(tt.args.startTime),
|
||||||
|
)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, tt.want) != tt.wantDeepEqualErr {
|
||||||
|
t.Errorf("Snowflake.New() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
node, err := got.NewNode()
|
||||||
|
if (err != nil) != tt.wantNewNodeErr {
|
||||||
|
t.Errorf("NewNode() = %v, want %v", err != nil, tt.wantNewNodeErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
id := node.Generate().Int64()
|
||||||
|
if id <= 0 {
|
||||||
|
t.Errorf("node.Generate().Int64() = %v, want %s", id, "id > 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSnowflake(b *testing.B) {
|
||||||
|
st, err := GetNowDate()
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
snowflake := New(
|
||||||
|
WithMachineNodeID(1),
|
||||||
|
WithStartTime(st),
|
||||||
|
)
|
||||||
|
|
||||||
|
node, err := snowflake.NewNode()
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
node.Generate().Int64()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ AccountModel = (*customAccountModel)(nil)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// AccountModel is an interface to be customized, add more methods here,
|
||||||
|
// and implement the added methods in customAccountModel.
|
||||||
|
AccountModel interface {
|
||||||
|
accountModel
|
||||||
|
}
|
||||||
|
|
||||||
|
customAccountModel struct {
|
||||||
|
*defaultAccountModel
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewAccountModel returns a model for the database table.
|
||||||
|
func NewAccountModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) AccountModel {
|
||||||
|
return &customAccountModel{
|
||||||
|
defaultAccountModel: newAccountModel(conn, c, opts...),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accountFieldNames = builder.RawFieldNames(&Account{})
|
||||||
|
accountRows = strings.Join(accountFieldNames, ",")
|
||||||
|
accountRowsExpectAutoSet = strings.Join(stringx.Remove(accountFieldNames, "`id`"), ",")
|
||||||
|
accountRowsWithPlaceHolder = strings.Join(stringx.Remove(accountFieldNames, "`id`"), "=?,") + "=?"
|
||||||
|
|
||||||
|
cacheAccountIdPrefix = "cache:account:id:"
|
||||||
|
cacheAccountAccountPrefix = "cache:account:account:"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
accountModel interface {
|
||||||
|
Insert(ctx context.Context, data *Account) (sql.Result, error)
|
||||||
|
FindOne(ctx context.Context, id int64) (*Account, error)
|
||||||
|
FindOneByAccount(ctx context.Context, account string) (*Account, error)
|
||||||
|
Update(ctx context.Context, data *Account) error
|
||||||
|
Delete(ctx context.Context, id int64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultAccountModel struct {
|
||||||
|
sqlc.CachedConn
|
||||||
|
table string
|
||||||
|
}
|
||||||
|
|
||||||
|
Account struct {
|
||||||
|
Id int64 `db:"id"`
|
||||||
|
Account string `db:"account"`
|
||||||
|
Token string `db:"token"`
|
||||||
|
Platform int64 `db:"platform"` // 平台類型 1. ark 2. google
|
||||||
|
CreateTime int64 `db:"create_time"`
|
||||||
|
UpdateTime int64 `db:"update_time"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAccountModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultAccountModel {
|
||||||
|
return &defaultAccountModel{
|
||||||
|
CachedConn: sqlc.NewConn(conn, c, opts...),
|
||||||
|
table: "`account`",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountModel) withSession(session sqlx.Session) *defaultAccountModel {
|
||||||
|
return &defaultAccountModel{
|
||||||
|
CachedConn: m.CachedConn.WithSession(session),
|
||||||
|
table: "`account`",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountModel) Delete(ctx context.Context, id int64) error {
|
||||||
|
data, err := m.FindOne(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
accountAccountKey := fmt.Sprintf("%s%v", cacheAccountAccountPrefix, data.Account)
|
||||||
|
accountIdKey := fmt.Sprintf("%s%v", cacheAccountIdPrefix, id)
|
||||||
|
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
|
||||||
|
return conn.ExecCtx(ctx, query, id)
|
||||||
|
}, accountAccountKey, accountIdKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountModel) FindOne(ctx context.Context, id int64) (*Account, error) {
|
||||||
|
accountIdKey := fmt.Sprintf("%s%v", cacheAccountIdPrefix, id)
|
||||||
|
var resp Account
|
||||||
|
err := m.QueryRowCtx(ctx, &resp, accountIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", accountRows, m.table)
|
||||||
|
return conn.QueryRowCtx(ctx, v, query, id)
|
||||||
|
})
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountModel) FindOneByAccount(ctx context.Context, account string) (*Account, error) {
|
||||||
|
accountAccountKey := fmt.Sprintf("%s%v", cacheAccountAccountPrefix, account)
|
||||||
|
var resp Account
|
||||||
|
err := m.QueryRowIndexCtx(ctx, &resp, accountAccountKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `account` = ? limit 1", accountRows, m.table)
|
||||||
|
if err := conn.QueryRowCtx(ctx, &resp, query, account); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.Id, nil
|
||||||
|
}, m.queryPrimary)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountModel) Insert(ctx context.Context, data *Account) (sql.Result, error) {
|
||||||
|
accountAccountKey := fmt.Sprintf("%s%v", cacheAccountAccountPrefix, data.Account)
|
||||||
|
accountIdKey := fmt.Sprintf("%s%v", cacheAccountIdPrefix, data.Id)
|
||||||
|
ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, accountRowsExpectAutoSet)
|
||||||
|
return conn.ExecCtx(ctx, query, data.Account, data.Token, data.Platform, data.CreateTime, data.UpdateTime)
|
||||||
|
}, accountAccountKey, accountIdKey)
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountModel) Update(ctx context.Context, newData *Account) error {
|
||||||
|
data, err := m.FindOne(ctx, newData.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
accountAccountKey := fmt.Sprintf("%s%v", cacheAccountAccountPrefix, data.Account)
|
||||||
|
accountIdKey := fmt.Sprintf("%s%v", cacheAccountIdPrefix, data.Id)
|
||||||
|
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, accountRowsWithPlaceHolder)
|
||||||
|
return conn.ExecCtx(ctx, query, newData.Account, newData.Token, newData.Platform, newData.CreateTime, newData.UpdateTime, newData.Id)
|
||||||
|
}, accountAccountKey, accountIdKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountModel) formatPrimary(primary any) string {
|
||||||
|
return fmt.Sprintf("%s%v", cacheAccountIdPrefix, primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", accountRows, m.table)
|
||||||
|
return conn.QueryRowCtx(ctx, v, query, primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountModel) tableName() string {
|
||||||
|
return m.table
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ AccountToUidModel = (*customAccountToUidModel)(nil)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// AccountToUidModel is an interface to be customized, add more methods here,
|
||||||
|
// and implement the added methods in customAccountToUidModel.
|
||||||
|
AccountToUidModel interface {
|
||||||
|
accountToUidModel
|
||||||
|
}
|
||||||
|
|
||||||
|
customAccountToUidModel struct {
|
||||||
|
*defaultAccountToUidModel
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewAccountToUidModel returns a model for the database table.
|
||||||
|
func NewAccountToUidModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) AccountToUidModel {
|
||||||
|
return &customAccountToUidModel{
|
||||||
|
defaultAccountToUidModel: newAccountToUidModel(conn, c, opts...),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accountToUidFieldNames = builder.RawFieldNames(&AccountToUid{})
|
||||||
|
accountToUidRows = strings.Join(accountToUidFieldNames, ",")
|
||||||
|
accountToUidRowsExpectAutoSet = strings.Join(stringx.Remove(accountToUidFieldNames, "`id`"), ",")
|
||||||
|
accountToUidRowsWithPlaceHolder = strings.Join(stringx.Remove(accountToUidFieldNames, "`id`"), "=?,") + "=?"
|
||||||
|
|
||||||
|
cacheAccountToUidIdPrefix = "cache:accountToUid:id:"
|
||||||
|
cacheAccountToUidAccountPrefix = "cache:accountToUid:account:"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
accountToUidModel interface {
|
||||||
|
Insert(ctx context.Context, data *AccountToUid) (sql.Result, error)
|
||||||
|
FindOne(ctx context.Context, id int64) (*AccountToUid, error)
|
||||||
|
FindOneByAccount(ctx context.Context, account string) (*AccountToUid, error)
|
||||||
|
Update(ctx context.Context, data *AccountToUid) error
|
||||||
|
Delete(ctx context.Context, id int64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultAccountToUidModel struct {
|
||||||
|
sqlc.CachedConn
|
||||||
|
table string
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountToUid struct {
|
||||||
|
Id int64 `db:"id"`
|
||||||
|
Account string `db:"account"`
|
||||||
|
Uid string `db:"uid"`
|
||||||
|
Type int64 `db:"type"` // 1 手機 2 信箱 3 自定義帳號
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAccountToUidModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultAccountToUidModel {
|
||||||
|
return &defaultAccountToUidModel{
|
||||||
|
CachedConn: sqlc.NewConn(conn, c, opts...),
|
||||||
|
table: "`account_to_uid`",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountToUidModel) withSession(session sqlx.Session) *defaultAccountToUidModel {
|
||||||
|
return &defaultAccountToUidModel{
|
||||||
|
CachedConn: m.CachedConn.WithSession(session),
|
||||||
|
table: "`account_to_uid`",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountToUidModel) Delete(ctx context.Context, id int64) error {
|
||||||
|
data, err := m.FindOne(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
accountToUidAccountKey := fmt.Sprintf("%s%v", cacheAccountToUidAccountPrefix, data.Account)
|
||||||
|
accountToUidIdKey := fmt.Sprintf("%s%v", cacheAccountToUidIdPrefix, id)
|
||||||
|
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
|
||||||
|
return conn.ExecCtx(ctx, query, id)
|
||||||
|
}, accountToUidAccountKey, accountToUidIdKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountToUidModel) FindOne(ctx context.Context, id int64) (*AccountToUid, error) {
|
||||||
|
accountToUidIdKey := fmt.Sprintf("%s%v", cacheAccountToUidIdPrefix, id)
|
||||||
|
var resp AccountToUid
|
||||||
|
err := m.QueryRowCtx(ctx, &resp, accountToUidIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", accountToUidRows, m.table)
|
||||||
|
return conn.QueryRowCtx(ctx, v, query, id)
|
||||||
|
})
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountToUidModel) FindOneByAccount(ctx context.Context, account string) (*AccountToUid, error) {
|
||||||
|
accountToUidAccountKey := fmt.Sprintf("%s%v", cacheAccountToUidAccountPrefix, account)
|
||||||
|
var resp AccountToUid
|
||||||
|
err := m.QueryRowIndexCtx(ctx, &resp, accountToUidAccountKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `account` = ? limit 1", accountToUidRows, m.table)
|
||||||
|
if err := conn.QueryRowCtx(ctx, &resp, query, account); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.Id, nil
|
||||||
|
}, m.queryPrimary)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountToUidModel) Insert(ctx context.Context, data *AccountToUid) (sql.Result, error) {
|
||||||
|
accountToUidAccountKey := fmt.Sprintf("%s%v", cacheAccountToUidAccountPrefix, data.Account)
|
||||||
|
accountToUidIdKey := fmt.Sprintf("%s%v", cacheAccountToUidIdPrefix, data.Id)
|
||||||
|
ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?)", m.table, accountToUidRowsExpectAutoSet)
|
||||||
|
return conn.ExecCtx(ctx, query, data.Account, data.Uid, data.Type)
|
||||||
|
}, accountToUidAccountKey, accountToUidIdKey)
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountToUidModel) Update(ctx context.Context, newData *AccountToUid) error {
|
||||||
|
data, err := m.FindOne(ctx, newData.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
accountToUidAccountKey := fmt.Sprintf("%s%v", cacheAccountToUidAccountPrefix, data.Account)
|
||||||
|
accountToUidIdKey := fmt.Sprintf("%s%v", cacheAccountToUidIdPrefix, data.Id)
|
||||||
|
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, accountToUidRowsWithPlaceHolder)
|
||||||
|
return conn.ExecCtx(ctx, query, newData.Account, newData.Uid, newData.Type, newData.Id)
|
||||||
|
}, accountToUidAccountKey, accountToUidIdKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountToUidModel) formatPrimary(primary any) string {
|
||||||
|
return fmt.Sprintf("%s%v", cacheAccountToUidIdPrefix, primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountToUidModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", accountToUidRows, m.table)
|
||||||
|
return conn.QueryRowCtx(ctx, v, query, primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAccountToUidModel) tableName() string {
|
||||||
|
return m.table
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ MachineNodeModel = (*customMachineNodeModel)(nil)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// MachineNodeModel is an interface to be customized, add more methods here,
|
||||||
|
// and implement the added methods in customMachineNodeModel.
|
||||||
|
MachineNodeModel interface {
|
||||||
|
machineNodeModel
|
||||||
|
}
|
||||||
|
|
||||||
|
customMachineNodeModel struct {
|
||||||
|
*defaultMachineNodeModel
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewMachineNodeModel returns a model for the database table.
|
||||||
|
func NewMachineNodeModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) MachineNodeModel {
|
||||||
|
return &customMachineNodeModel{
|
||||||
|
defaultMachineNodeModel: newMachineNodeModel(conn, c, opts...),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
machineNodeFieldNames = builder.RawFieldNames(&MachineNode{})
|
||||||
|
machineNodeRows = strings.Join(machineNodeFieldNames, ",")
|
||||||
|
machineNodeRowsExpectAutoSet = strings.Join(stringx.Remove(machineNodeFieldNames, "`id`"), ",")
|
||||||
|
machineNodeRowsWithPlaceHolder = strings.Join(stringx.Remove(machineNodeFieldNames, "`id`"), "=?,") + "=?"
|
||||||
|
|
||||||
|
cacheMachineNodeIdPrefix = "cache:machineNode:id:"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
machineNodeModel interface {
|
||||||
|
Insert(ctx context.Context, data *MachineNode) (sql.Result, error)
|
||||||
|
FindOne(ctx context.Context, id int64) (*MachineNode, error)
|
||||||
|
Update(ctx context.Context, data *MachineNode) error
|
||||||
|
Delete(ctx context.Context, id int64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultMachineNodeModel struct {
|
||||||
|
sqlc.CachedConn
|
||||||
|
table string
|
||||||
|
}
|
||||||
|
|
||||||
|
MachineNode struct {
|
||||||
|
Id int64 `db:"id"` // 流水號
|
||||||
|
CreateTime int64 `db:"create_time"` // 創建時間
|
||||||
|
UpdateTime int64 `db:"update_time"` // 更新時間
|
||||||
|
HostName string `db:"host_name"` // host name
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func newMachineNodeModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultMachineNodeModel {
|
||||||
|
return &defaultMachineNodeModel{
|
||||||
|
CachedConn: sqlc.NewConn(conn, c, opts...),
|
||||||
|
table: "`machine_node`",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultMachineNodeModel) withSession(session sqlx.Session) *defaultMachineNodeModel {
|
||||||
|
return &defaultMachineNodeModel{
|
||||||
|
CachedConn: m.CachedConn.WithSession(session),
|
||||||
|
table: "`machine_node`",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultMachineNodeModel) Delete(ctx context.Context, id int64) error {
|
||||||
|
machineNodeIdKey := fmt.Sprintf("%s%v", cacheMachineNodeIdPrefix, id)
|
||||||
|
_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
|
||||||
|
return conn.ExecCtx(ctx, query, id)
|
||||||
|
}, machineNodeIdKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultMachineNodeModel) FindOne(ctx context.Context, id int64) (*MachineNode, error) {
|
||||||
|
machineNodeIdKey := fmt.Sprintf("%s%v", cacheMachineNodeIdPrefix, id)
|
||||||
|
var resp MachineNode
|
||||||
|
err := m.QueryRowCtx(ctx, &resp, machineNodeIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", machineNodeRows, m.table)
|
||||||
|
return conn.QueryRowCtx(ctx, v, query, id)
|
||||||
|
})
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultMachineNodeModel) Insert(ctx context.Context, data *MachineNode) (sql.Result, error) {
|
||||||
|
machineNodeIdKey := fmt.Sprintf("%s%v", cacheMachineNodeIdPrefix, data.Id)
|
||||||
|
ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?)", m.table, machineNodeRowsExpectAutoSet)
|
||||||
|
return conn.ExecCtx(ctx, query, data.CreateTime, data.UpdateTime, data.HostName)
|
||||||
|
}, machineNodeIdKey)
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultMachineNodeModel) Update(ctx context.Context, data *MachineNode) error {
|
||||||
|
machineNodeIdKey := fmt.Sprintf("%s%v", cacheMachineNodeIdPrefix, data.Id)
|
||||||
|
_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, machineNodeRowsWithPlaceHolder)
|
||||||
|
return conn.ExecCtx(ctx, query, data.CreateTime, data.UpdateTime, data.HostName, data.Id)
|
||||||
|
}, machineNodeIdKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultMachineNodeModel) formatPrimary(primary any) string {
|
||||||
|
return fmt.Sprintf("%s%v", cacheMachineNodeIdPrefix, primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultMachineNodeModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", machineNodeRows, m.table)
|
||||||
|
return conn.QueryRowCtx(ctx, v, query, primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultMachineNodeModel) tableName() string {
|
||||||
|
return m.table
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/monc"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ AutoIdModel = (*customAutoIdModel)(nil)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// AutoIdModel is an interface to be customized, add more methods here,
|
||||||
|
// and implement the added methods in customAutoIdModel.
|
||||||
|
AutoIdModel interface {
|
||||||
|
autoIdModel
|
||||||
|
Inc(ctx context.Context, data *AutoId) error
|
||||||
|
}
|
||||||
|
|
||||||
|
customAutoIdModel struct {
|
||||||
|
*defaultAutoIdModel
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewAutoIdModel returns a model for the mongo.
|
||||||
|
func NewAutoIdModel(url, db, collection string, c cache.CacheConf) AutoIdModel {
|
||||||
|
conn := monc.MustNewModel(url, db, collection, c)
|
||||||
|
return &customAutoIdModel{
|
||||||
|
defaultAutoIdModel: newDefaultAutoIdModel(conn),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *customAutoIdModel) Inc(ctx context.Context, data *AutoId) error {
|
||||||
|
filter := bson.M{"admin": "auto_id"}
|
||||||
|
update := bson.M{"$inc": bson.M{"counter": 1}}
|
||||||
|
|
||||||
|
key := prefixAutoIdCacheKey + "count"
|
||||||
|
|
||||||
|
err := m.conn.FindOneAndUpdate(ctx, key, data, filter, update,
|
||||||
|
options.FindOneAndUpdate().SetUpsert(true),
|
||||||
|
options.FindOneAndUpdate().SetReturnDocument(options.After),
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/monc"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var prefixAutoIdCacheKey = "cache:autoId:"
|
||||||
|
|
||||||
|
type autoIdModel interface {
|
||||||
|
Insert(ctx context.Context, data *AutoId) error
|
||||||
|
FindOne(ctx context.Context, id string) (*AutoId, error)
|
||||||
|
Update(ctx context.Context, data *AutoId) (*mongo.UpdateResult, error)
|
||||||
|
Delete(ctx context.Context, id string) (int64, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultAutoIdModel struct {
|
||||||
|
conn *monc.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultAutoIdModel(conn *monc.Model) *defaultAutoIdModel {
|
||||||
|
return &defaultAutoIdModel{conn: conn}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAutoIdModel) Insert(ctx context.Context, data *AutoId) error {
|
||||||
|
if data.ID.IsZero() {
|
||||||
|
data.ID = primitive.NewObjectID()
|
||||||
|
data.CreateAt = time.Now()
|
||||||
|
data.UpdateAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
key := prefixAutoIdCacheKey + data.ID.Hex()
|
||||||
|
_, err := m.conn.InsertOne(ctx, key, data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAutoIdModel) FindOne(ctx context.Context, id string) (*AutoId, error) {
|
||||||
|
oid, err := primitive.ObjectIDFromHex(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidObjectId
|
||||||
|
}
|
||||||
|
|
||||||
|
var data AutoId
|
||||||
|
key := prefixAutoIdCacheKey + id
|
||||||
|
err = m.conn.FindOne(ctx, key, &data, bson.M{"_id": oid})
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &data, nil
|
||||||
|
case monc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAutoIdModel) Update(ctx context.Context, data *AutoId) (*mongo.UpdateResult, error) {
|
||||||
|
data.UpdateAt = time.Now()
|
||||||
|
key := prefixAutoIdCacheKey + data.ID.Hex()
|
||||||
|
res, err := m.conn.UpdateOne(ctx, key, bson.M{"_id": data.ID}, bson.M{"$set": data})
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultAutoIdModel) Delete(ctx context.Context, id string) (int64, error) {
|
||||||
|
oid, err := primitive.ObjectIDFromHex(id)
|
||||||
|
if err != nil {
|
||||||
|
return 0, ErrInvalidObjectId
|
||||||
|
}
|
||||||
|
key := prefixAutoIdCacheKey + id
|
||||||
|
res, err := m.conn.DeleteOne(ctx, key, bson.M{"_id": oid})
|
||||||
|
return res, err
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AutoId struct {
|
||||||
|
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
|
||||||
|
Name string `bson:"name,omitempty" json:"name,omitempty"`
|
||||||
|
Count uint64 `bson:"count,omitempty" json:"count,omitempty"`
|
||||||
|
UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
|
||||||
|
CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/mon"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotFound = mon.ErrNotFound
|
||||||
|
ErrInvalidObjectId = errors.New("invalid objectId")
|
||||||
|
)
|
|
@ -0,0 +1,27 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ UserTableModel = (*customUserTableModel)(nil)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// UserTableModel is an interface to be customized, add more methods here,
|
||||||
|
// and implement the added methods in customUserTableModel.
|
||||||
|
UserTableModel interface {
|
||||||
|
userTableModel
|
||||||
|
}
|
||||||
|
|
||||||
|
customUserTableModel struct {
|
||||||
|
*defaultUserTableModel
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewUserTableModel returns a model for the database table.
|
||||||
|
func NewUserTableModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserTableModel {
|
||||||
|
return &customUserTableModel{
|
||||||
|
defaultUserTableModel: newUserTableModel(conn, c, opts...),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
userTableFieldNames = builder.RawFieldNames(&UserTable{})
|
||||||
|
userTableRows = strings.Join(userTableFieldNames, ",")
|
||||||
|
userTableRowsExpectAutoSet = strings.Join(stringx.Remove(userTableFieldNames, "`id`"), ",")
|
||||||
|
userTableRowsWithPlaceHolder = strings.Join(stringx.Remove(userTableFieldNames, "`id`"), "=?,") + "=?"
|
||||||
|
|
||||||
|
cacheUserTableIdPrefix = "cache:userTable:id:"
|
||||||
|
cacheUserTableUidPrefix = "cache:userTable:uid:"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
userTableModel interface {
|
||||||
|
Insert(ctx context.Context, data *UserTable) (sql.Result, error)
|
||||||
|
FindOne(ctx context.Context, id int64) (*UserTable, error)
|
||||||
|
FindOneByUid(ctx context.Context, uid string) (*UserTable, error)
|
||||||
|
Update(ctx context.Context, data *UserTable) error
|
||||||
|
Delete(ctx context.Context, id int64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultUserTableModel struct {
|
||||||
|
sqlc.CachedConn
|
||||||
|
table string
|
||||||
|
}
|
||||||
|
|
||||||
|
UserTable struct {
|
||||||
|
Id int64 `db:"id"`
|
||||||
|
VerifyType int64 `db:"verify_type"` // 驗證類型 0. 異常 1.信箱 2.手機 3. GA 4.不驗證
|
||||||
|
AlarmType int64 `db:"alarm_type"` // 告警狀態 0. 異常 1. 正常(未告警) 2.系統告警中
|
||||||
|
Status int64 `db:"status"` // 會員狀態 0. 異常 1. 尚未驗證 2. 啟用 3. 停權中 4. 信箱以驗證 5. 手機以驗證 6. GA 以驗證
|
||||||
|
Uid string `db:"uid"` // 唯一辨識碼
|
||||||
|
NickName string `db:"nick_name"` // 暱稱
|
||||||
|
Language string `db:"language"` // 使用語言
|
||||||
|
Currency string `db:"currency"` // 使用幣別
|
||||||
|
Avatar string `db:"avatar"` // 會員頭像
|
||||||
|
CreateTime int64 `db:"create_time"`
|
||||||
|
UpdateTime int64 `db:"update_time"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func newUserTableModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultUserTableModel {
|
||||||
|
return &defaultUserTableModel{
|
||||||
|
CachedConn: sqlc.NewConn(conn, c, opts...),
|
||||||
|
table: "`user_table`",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultUserTableModel) withSession(session sqlx.Session) *defaultUserTableModel {
|
||||||
|
return &defaultUserTableModel{
|
||||||
|
CachedConn: m.CachedConn.WithSession(session),
|
||||||
|
table: "`user_table`",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultUserTableModel) Delete(ctx context.Context, id int64) error {
|
||||||
|
data, err := m.FindOne(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userTableIdKey := fmt.Sprintf("%s%v", cacheUserTableIdPrefix, id)
|
||||||
|
userTableUidKey := fmt.Sprintf("%s%v", cacheUserTableUidPrefix, data.Uid)
|
||||||
|
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
|
||||||
|
return conn.ExecCtx(ctx, query, id)
|
||||||
|
}, userTableIdKey, userTableUidKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultUserTableModel) FindOne(ctx context.Context, id int64) (*UserTable, error) {
|
||||||
|
userTableIdKey := fmt.Sprintf("%s%v", cacheUserTableIdPrefix, id)
|
||||||
|
var resp UserTable
|
||||||
|
err := m.QueryRowCtx(ctx, &resp, userTableIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", userTableRows, m.table)
|
||||||
|
return conn.QueryRowCtx(ctx, v, query, id)
|
||||||
|
})
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultUserTableModel) FindOneByUid(ctx context.Context, uid string) (*UserTable, error) {
|
||||||
|
userTableUidKey := fmt.Sprintf("%s%v", cacheUserTableUidPrefix, uid)
|
||||||
|
var resp UserTable
|
||||||
|
err := m.QueryRowIndexCtx(ctx, &resp, userTableUidKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `uid` = ? limit 1", userTableRows, m.table)
|
||||||
|
if err := conn.QueryRowCtx(ctx, &resp, query, uid); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.Id, nil
|
||||||
|
}, m.queryPrimary)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultUserTableModel) Insert(ctx context.Context, data *UserTable) (sql.Result, error) {
|
||||||
|
userTableIdKey := fmt.Sprintf("%s%v", cacheUserTableIdPrefix, data.Id)
|
||||||
|
userTableUidKey := fmt.Sprintf("%s%v", cacheUserTableUidPrefix, data.Uid)
|
||||||
|
ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, userTableRowsExpectAutoSet)
|
||||||
|
return conn.ExecCtx(ctx, query, data.VerifyType, data.AlarmType, data.Status, data.Uid, data.NickName, data.Language, data.Currency, data.Avatar, data.CreateTime, data.UpdateTime)
|
||||||
|
}, userTableIdKey, userTableUidKey)
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultUserTableModel) Update(ctx context.Context, newData *UserTable) error {
|
||||||
|
data, err := m.FindOne(ctx, newData.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userTableIdKey := fmt.Sprintf("%s%v", cacheUserTableIdPrefix, data.Id)
|
||||||
|
userTableUidKey := fmt.Sprintf("%s%v", cacheUserTableUidPrefix, data.Uid)
|
||||||
|
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userTableRowsWithPlaceHolder)
|
||||||
|
return conn.ExecCtx(ctx, query, newData.VerifyType, newData.AlarmType, newData.Status, newData.Uid, newData.NickName, newData.Language, newData.Currency, newData.Avatar, newData.CreateTime, newData.UpdateTime, newData.Id)
|
||||||
|
}, userTableIdKey, userTableUidKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultUserTableModel) formatPrimary(primary any) string {
|
||||||
|
return fmt.Sprintf("%s%v", cacheUserTableIdPrefix, primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultUserTableModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error {
|
||||||
|
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", userTableRows, m.table)
|
||||||
|
return conn.QueryRowCtx(ctx, v, query, primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultUserTableModel) tableName() string {
|
||||||
|
return m.table
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import "github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
|
||||||
|
var ErrNotFound = sqlx.ErrNotFound
|
|
@ -1,13 +1,56 @@
|
||||||
package svc
|
package svc
|
||||||
|
|
||||||
import "app-cloudep-member-server/internal/config"
|
import (
|
||||||
|
"app-cloudep-member-server/internal/config"
|
||||||
|
"app-cloudep-member-server/internal/domain"
|
||||||
|
domainUC "app-cloudep-member-server/internal/domain/usecase"
|
||||||
|
"app-cloudep-member-server/internal/model"
|
||||||
|
mgo "app-cloudep-member-server/internal/model/mongo"
|
||||||
|
"app-cloudep-member-server/internal/usecase"
|
||||||
|
ers "code.30cm.net/digimon/library-go/errors"
|
||||||
|
vi "code.30cm.net/digimon/library-go/validator"
|
||||||
|
"fmt"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
type ServiceContext struct {
|
type ServiceContext struct {
|
||||||
Config config.Config
|
Config config.Config
|
||||||
|
Validate vi.Validate
|
||||||
|
|
||||||
|
AccountModel model.AccountModel
|
||||||
|
UserModel model.UserTableModel
|
||||||
|
AccountToUidModel model.AccountToUidModel
|
||||||
|
GenUIDUseCase domainUC.UIDGenerateUseCase
|
||||||
|
Redis redis.Redis
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceContext(c config.Config) *ServiceContext {
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
// 設置
|
||||||
|
ers.Scope = domain.Scope
|
||||||
|
|
||||||
|
sqlConn := sqlx.NewMysql(c.DB.DsnString)
|
||||||
|
newRedis, err := redis.NewRedis(c.RedisCluster, redis.Cluster())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
mongo := mgo.NewAutoIdModel(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"%s://%s:%s@%s:%s", c.Mongo.Schema,
|
||||||
|
c.Mongo.User, c.Mongo.Password, c.Mongo.Host, c.Mongo.Port),
|
||||||
|
c.Mongo.Database,
|
||||||
|
c.Mongo.Collection,
|
||||||
|
c.Cache)
|
||||||
|
|
||||||
return &ServiceContext{
|
return &ServiceContext{
|
||||||
Config: c,
|
Config: c,
|
||||||
|
Validate: vi.MustValidator(vi.WithAccount("account")),
|
||||||
|
Redis: *newRedis,
|
||||||
|
UserModel: model.NewUserTableModel(sqlConn, c.Cache),
|
||||||
|
AccountToUidModel: model.NewAccountToUidModel(sqlConn, c.Cache),
|
||||||
|
AccountModel: model.NewAccountModel(sqlConn, c.Cache),
|
||||||
|
GenUIDUseCase: usecase.MustGenerateUseCase(usecase.GenerateUseCaseParam{
|
||||||
|
GenerateUIDRepo: mongo,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
package usecase
|
|
@ -0,0 +1,75 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"app-cloudep-member-server/internal/domain"
|
||||||
|
"app-cloudep-member-server/internal/domain/usecase"
|
||||||
|
mgo "app-cloudep-member-server/internal/model/mongo"
|
||||||
|
"context"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GenerateUseCaseParam struct {
|
||||||
|
GenerateUIDRepo mgo.AutoIdModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenerateUseCase struct {
|
||||||
|
generateUIDRepo mgo.AutoIdModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustGenerateUseCase(param GenerateUseCaseParam) usecase.UIDGenerateUseCase {
|
||||||
|
return &GenerateUseCase{
|
||||||
|
generateUIDRepo: param.GenerateUIDRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate 利用 mongo 創立全局唯一的 ark id
|
||||||
|
// 如果之後有效能問題,扛在同一個 mongo 資料庫,在改成雪花算法
|
||||||
|
func (g GenerateUseCase) Generate(ctx context.Context) (string, error) {
|
||||||
|
var data mgo.AutoId
|
||||||
|
err := g.generateUIDRepo.Inc(ctx, &data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
uid := strconv.Itoa(int(domain.InitAutoId + data.Count))
|
||||||
|
code, err := generateReferralCode(uid)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateReferralCode 從 UID 生成 referralCode
|
||||||
|
func generateReferralCode(uid string) (string, error) {
|
||||||
|
uidInt, err := strconv.ParseInt(uid, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
maxReferralUIDBoundary := int64(math.Pow(float64(len(domain.ConvertTable)), float64(domain.DefaultReferralCodeLen)))
|
||||||
|
if uidInt > maxReferralUIDBoundary {
|
||||||
|
return "", usecase.UIDOutOfRangeErrorCodeError("uid encode out of range")
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded := encodeToBase(uidInt, len(domain.ConvertTable), domain.DefaultReferralCodeLen)
|
||||||
|
|
||||||
|
return encoded, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeToBase(num int64, base int, length int) string {
|
||||||
|
result := ""
|
||||||
|
for num > 0 {
|
||||||
|
index := num % int64(base)
|
||||||
|
result = domain.ConvertTable[index] + result
|
||||||
|
num /= int64(base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makes sure the length fo result feats the input length
|
||||||
|
if len(result) < length {
|
||||||
|
result = strings.Repeat(domain.ConvertTable[0], length-len(result)) + result
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
Loading…
Reference in New Issue