feat:init project
This commit is contained in:
commit
0bbe2ace83
|
@ -0,0 +1,5 @@
|
|||
.idea/
|
||||
go.sum
|
||||
account/
|
||||
gen_result/
|
||||
etc/member.yaml
|
|
@ -0,0 +1,43 @@
|
|||
FROM golang:1.22 AS builder
|
||||
|
||||
LABEL stage=gobuilder
|
||||
|
||||
ENV CGO_ENABLED 0
|
||||
RUN apt-get update && \
|
||||
apt-get install git
|
||||
|
||||
WORKDIR /build
|
||||
#
|
||||
## Download public key for yt.com
|
||||
#RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan code.30cm.net >> ~/.ssh/known_hosts
|
||||
#
|
||||
## Forces the usage of git and ssh key fwded by ssh-agent for yt.com git repos
|
||||
#RUN git config --global url."git@git.30cm.net:".insteadOf "https://code.30cm.net"
|
||||
#
|
||||
## private go packages
|
||||
#ENV GOPRIVATE=code.30cm.net
|
||||
|
||||
ADD ../go.mod .
|
||||
ADD ../go.sum .
|
||||
RUN --mount=type=ssh go mod download
|
||||
|
||||
ENV FLAG="-s -w -X main.Version=${VERSION} -X main.Built=${BUILT} -X main.GitCommit=${GIT_COMMIT}"
|
||||
COPY .. .
|
||||
COPY ../etc /app/etc
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
||||
-ldflags "$FLAG" \
|
||||
-o /app/member member.go
|
||||
|
||||
|
||||
FROM gcr.io/distroless/static-debian12
|
||||
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY --from=builder /usr/share/zoneinfo/Asia/Taipei /usr/share/zoneinfo/Asia/Taipei
|
||||
ENV TZ Asia/Taipei
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/member /app/member
|
||||
COPY --from=builder /app/etc /app/etc
|
||||
|
||||
CMD ["./member", "-f", "etc/member.yaml"]
|
|
@ -0,0 +1,10 @@
|
|||
Name: member.rpc
|
||||
ListenOn: 0.0.0.0:8080
|
||||
Etcd:
|
||||
Hosts:
|
||||
- 127.0.0.1:2379
|
||||
Key: member.rpc
|
||||
DB:
|
||||
DsnString: root:yytt@tcp(127.0.0.1:3306)/ark_member?charset=utf8mb4&parseTime=true&loc=Asia%2FTaipei
|
||||
Cache:
|
||||
- Host: 127.0.0.1:7001
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE IF EXISTS `account`;
|
|
@ -0,0 +1,10 @@
|
|||
CREATE TABLE `account` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`account` VARCHAR(50) NOT NULL,
|
||||
`token` VARCHAR(255) NOT NULL,
|
||||
`platform` INT NOT NULL COMMENT '平台類型 1. ark 2. google',
|
||||
`create_time` BIGINT NOT NULL DEFAULT 0,
|
||||
`update_time` BIGINT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `uk_account` (`account` ASC)
|
||||
)ENGINE=InnoDB COMMENT='帳號';
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE IF EXISTS `account_to_uid`;
|
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE `account_to_uid` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`account` VARCHAR(50) NOT NULL,
|
||||
`uid` VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `uk_account` (`account` ASC),
|
||||
INDEX `idx_uid` (`uid` ASC)
|
||||
)ENGINE=InnoDB COMMENT='帳號轉換表';
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE IF EXISTS `user_table`;
|
|
@ -0,0 +1,18 @@
|
|||
CREATE TABLE `user_table` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`verify_type` tinyint DEFAULT 0 NOT NULL COMMENT '驗證類型 0. 異常 1.信箱 2.手機 3. GA 4.不驗證',
|
||||
`alarm_type` tinyint DEFAULT 0 NOT NULL COMMENT '告警狀態 0. 異常 1. 正常(未告警) 2.系統告警中',
|
||||
`status` tinyint DEFAULT 0 NOT NULL COMMENT '會員狀態 0. 異常 1. 尚未驗證 2. 啟用 3. 停權中 4. 信箱以驗證 5. 手機以驗證 6. GA 以驗證',
|
||||
`uid` VARCHAR(255) NOT NULL,
|
||||
`role_id` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`language` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`currency` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`nick_name` VARCHAR(255) DEFAULT '',
|
||||
`gender` tinyint DEFAULT 0 NOT NULL COMMENT '0. 不願透露, 1 男 2 女',
|
||||
`birthday` INT NOT NULL DEFAULT 0,
|
||||
`create_time` BIGINT NOT NULL DEFAULT 0,
|
||||
`update_time` BIGINT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_create_time` (`create_time` ASC),
|
||||
UNIQUE INDEX `uk_uid` (`uid` ASC)
|
||||
)ENGINE=InnoDB COMMENT='使用者資訊';
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE IF EXISTS `machine_node`;
|
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE `machine_node` (
|
||||
`id` bigint AUTO_INCREMENT NOT NULL COMMENT '流水號',
|
||||
`create_time` bigint DEFAULT 0 NOT NULL COMMENT '創建時間',
|
||||
`update_time` bigint DEFAULT 0 NOT NULL COMMENT '更新時間',
|
||||
`host_name` varchar(200) DEFAULT '' NOT NULL COMMENT 'host name',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='machineID Assigner for Generator';
|
|
@ -0,0 +1 @@
|
|||
DROP DATABASE IF EXISTS `ark_member`;
|
|
@ -0,0 +1 @@
|
|||
CREATE DATABASE IF NOT EXISTS `ark_member`;
|
|
@ -0,0 +1,216 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package member;
|
||||
option go_package="./member";
|
||||
|
||||
// ================ enum ================
|
||||
enum VerifyType {
|
||||
VERIFY_NONE = 0; // 初始(異常)
|
||||
VERIFY_EMAIL = 1;
|
||||
VERIFY_PHONE = 2;
|
||||
VERIFY_GOOGLE = 3;
|
||||
}
|
||||
|
||||
enum AlarmType {
|
||||
ALARM_NONE = 0; // 初始(異常)
|
||||
ALARM_NOT = 1; // 未告警
|
||||
ALARM_SYSTEM = 2; // 系統告警中
|
||||
}
|
||||
|
||||
enum MemberStatus {
|
||||
STATUS_NONE = 0; // 初始(異常)
|
||||
STATUS_VERIFY = 1; // 尚未驗證
|
||||
STATUS_COMPLETE = 2; // 帳號啟用中
|
||||
STATUS_DISABLE = 3; // 帳號停權中
|
||||
STATUS_EMAIL = 4; // 信箱以驗證
|
||||
STATUS_PHONE = 5; // 手機以驗證
|
||||
STATUS_GA = 6; // GA 已綁定
|
||||
}
|
||||
|
||||
enum Gender {
|
||||
GENDER_NONE = 0; // 初始(未提供)
|
||||
GENDER_MALE = 1; // 男性
|
||||
GENDER_FEMALE = 2; // 女性
|
||||
}
|
||||
// ================ enum ================
|
||||
|
||||
|
||||
// ================ common ================
|
||||
message Pager {
|
||||
int64 total =1;
|
||||
int64 size=2;
|
||||
int64 index=3;
|
||||
}
|
||||
|
||||
message Response {
|
||||
BaseResp status=1;
|
||||
}
|
||||
|
||||
message BaseResp {
|
||||
string code = 1;
|
||||
string message = 2;
|
||||
string error = 3;
|
||||
}
|
||||
|
||||
// ================ common ================
|
||||
|
||||
|
||||
// ================ account ================
|
||||
message CreateLoginUserReq {
|
||||
string login_id = 1;
|
||||
int64 platform = 2;
|
||||
string token = 3;
|
||||
}
|
||||
|
||||
message BindingUserReq {
|
||||
string uid = 1;
|
||||
string login_id = 2;
|
||||
}
|
||||
|
||||
message CreateUserInfoReq {
|
||||
string uid = 1;
|
||||
VerifyType verify_type = 2;
|
||||
AlarmType alarm_type = 3;
|
||||
MemberStatus status = 4;
|
||||
string role_id = 5;
|
||||
string language = 6;
|
||||
string currency = 7;
|
||||
optional string nick_name = 8;
|
||||
optional uint32 gender = 9;
|
||||
optional int64 birthday = 10;
|
||||
}
|
||||
|
||||
message GetAccountInfoResp {
|
||||
BaseResp status = 1;
|
||||
CreateLoginUserReq data = 2;
|
||||
}
|
||||
|
||||
// UpdateUserInfoReq 不處理邏輯給不給改,這裡只關新增修改刪除
|
||||
message UpdateUserInfoReq {
|
||||
string uid = 1;
|
||||
optional string language = 2;
|
||||
optional string currency = 3;
|
||||
optional string nick_name = 4;
|
||||
optional uint32 gender = 5;
|
||||
optional int64 birthday = 6;
|
||||
optional VerifyType verify_type = 7;
|
||||
optional AlarmType alarm_type = 8;
|
||||
}
|
||||
|
||||
message GetUIDByAccountReq {
|
||||
string account = 1;
|
||||
}
|
||||
|
||||
message UID {
|
||||
string uid = 1;
|
||||
}
|
||||
|
||||
message GetUidByAccountResp {
|
||||
BaseResp status = 1;
|
||||
UID data = 2;
|
||||
}
|
||||
|
||||
message UpdateTokenReq {
|
||||
string account = 1;
|
||||
string token = 2;
|
||||
}
|
||||
|
||||
message GenerateRefreshCodeReq {
|
||||
string account = 1;
|
||||
int32 code_type =2;
|
||||
}
|
||||
|
||||
message VerifyCode {
|
||||
string verify_code = 1;
|
||||
}
|
||||
|
||||
message GenerateRefreshCodeResp {
|
||||
BaseResp status = 1;
|
||||
VerifyCode data = 2;
|
||||
}
|
||||
|
||||
message VerifyRefreshCodeReq {
|
||||
string account = 1;
|
||||
int32 code_type =2;
|
||||
string verify_code = 3;
|
||||
}
|
||||
|
||||
message UpdateStatusReq {
|
||||
string account = 1;
|
||||
MemberStatus status = 2;
|
||||
}
|
||||
|
||||
message GetUserInfoReq {
|
||||
string uid = 1;
|
||||
optional string nick_name =2;
|
||||
}
|
||||
|
||||
message UserInfo {
|
||||
string uid = 1;
|
||||
VerifyType verify_type = 2;
|
||||
AlarmType alarm_type = 3;
|
||||
MemberStatus status = 4;
|
||||
string role_id = 5;
|
||||
string language = 6;
|
||||
string currency = 7;
|
||||
optional string nick_name = 8;
|
||||
optional uint32 gender = 9;
|
||||
optional int64 birthday = 10;
|
||||
}
|
||||
|
||||
message GetUserInfoResp {
|
||||
BaseResp status = 1;
|
||||
UserInfo data = 2;
|
||||
}
|
||||
|
||||
message ListUserInfoReq {
|
||||
optional string role_id = 1;
|
||||
optional VerifyType verify_type = 2;
|
||||
optional AlarmType alarm_type = 3;
|
||||
optional MemberStatus status = 4;
|
||||
optional int64 create_start_time = 5;
|
||||
optional int64 create_end_time = 6;
|
||||
int64 page_size =7;
|
||||
int64 page_index=8;
|
||||
}
|
||||
|
||||
message ListUserInfoResp {
|
||||
BaseResp status = 1;
|
||||
repeated UserInfo data = 2;
|
||||
Pager page =3;
|
||||
}
|
||||
|
||||
|
||||
service Account {
|
||||
// CreateUserAccount 建立帳號與密碼 -> 可登入,但可不可以做其他事情看業務流程,也可以只註冊就好
|
||||
rpc CreateUserAccount(CreateLoginUserReq) returns(Response);
|
||||
// GetUserAccountInfo 取得帳號密碼資料
|
||||
rpc GetUserAccountInfo(GetUIDByAccountReq) returns(GetAccountInfoResp);
|
||||
// UpdateUserToken 更新密碼
|
||||
rpc UpdateUserToken(UpdateTokenReq) returns(Response);
|
||||
|
||||
|
||||
// GetUidByAccount 用帳號換取 UID
|
||||
rpc GetUidByAccount(GetUIDByAccountReq) returns(GetUidByAccountResp);
|
||||
// BindAccount 綁定帳號 -> account bind to UID
|
||||
rpc BindAccount(BindingUserReq) returns(Response);
|
||||
|
||||
|
||||
// BindUserInfo 初次,綁定 User Info
|
||||
rpc BindUserInfo(CreateUserInfoReq) returns(Response);
|
||||
// UpdateUserInfo 更新 User Info
|
||||
rpc UpdateUserInfo(UpdateUserInfoReq) returns(Response);
|
||||
// UpdateStatus 修改狀態
|
||||
rpc UpdateStatus(UpdateStatusReq) returns(Response);
|
||||
// UpdateStatus 取得會員資訊
|
||||
rpc GetUserInfo(GetUserInfoReq) returns(GetUserInfoResp);
|
||||
// ListMember 取得會員列表
|
||||
rpc ListMember(ListUserInfoReq) returns(ListUserInfoResp);
|
||||
|
||||
// GenerateRefreshCode 這個帳號驗證碼(十分鐘),通用的
|
||||
rpc GenerateRefreshCode(GenerateRefreshCodeReq) returns(GenerateRefreshCodeResp);
|
||||
// VerifyRefreshCode 驗證忘記密碼 token
|
||||
rpc VerifyRefreshCode(VerifyRefreshCodeReq) returns(Response);
|
||||
|
||||
}
|
||||
// ================ account ================
|
|
@ -0,0 +1,101 @@
|
|||
module member
|
||||
|
||||
go 1.22.3
|
||||
|
||||
require (
|
||||
github.com/bwmarrin/snowflake v0.3.0
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/zeromicro/go-zero v1.6.6
|
||||
go.uber.org/goleak v1.2.1
|
||||
golang.org/x/crypto v0.24.0
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.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.3.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // 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/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // 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/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.18.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.5.3 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.14 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.14 // indirect
|
||||
go.opentelemetry.io/otel v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.19.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.20.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/term v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.29.3 // indirect
|
||||
k8s.io/apimachinery v0.29.4 // indirect
|
||||
k8s.io/client-go v0.29.3 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||
"github.com/zeromicro/go-zero/zrpc"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
zrpc.RpcServerConf
|
||||
// 加上DB結構體
|
||||
DB struct {
|
||||
DsnString string
|
||||
}
|
||||
Cache cache.CacheConf
|
||||
|
||||
Bcrypt struct {
|
||||
Cost int
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package domain
|
||||
|
||||
const (
|
||||
Scope = 10
|
||||
)
|
|
@ -0,0 +1,16 @@
|
|||
package domain
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Code int
|
||||
|
||||
func (c Code) ToString() string {
|
||||
return fmt.Sprintf("%d", c)
|
||||
}
|
||||
|
||||
const (
|
||||
CodeOk = Code(102000)
|
||||
CodeParamInvalid = Code(304000)
|
||||
CodeInternalError = Code(305000)
|
||||
CodeAccountExists = Code(306000)
|
||||
)
|
|
@ -0,0 +1,98 @@
|
|||
package code
|
||||
|
||||
const (
|
||||
OK uint32 = 0
|
||||
)
|
||||
|
||||
// Scope
|
||||
const (
|
||||
Unset uint32 = iota
|
||||
CloudEPPortalGW
|
||||
CloudEPMember
|
||||
)
|
||||
|
||||
// Category for general operations: 100 - 4900
|
||||
const (
|
||||
_ = iota
|
||||
CatInput uint32 = iota * 100
|
||||
CatDB
|
||||
CatResource
|
||||
CatGRPC
|
||||
CatAuth
|
||||
CatSystem
|
||||
CatPubSub
|
||||
)
|
||||
|
||||
// CatArk Category for specific app/service: 5000 - 9900
|
||||
const (
|
||||
CatArk uint32 = (iota + 50) * 100
|
||||
)
|
||||
|
||||
// Detail - Input 1xx
|
||||
const (
|
||||
_ = iota + CatInput
|
||||
InvalidFormat
|
||||
NotValidImplementation
|
||||
InvalidRange
|
||||
)
|
||||
|
||||
// Detail - Database 2xx
|
||||
const (
|
||||
_ = iota + CatDB
|
||||
DBError // general error
|
||||
DBDataConvert
|
||||
DBDuplicate
|
||||
)
|
||||
|
||||
// Detail - Resource 3xx
|
||||
const (
|
||||
_ = iota + CatResource
|
||||
ResourceNotFound
|
||||
InvalidResourceFormat
|
||||
ResourceAlreadyExist
|
||||
ResourceInsufficient
|
||||
InsufficientPermission
|
||||
InvalidMeasurementID
|
||||
ResourceExpired
|
||||
ResourceMigrated
|
||||
InvalidResourceState
|
||||
InsufficientQuota
|
||||
ResourceHasMultiOwner
|
||||
)
|
||||
|
||||
/* Detail - GRPC */
|
||||
// The GRPC detail code uses Go GRPC's built-in codes.
|
||||
// Refer to "google.golang.org/grpc/codes" for more detail.
|
||||
|
||||
// Detail - Auth 5xx
|
||||
const (
|
||||
_ = iota + CatAuth
|
||||
Unauthorized
|
||||
AuthExpired
|
||||
InvalidPosixTime
|
||||
SigAndPayloadNotMatched
|
||||
Forbidden
|
||||
)
|
||||
|
||||
// Detail - System 6xx
|
||||
const (
|
||||
_ = iota + CatSystem
|
||||
SystemInternalError
|
||||
SystemMaintainError
|
||||
SystemTimeoutError
|
||||
)
|
||||
|
||||
// Detail - PubSub 7xx
|
||||
const (
|
||||
_ = iota + CatPubSub
|
||||
Publish
|
||||
Consume
|
||||
MsgSizeTooLarge
|
||||
)
|
||||
|
||||
// Detail - Ark 5xxx
|
||||
const (
|
||||
_ = iota + CatArk
|
||||
ArkInternal
|
||||
ArkHttp400
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package code
|
||||
|
||||
// CatToStr collects general error messages for each Category
|
||||
// It is used to send back to API caller
|
||||
var CatToStr = map[uint32]string{
|
||||
CatInput: "Invalid Input Data",
|
||||
CatDB: "Database Error",
|
||||
CatResource: "Resource Error",
|
||||
CatGRPC: "Internal Service Communication Error",
|
||||
CatAuth: "Authentication Error",
|
||||
CatArk: "Internal Service Communication Error",
|
||||
CatSystem: "System Error",
|
||||
}
|
|
@ -0,0 +1,441 @@
|
|||
package error
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
_ "github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"member/internal/lib/error/code"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func newErr(scope, detail uint32, msg string) *Err {
|
||||
cat := detail / 100 * 100
|
||||
return &Err{
|
||||
category: cat,
|
||||
code: detail,
|
||||
scope: scope,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func newBuiltinGRPCErr(scope, detail uint32, msg string) *Err {
|
||||
return &Err{
|
||||
category: code.CatGRPC,
|
||||
code: detail,
|
||||
scope: scope,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// FromError tries to let error as Err
|
||||
// it supports to unwrap error that has Err
|
||||
// return nil if failed to transfer
|
||||
func FromError(err error) *Err {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var e *Err
|
||||
if errors.As(err, &e) {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FromCode parses code as following
|
||||
// Decimal: 120314
|
||||
// 12 represents Scope
|
||||
// 03 represents Category
|
||||
// 14 represents Detail error code
|
||||
func FromCode(code uint32) *Err {
|
||||
scope := code / 10000
|
||||
detail := code % 10000
|
||||
return &Err{
|
||||
category: detail / 100 * 100,
|
||||
code: detail,
|
||||
scope: scope,
|
||||
msg: "",
|
||||
}
|
||||
}
|
||||
|
||||
// FromGRPCError transfer error to Err
|
||||
// useful for gRPC client
|
||||
func FromGRPCError(err error) *Err {
|
||||
s, _ := status.FromError(err)
|
||||
e := FromCode(uint32(s.Code()))
|
||||
e.msg = s.Message()
|
||||
|
||||
// For GRPC built-in code
|
||||
if e.Scope() == code.Unset && e.Category() == 0 && e.Code() != code.OK {
|
||||
e = newBuiltinGRPCErr(Scope, e.Code(), s.Message())
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// Deprecated: check GRPCStatus() in Errs struct
|
||||
// ToGRPCError returns the status.Status
|
||||
// Useful to return error in gRPC server
|
||||
func ToGRPCError(e *Err) error {
|
||||
return status.New(codes.Code(e.FullCode()), e.Error()).Err()
|
||||
}
|
||||
|
||||
/*** System ***/
|
||||
|
||||
// SystemTimeoutError returns Err
|
||||
func SystemTimeoutError(s ...string) *Err {
|
||||
return newErr(Scope, code.SystemTimeoutError, fmt.Sprintf("system timeout: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// SystemTimeoutErrorL logs error message and returns Err
|
||||
func SystemTimeoutErrorL(l logx.Logger, s ...string) *Err {
|
||||
e := SystemTimeoutError(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// SystemInternalError returns Err struct
|
||||
func SystemInternalError(s ...string) *Err {
|
||||
return newErr(Scope, code.SystemInternalError, fmt.Sprintf("internal error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// SystemInternalErrorL logs error message and returns Err
|
||||
func SystemInternalErrorL(l logx.Logger, s ...string) *Err {
|
||||
e := SystemInternalError(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// SystemMaintainErrorL logs error message and returns Err
|
||||
func SystemMaintainErrorL(l logx.Logger, s ...string) *Err {
|
||||
e := SystemMaintainError(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// SystemMaintainError returns Err struct
|
||||
func SystemMaintainError(s ...string) *Err {
|
||||
return newErr(Scope, code.SystemMaintainError, fmt.Sprintf("service under maintenance: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
/*** CatInput ***/
|
||||
|
||||
// InvalidFormat returns Err struct
|
||||
func InvalidFormat(s ...string) *Err {
|
||||
return newErr(Scope, code.InvalidFormat, fmt.Sprintf("invalid format: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidFormatL logs error message and returns Err
|
||||
func InvalidFormatL(l logx.Logger, s ...string) *Err {
|
||||
e := InvalidFormat(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidRange returns Err struct
|
||||
func InvalidRange(s ...string) *Err {
|
||||
return newErr(Scope, code.InvalidRange, fmt.Sprintf("invalid range: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidRangeL logs error message and returns Err
|
||||
func InvalidRangeL(l logx.Logger, s ...string) *Err {
|
||||
e := InvalidRange(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// NotValidImplementation returns Err struct
|
||||
func NotValidImplementation(s ...string) *Err {
|
||||
return newErr(Scope, code.NotValidImplementation, fmt.Sprintf("not valid implementation: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// NotValidImplementationL logs error message and returns Err
|
||||
func NotValidImplementationL(l logx.Logger, s ...string) *Err {
|
||||
e := NotValidImplementation(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
/*** CatDB ***/
|
||||
|
||||
// DBError returns Err
|
||||
func DBError(s ...string) *Err {
|
||||
return newErr(Scope, code.DBError, fmt.Sprintf("db error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// DBErrorL logs error message and returns Err
|
||||
func DBErrorL(l logx.Logger, s ...string) *Err {
|
||||
e := DBError(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// DBDataConvert returns Err
|
||||
func DBDataConvert(s ...string) *Err {
|
||||
return newErr(Scope, code.DBDataConvert, fmt.Sprintf("data from db convert error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// DBDataConvertL logs error message and returns Err
|
||||
func DBDataConvertL(l logx.Logger, s ...string) *Err {
|
||||
e := DBDataConvert(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// DBDuplicate returns Err
|
||||
func DBDuplicate(s ...string) *Err {
|
||||
return newErr(Scope, code.DBDuplicate, fmt.Sprintf("data Duplicate key error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// DBDuplicateL logs error message and returns Err
|
||||
func DBDuplicateL(l logx.Logger, s ...string) *Err {
|
||||
e := DBDuplicate(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
/*** CatResource ***/
|
||||
|
||||
// ResourceNotFound returns Err and logging
|
||||
func ResourceNotFound(s ...string) *Err {
|
||||
return newErr(Scope, code.ResourceNotFound, fmt.Sprintf("resource not found: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ResourceNotFoundL logs error message and returns Err
|
||||
func ResourceNotFoundL(l logx.Logger, s ...string) *Err {
|
||||
e := ResourceNotFound(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidResourceFormat returns Err
|
||||
func InvalidResourceFormat(s ...string) *Err {
|
||||
return newErr(Scope, code.InvalidResourceFormat, fmt.Sprintf("invalid resource format: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidResourceFormatL logs error message and returns Err
|
||||
func InvalidResourceFormatL(l logx.Logger, s ...string) *Err {
|
||||
e := InvalidResourceFormat(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidResourceState returns status not correct.
|
||||
// for example: company should be destroy, agent should be no-sensor/fail-install ...
|
||||
func InvalidResourceState(s ...string) *Err {
|
||||
return newErr(Scope, code.InvalidResourceState, fmt.Sprintf("invalid resource state: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidResourceStateL logs error message and returns status not correct.
|
||||
func InvalidResourceStateL(l logx.Logger, s ...string) *Err {
|
||||
e := InvalidResourceState(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
func ResourceInsufficient(s ...string) *Err {
|
||||
return newErr(Scope, code.ResourceInsufficient,
|
||||
fmt.Sprintf("insufficient resource: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
func ResourceInsufficientL(l logx.Logger, s ...string) *Err {
|
||||
e := ResourceInsufficient(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// InsufficientPermission returns Err
|
||||
func InsufficientPermission(s ...string) *Err {
|
||||
return newErr(Scope, code.InsufficientPermission,
|
||||
fmt.Sprintf("insufficient permission: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InsufficientPermissionL returns Err and log
|
||||
func InsufficientPermissionL(l logx.Logger, s ...string) *Err {
|
||||
e := InsufficientPermission(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// ResourceAlreadyExist returns Err
|
||||
func ResourceAlreadyExist(s ...string) *Err {
|
||||
return newErr(Scope, code.ResourceAlreadyExist, fmt.Sprintf("resource already exist: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ResourceAlreadyExistL logs error message and returns Err
|
||||
func ResourceAlreadyExistL(l logx.Logger, s ...string) *Err {
|
||||
e := ResourceAlreadyExist(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidMeasurementID returns Err
|
||||
func InvalidMeasurementID(s ...string) *Err {
|
||||
return newErr(Scope, code.InvalidMeasurementID, fmt.Sprintf("missing measurement id: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidMeasurementIDL logs error message and returns Err
|
||||
func InvalidMeasurementIDL(l logx.Logger, s ...string) *Err {
|
||||
e := InvalidMeasurementID(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// ResourceExpired returns Err
|
||||
func ResourceExpired(s ...string) *Err {
|
||||
return newErr(Scope, code.ResourceExpired, fmt.Sprintf("resource expired: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ResourceExpiredL logs error message and returns Err
|
||||
func ResourceExpiredL(l logx.Logger, s ...string) *Err {
|
||||
e := ResourceExpired(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// ResourceMigrated returns Err
|
||||
func ResourceMigrated(s ...string) *Err {
|
||||
return newErr(Scope, code.ResourceMigrated, fmt.Sprintf("resource migrated: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ResourceMigratedL logs error message and returns Err
|
||||
func ResourceMigratedL(l logx.Logger, s ...string) *Err {
|
||||
e := ResourceMigrated(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// InsufficientQuota returns Err
|
||||
func InsufficientQuota(s ...string) *Err {
|
||||
return newErr(Scope, code.InsufficientQuota, fmt.Sprintf("insufficient quota: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InsufficientQuotaL logs error message and returns Err
|
||||
func InsufficientQuotaL(l logx.Logger, s ...string) *Err {
|
||||
e := InsufficientQuota(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
/*** CatAuth ***/
|
||||
|
||||
// Unauthorized returns Err
|
||||
func Unauthorized(s ...string) *Err {
|
||||
return newErr(Scope, code.Unauthorized, fmt.Sprintf("unauthorized: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// UnauthorizedL logs error message and returns Err
|
||||
func UnauthorizedL(l logx.Logger, s ...string) *Err {
|
||||
e := Unauthorized(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// AuthExpired returns Err
|
||||
func AuthExpired(s ...string) *Err {
|
||||
return newErr(Scope, code.AuthExpired, fmt.Sprintf("expired: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// AuthExpiredL logs error message and returns Err
|
||||
func AuthExpiredL(l logx.Logger, s ...string) *Err {
|
||||
e := AuthExpired(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidPosixTime returns Err
|
||||
func InvalidPosixTime(s ...string) *Err {
|
||||
return newErr(Scope, code.InvalidPosixTime, fmt.Sprintf("invalid posix time: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidPosixTimeL logs error message and returns Err
|
||||
func InvalidPosixTimeL(l logx.Logger, s ...string) *Err {
|
||||
e := InvalidPosixTime(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// SigAndPayloadNotMatched returns Err
|
||||
func SigAndPayloadNotMatched(s ...string) *Err {
|
||||
return newErr(Scope, code.SigAndPayloadNotMatched, fmt.Sprintf("signature and the payload are not match: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// SigAndPayloadNotMatchedL logs error message and returns Err
|
||||
func SigAndPayloadNotMatchedL(l logx.Logger, s ...string) *Err {
|
||||
e := SigAndPayloadNotMatched(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// Forbidden returns Err
|
||||
func Forbidden(s ...string) *Err {
|
||||
return newErr(Scope, code.Forbidden, fmt.Sprintf("forbidden: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ForbiddenL logs error message and returns Err
|
||||
func ForbiddenL(l logx.Logger, s ...string) *Err {
|
||||
e := Forbidden(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// IsAuthUnauthorizedError check the err is unauthorized error
|
||||
func IsAuthUnauthorizedError(err *Err) bool {
|
||||
switch err.Code() {
|
||||
case code.Unauthorized, code.AuthExpired, code.InvalidPosixTime,
|
||||
code.SigAndPayloadNotMatched, code.Forbidden,
|
||||
code.InvalidFormat, code.ResourceNotFound:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*** CatXBC ***/
|
||||
|
||||
// ArkInternal returns Err
|
||||
func ArkInternal(s ...string) *Err {
|
||||
return newErr(Scope, code.ArkInternal, fmt.Sprintf("ark internal error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ArkInternalL logs error message and returns Err
|
||||
func ArkInternalL(l logx.Logger, s ...string) *Err {
|
||||
e := ArkInternal(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
/*** CatPubSub ***/
|
||||
|
||||
// Publish returns Err
|
||||
func Publish(s ...string) *Err {
|
||||
return newErr(Scope, code.Publish, fmt.Sprintf("publish: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// PublishL logs error message and returns Err
|
||||
func PublishL(l logx.Logger, s ...string) *Err {
|
||||
e := Publish(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
||||
|
||||
// Consume returns Err
|
||||
func Consume(s ...string) *Err {
|
||||
return newErr(Scope, code.Consume, fmt.Sprintf("consume: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// MsgSizeTooLarge returns Err
|
||||
func MsgSizeTooLarge(s ...string) *Err {
|
||||
return newErr(Scope, code.MsgSizeTooLarge, fmt.Sprintf("kafka error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// MsgSizeTooLargeL logs error message and returns Err
|
||||
func MsgSizeTooLargeL(l logx.Logger, s ...string) *Err {
|
||||
e := MsgSizeTooLarge(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
return e
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,197 @@
|
|||
package error
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"member/internal/lib/error/code"
|
||||
"net/http"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// TODO Error要移到common 包
|
||||
|
||||
// Scope global variable should be set by service or module
|
||||
var Scope = code.Unset
|
||||
|
||||
type Err struct {
|
||||
category uint32
|
||||
code uint32
|
||||
scope uint32
|
||||
msg string
|
||||
internalErr error
|
||||
}
|
||||
|
||||
// Error is the interface of error
|
||||
// Getter function of private property "msg"
|
||||
func (e *Err) Error() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// chain the error string if the internal err exists
|
||||
var internalErrStr string
|
||||
if e.internalErr != nil {
|
||||
internalErrStr = e.internalErr.Error()
|
||||
}
|
||||
|
||||
if e.msg != "" {
|
||||
if internalErrStr != "" {
|
||||
return fmt.Sprintf("%s: %s", e.msg, internalErrStr)
|
||||
}
|
||||
return e.msg
|
||||
}
|
||||
|
||||
generalErrStr := e.GeneralError()
|
||||
if internalErrStr != "" {
|
||||
return fmt.Sprintf("%s: %s", generalErrStr, internalErrStr)
|
||||
}
|
||||
return generalErrStr
|
||||
}
|
||||
|
||||
// Category getter function of private property "category"
|
||||
func (e *Err) Category() uint32 {
|
||||
if e == nil {
|
||||
return 0
|
||||
}
|
||||
return e.category
|
||||
}
|
||||
|
||||
// Scope getter function of private property "scope"
|
||||
func (e *Err) Scope() uint32 {
|
||||
if e == nil {
|
||||
return code.Unset
|
||||
}
|
||||
|
||||
return e.scope
|
||||
}
|
||||
|
||||
// CodeStr returns the string of error code with zero padding
|
||||
func (e *Err) CodeStr() string {
|
||||
if e == nil {
|
||||
return "00000"
|
||||
}
|
||||
|
||||
if e.Category() == code.CatGRPC {
|
||||
return fmt.Sprintf("%d%04d", e.Scope(), e.Category()+e.Code())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d%04d", e.Scope(), e.Code())
|
||||
}
|
||||
|
||||
// Code getter function of private property "code"
|
||||
func (e *Err) Code() uint32 {
|
||||
if e == nil {
|
||||
return code.OK
|
||||
}
|
||||
|
||||
return e.code
|
||||
}
|
||||
|
||||
func (e *Err) FullCode() uint32 {
|
||||
if e == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if e.Category() == code.CatGRPC {
|
||||
return e.Scope()*10000 + e.Category() + e.Code()
|
||||
}
|
||||
|
||||
return e.Scope()*10000 + e.Code()
|
||||
}
|
||||
|
||||
// HTTPStatus returns corresponding HTTP status code
|
||||
func (e *Err) HTTPStatus() int {
|
||||
if e == nil || e.Code() == code.OK {
|
||||
return http.StatusOK
|
||||
}
|
||||
// determine status code by code
|
||||
switch e.Code() {
|
||||
case code.ResourceInsufficient:
|
||||
// 400
|
||||
return http.StatusBadRequest
|
||||
case code.Unauthorized, code.InsufficientPermission:
|
||||
// 401
|
||||
return http.StatusUnauthorized
|
||||
case code.InsufficientQuota:
|
||||
// 402
|
||||
return http.StatusPaymentRequired
|
||||
case code.InvalidPosixTime, code.Forbidden:
|
||||
// 403
|
||||
return http.StatusForbidden
|
||||
case code.ResourceNotFound:
|
||||
// 404
|
||||
return http.StatusNotFound
|
||||
case code.ResourceAlreadyExist, code.InvalidResourceState:
|
||||
// 409
|
||||
return http.StatusConflict
|
||||
case code.NotValidImplementation:
|
||||
// 501
|
||||
return http.StatusNotImplemented
|
||||
default:
|
||||
}
|
||||
|
||||
// determine status code by category
|
||||
switch e.Category() {
|
||||
case code.CatInput:
|
||||
return http.StatusBadRequest
|
||||
default:
|
||||
// return status code 500 if none of the condition is met
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
// GeneralError transform category level error message
|
||||
// It's the general error message for customer/API caller
|
||||
func (e *Err) GeneralError() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
errStr, ok := code.CatToStr[e.Category()]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return errStr
|
||||
}
|
||||
|
||||
// Is called when performing errors.Is().
|
||||
// DO NOT USE THIS FUNCTION DIRECTLY unless you are very certain about what you're doing.
|
||||
// Use errors.Is instead.
|
||||
// This function compares if two error variables are both *Err, and have the same code (without checking the wrapped internal error)
|
||||
func (e *Err) Is(f error) bool {
|
||||
var err *Err
|
||||
ok := errors.As(f, &err)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return e.Code() == err.Code()
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error
|
||||
// The result of unwrapping an error may itself have an Unwrap method;
|
||||
// we call the sequence of errors produced by repeated unwrapping the error chain.
|
||||
func (e *Err) Unwrap() error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.internalErr
|
||||
}
|
||||
|
||||
// Wrap sets the internal error to Err struct
|
||||
func (e *Err) Wrap(internalErr error) *Err {
|
||||
if e != nil {
|
||||
e.internalErr = internalErr
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Err) GRPCStatus() *status.Status {
|
||||
if e == nil {
|
||||
return status.New(codes.OK, "")
|
||||
}
|
||||
|
||||
return status.New(codes.Code(e.FullCode()), e.Error())
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
package error
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"member/internal/lib/error/code"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCode_GivenNilReceiver_CodeReturnOK_CodeStrReturns00000(t *testing.T) {
|
||||
// setup
|
||||
var e *Err = nil
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, code.OK, e.Code())
|
||||
assert.Equal(t, "00000", e.CodeStr())
|
||||
assert.Equal(t, "", e.Error())
|
||||
}
|
||||
|
||||
func TestCode_GivenScope99DetailCode6687_ShouldReturn996687(t *testing.T) {
|
||||
// setup
|
||||
e := Err{scope: 99, code: 6687}
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, uint32(6687), e.Code())
|
||||
assert.Equal(t, "996687", e.CodeStr())
|
||||
}
|
||||
|
||||
func TestCode_GivenScope0DetailCode87_ShouldReturn87(t *testing.T) {
|
||||
// setup
|
||||
e := Err{scope: 0, code: 87}
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, uint32(87), e.Code())
|
||||
assert.Equal(t, "00087", e.CodeStr())
|
||||
}
|
||||
|
||||
func TestFromCode_Given870005_ShouldHasScope87_Cat0_Detail5(t *testing.T) {
|
||||
// setup
|
||||
e := FromCode(870005)
|
||||
|
||||
// assert
|
||||
assert.Equal(t, uint32(87), e.Scope())
|
||||
assert.Equal(t, uint32(0), e.Category())
|
||||
assert.Equal(t, uint32(5), e.Code())
|
||||
assert.Equal(t, "", e.Error())
|
||||
}
|
||||
|
||||
func TestFromCode_Given0_ShouldHasScope0_Cat0_Detail0(t *testing.T) {
|
||||
// setup
|
||||
e := FromCode(0)
|
||||
|
||||
// assert
|
||||
assert.Equal(t, uint32(0), e.Scope())
|
||||
assert.Equal(t, uint32(0), e.Category())
|
||||
assert.Equal(t, uint32(0), e.Code())
|
||||
assert.Equal(t, "", e.Error())
|
||||
}
|
||||
|
||||
func TestFromCode_Given9105_ShouldHasScope0_Cat9100_Detail9105(t *testing.T) {
|
||||
// setup
|
||||
e := FromCode(9105)
|
||||
|
||||
// assert
|
||||
assert.Equal(t, uint32(0), e.Scope())
|
||||
assert.Equal(t, uint32(9100), e.Category())
|
||||
assert.Equal(t, uint32(9105), e.Code())
|
||||
assert.Equal(t, "", e.Error())
|
||||
}
|
||||
|
||||
func TestErr_ShouldImplementErrorFunction(t *testing.T) {
|
||||
// setup a func return error
|
||||
f := func() error { return InvalidFormat("fake field") }
|
||||
|
||||
// act
|
||||
err := f()
|
||||
|
||||
// assert
|
||||
assert.NotNil(t, err)
|
||||
assert.Contains(t, fmt.Sprint(err), "fake field") // can be printed
|
||||
}
|
||||
|
||||
func TestGeneralError_GivenNilErr_ShouldReturnEmptyString(t *testing.T) {
|
||||
// setup
|
||||
var e *Err = nil
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, "", e.GeneralError())
|
||||
}
|
||||
|
||||
func TestGeneralError_GivenNotExistCat_ShouldReturnEmptyString(t *testing.T) {
|
||||
// setup
|
||||
e := Err{category: 123456}
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, "", e.GeneralError())
|
||||
}
|
||||
|
||||
func TestGeneralError_GivenCatDB_ShouldReturnDBError(t *testing.T) {
|
||||
// setup
|
||||
e := Err{category: code.CatDB}
|
||||
catErrStr := code.CatToStr[code.CatDB]
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, catErrStr, e.GeneralError())
|
||||
}
|
||||
|
||||
func TestError_GivenEmptyMsg_ShouldReturnCatGeneralErrorMessage(t *testing.T) {
|
||||
// setup
|
||||
e := Err{category: code.CatDB, msg: ""}
|
||||
|
||||
// act
|
||||
errMsg := e.Error()
|
||||
|
||||
// assert
|
||||
assert.Equal(t, code.CatToStr[code.CatDB], errMsg)
|
||||
}
|
||||
|
||||
func TestError_GivenMsg_ShouldReturnGiveMsg(t *testing.T) {
|
||||
// setup
|
||||
e := Err{msg: "FAKE"}
|
||||
|
||||
// act
|
||||
errMsg := e.Error()
|
||||
|
||||
// assert
|
||||
assert.Equal(t, "FAKE", errMsg)
|
||||
}
|
||||
|
||||
func TestIs_GivenNilErr_ShouldReturnFalse(t *testing.T) {
|
||||
var nilErrs *Err
|
||||
// act
|
||||
result := errors.Is(nilErrs, DBError())
|
||||
result2 := errors.Is(DBError(), nilErrs)
|
||||
|
||||
// assert
|
||||
assert.False(t, result)
|
||||
assert.False(t, result2)
|
||||
}
|
||||
|
||||
func TestIs_GivenNil_ShouldReturnFalse(t *testing.T) {
|
||||
// act
|
||||
result := errors.Is(nil, DBError())
|
||||
result2 := errors.Is(DBError(), nil)
|
||||
|
||||
// assert
|
||||
assert.False(t, result)
|
||||
assert.False(t, result2)
|
||||
}
|
||||
|
||||
func TestIs_GivenNilReceiver_ShouldReturnCorrectResult(t *testing.T) {
|
||||
var nilErr *Err = nil
|
||||
|
||||
// test 1: nilErr != DBError
|
||||
var dbErr error = DBError("fake db error")
|
||||
assert.False(t, nilErr.Is(dbErr))
|
||||
|
||||
// test 2: nilErr != nil error
|
||||
var nilError error
|
||||
assert.False(t, nilErr.Is(nilError))
|
||||
|
||||
// test 3: nilErr == another nilErr
|
||||
var nilErr2 *Err = nil
|
||||
assert.True(t, nilErr.Is(nilErr2))
|
||||
}
|
||||
|
||||
func TestIs_GivenDBError_ShouldReturnTrue(t *testing.T) {
|
||||
// setup
|
||||
dbErr := DBError("fake db error")
|
||||
|
||||
// act
|
||||
result := errors.Is(dbErr, DBError("not care"))
|
||||
result2 := errors.Is(DBError(), dbErr)
|
||||
|
||||
// assert
|
||||
assert.True(t, result)
|
||||
assert.True(t, result2)
|
||||
}
|
||||
|
||||
func TestIs_GivenDBErrorAssignToErrorType_ShouldReturnTrue(t *testing.T) {
|
||||
// setup
|
||||
var dbErr error = DBError("fake db error")
|
||||
|
||||
// act
|
||||
result := errors.Is(dbErr, DBError("not care"))
|
||||
result2 := errors.Is(DBError(), dbErr)
|
||||
|
||||
// assert
|
||||
assert.True(t, result)
|
||||
assert.True(t, result2)
|
||||
}
|
||||
|
||||
func TestWrap_GivenNilErr_ShouldNoPanic(t *testing.T) {
|
||||
// act & assert
|
||||
assert.NotPanics(t, func() {
|
||||
var e *Err = nil
|
||||
_ = e.Wrap(fmt.Errorf("test"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestWrap_GivenErrorToWrap_ShouldReturnErrorWithWrappedError(t *testing.T) {
|
||||
// act & assert
|
||||
wrappedErr := fmt.Errorf("test")
|
||||
wrappingErr := SystemInternalError("WrappingError").Wrap(wrappedErr)
|
||||
unWrappedErr := wrappingErr.Unwrap()
|
||||
|
||||
assert.Equal(t, wrappedErr, unWrappedErr)
|
||||
}
|
||||
|
||||
func TestUnwrap_GivenNilErr_ShouldReturnNil(t *testing.T) {
|
||||
var e *Err = nil
|
||||
internalErr := e.Unwrap()
|
||||
assert.Nil(t, internalErr)
|
||||
}
|
||||
|
||||
func TestErrorsIs_GivenNilErr_ShouldReturnFalse(t *testing.T) {
|
||||
var e *Err = nil
|
||||
assert.False(t, errors.Is(e, fmt.Errorf("test")))
|
||||
}
|
||||
|
||||
func TestErrorsAs_GivenNilErr_ShouldReturnFalse(t *testing.T) {
|
||||
var internalErr *testErr
|
||||
var e *Err = nil
|
||||
assert.False(t, errors.As(e, &internalErr))
|
||||
}
|
||||
|
||||
func TestGRPCStatus(t *testing.T) {
|
||||
// setup table driven tests
|
||||
tests := []struct {
|
||||
name string
|
||||
given *Err
|
||||
expect *status.Status
|
||||
expectConvert error
|
||||
}{
|
||||
{
|
||||
"nil errs.Err",
|
||||
nil,
|
||||
status.New(codes.OK, ""),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"InvalidFormat Err",
|
||||
InvalidFormat("fake"),
|
||||
status.New(codes.Code(101), "invalid format: fake"),
|
||||
status.New(codes.Code(101), "invalid format: fake").Err(),
|
||||
},
|
||||
}
|
||||
|
||||
// act & assert
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
s := test.given.GRPCStatus()
|
||||
assert.Equal(t, test.expect.Code(), s.Code())
|
||||
assert.Equal(t, test.expect.Message(), s.Message())
|
||||
assert.Equal(t, test.expectConvert, status.Convert(test.given).Err())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestErr_HTTPStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err *Err
|
||||
want int
|
||||
}{
|
||||
{name: "nil error", err: nil, want: http.StatusOK},
|
||||
{name: "invalid measurement id", err: &Err{category: code.CatResource, code: code.InvalidMeasurementID}, want: http.StatusInternalServerError},
|
||||
{name: "resource already exists", err: &Err{category: code.CatResource, code: code.ResourceAlreadyExist}, want: http.StatusConflict},
|
||||
{name: "invalid resource state", err: &Err{category: code.CatResource, code: code.InvalidResourceState}, want: http.StatusConflict},
|
||||
{name: "invalid posix time", err: &Err{category: code.CatAuth, code: code.InvalidPosixTime}, want: http.StatusForbidden},
|
||||
{name: "unauthorized", err: &Err{category: code.CatAuth, code: code.Unauthorized}, want: http.StatusUnauthorized},
|
||||
{name: "db error", err: &Err{category: code.CatDB, code: code.DBError}, want: http.StatusInternalServerError},
|
||||
{name: "insufficient permission", err: &Err{category: code.CatResource, code: code.InsufficientPermission}, want: http.StatusUnauthorized},
|
||||
{name: "resource insufficient", err: &Err{category: code.CatResource, code: code.ResourceInsufficient}, want: http.StatusBadRequest},
|
||||
{name: "invalid format", err: &Err{category: code.CatInput, code: code.InvalidFormat}, want: http.StatusBadRequest},
|
||||
{name: "resource not found", err: &Err{code: code.ResourceNotFound}, want: http.StatusNotFound},
|
||||
{name: "ok", err: &Err{code: code.OK}, want: http.StatusOK},
|
||||
{name: "not valid implementation", err: &Err{category: code.CatInput, code: code.NotValidImplementation}, want: http.StatusNotImplemented},
|
||||
{name: "forbidden", err: &Err{category: code.CatAuth, code: code.Forbidden}, want: http.StatusForbidden},
|
||||
{name: "insufficient quota", err: &Err{category: code.CatResource, code: code.InsufficientQuota}, want: http.StatusPaymentRequired},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
// act
|
||||
got := tt.err.HTTPStatus()
|
||||
|
||||
// assert
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc"
|
||||
ers "member/internal/lib/error"
|
||||
"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,24 @@
|
|||
package required
|
||||
|
||||
import "github.com/go-playground/validator/v10"
|
||||
|
||||
// ValidateAll TODO 要移到common 包
|
||||
func ValidateAll(validate *validator.Validate, obj any) error {
|
||||
err := validate.Struct(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MustValidator(option ...Option) *validator.Validate {
|
||||
// TODO Validator 要抽出來
|
||||
v := validator.New()
|
||||
err := BindToValidator(v, option...)
|
||||
if err != nil {
|
||||
// log
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package required
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
ValidatorName string
|
||||
ValidatorFunc func(fl validator.FieldLevel) bool
|
||||
}
|
||||
|
||||
func BindToValidator(v *validator.Validate, opts ...Option) error {
|
||||
for _, item := range opts {
|
||||
err := v.RegisterValidation(item.ValidatorName, item.ValidatorFunc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to register validator : %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithAccount 創建一個新的 Option 結構,包含自定義的驗證函數,用於驗證 email 和台灣的手機號碼格式
|
||||
func WithAccount(tagName string) Option {
|
||||
return Option{
|
||||
ValidatorName: tagName,
|
||||
ValidatorFunc: func(fl validator.FieldLevel) bool {
|
||||
value := fl.Field().String()
|
||||
emailRegex := `^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
|
||||
phoneRegex := `^(\+886|0)?9\d{8}$`
|
||||
|
||||
emailMatch, _ := regexp.MatchString(emailRegex, value)
|
||||
phoneMatch, _ := regexp.MatchString(phoneRegex, value)
|
||||
|
||||
return emailMatch || phoneMatch
|
||||
},
|
||||
}
|
||||
}
|
|
@ -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,73 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"member/internal/domain"
|
||||
ers "member/internal/lib/error"
|
||||
"member/internal/lib/required"
|
||||
"member/internal/model"
|
||||
"strconv"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type BindAccountLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewBindAccountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BindAccountLogic {
|
||||
return &BindAccountLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
type bindLoginUserReq struct {
|
||||
Account string `json:"account" validate:"account"`
|
||||
UID string `json:"uid" `
|
||||
}
|
||||
|
||||
// BindAccount 綁定帳號 -> account bind to UID
|
||||
func (l *BindAccountLogic) BindAccount(in *member.BindingUserReq) (*member.Response, error) {
|
||||
// 驗證資料
|
||||
err := required.ValidateAll(l.svcCtx.Validate, &bindLoginUserReq{
|
||||
Account: in.GetLoginId(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, ers.InvalidFormat(err.Error())
|
||||
}
|
||||
|
||||
uid := in.GetUid()
|
||||
// 有UID 綁看看,沒帶UID 近來,確認沒重複就直接綁一個給他
|
||||
if in.GetUid() == "" {
|
||||
uid = strconv.FormatInt(int64(l.svcCtx.SnackFlowGen.Generate()), 10)
|
||||
}
|
||||
// 先確定有這個Account
|
||||
_, err = l.svcCtx.AccountModel.FindOneByAccount(l.ctx, in.GetLoginId())
|
||||
if err != nil {
|
||||
return nil, ers.ResourceNotFound(fmt.Sprintf("failed to get account : %s ", in.GetLoginId()))
|
||||
}
|
||||
|
||||
_, err = l.svcCtx.AccountToUidModel.Insert(l.ctx, &model.AccountToUid{
|
||||
Account: in.LoginId,
|
||||
Uid: uid,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, ers.DBError(err.Error())
|
||||
}
|
||||
|
||||
return &member.Response{
|
||||
Status: &member.BaseResp{
|
||||
Code: domain.CodeOk.ToString(),
|
||||
Message: "success",
|
||||
Error: "",
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type BindUserInfoLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewBindUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BindUserInfoLogic {
|
||||
return &BindUserInfoLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// BindUserInfo 初次,綁定 User Info
|
||||
func (l *BindUserInfoLogic) BindUserInfo(in *member.CreateUserInfoReq) (*member.Response, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &member.Response{}, nil
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"member/gen_result/pb/member"
|
||||
ers "member/internal/lib/error"
|
||||
"member/internal/lib/required"
|
||||
"member/internal/model"
|
||||
"member/internal/svc"
|
||||
"member/internal/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CreateUserAccountLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewCreateUserAccountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateUserAccountLogic {
|
||||
return &CreateUserAccountLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
type createLoginUserReq struct {
|
||||
LoginId string `json:"login_id" validate:"account"`
|
||||
Platform int64 `json:"platform" validate:"required,oneof=1 2 3"`
|
||||
Token string `json:"token" validate:"required"`
|
||||
}
|
||||
|
||||
// CreateUserAccount 建立帳號與密碼 -> 可登入,但可不可以做其他事情看業務流程,也可以只註冊就好
|
||||
func (l *CreateUserAccountLogic) CreateUserAccount(in *member.CreateLoginUserReq) (*member.Response, error) {
|
||||
// 驗證資料
|
||||
err := required.ValidateAll(l.svcCtx.Validate, &createLoginUserReq{
|
||||
LoginId: in.GetLoginId(),
|
||||
Platform: in.GetPlatform(),
|
||||
Token: in.GetToken(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, ers.InvalidFormat(err.Error())
|
||||
}
|
||||
|
||||
token, err := utils.HashPassword(in.GetToken(), l.svcCtx.Config.Bcrypt.Cost)
|
||||
if err != nil {
|
||||
return nil, ers.ArkInternal(fmt.Sprintf("failed to encrypt err: %v", err.Error()))
|
||||
}
|
||||
|
||||
// 新增進去
|
||||
now := time.Now().UTC().Unix()
|
||||
_, err = l.svcCtx.AccountModel.Insert(l.ctx, &model.Account{
|
||||
Account: in.LoginId,
|
||||
Token: token,
|
||||
Platform: in.Platform,
|
||||
CreateTime: now,
|
||||
UpdateTime: now,
|
||||
})
|
||||
if err != nil {
|
||||
// 新增進去
|
||||
var mysqlErr *mysql.MySQLError
|
||||
if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
|
||||
// 處理重複條目錯誤
|
||||
return nil, ers.DBDuplicate(in.LoginId)
|
||||
}
|
||||
|
||||
return nil, ers.DBError(err.Error())
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GenerateRefreshCodeLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGenerateRefreshCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GenerateRefreshCodeLogic {
|
||||
return &GenerateRefreshCodeLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateRefreshCode 這個帳號驗證碼(十分鐘),通用的
|
||||
func (l *GenerateRefreshCodeLogic) GenerateRefreshCode(in *member.GenerateRefreshCodeReq) (*member.GenerateRefreshCodeResp, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &member.GenerateRefreshCodeResp{}, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetUidByAccountLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetUidByAccountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUidByAccountLogic {
|
||||
return &GetUidByAccountLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// GetUidByAccount 用帳號換取 UID
|
||||
func (l *GetUidByAccountLogic) GetUidByAccount(in *member.GetUIDByAccountReq) (*member.GetUidByAccountResp, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &member.GetUidByAccountResp{}, nil
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/domain"
|
||||
ers "member/internal/lib/error"
|
||||
"member/internal/lib/required"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetUserAccountInfoLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetUserAccountInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserAccountInfoLogic {
|
||||
return &GetUserAccountInfoLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
type getUserAccountReq struct {
|
||||
LoginId string `json:"login_id" validate:"account"`
|
||||
}
|
||||
|
||||
// GetUserAccountInfo 取得帳號密碼資料
|
||||
func (l *GetUserAccountInfoLogic) GetUserAccountInfo(in *member.GetUIDByAccountReq) (*member.GetAccountInfoResp, error) {
|
||||
// 驗證輸入資料
|
||||
err := required.ValidateAll(l.svcCtx.Validate, &getUserAccountReq{
|
||||
LoginId: in.GetAccount(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, ers.InvalidFormat(err.Error())
|
||||
}
|
||||
|
||||
account, err := l.svcCtx.AccountModel.FindOneByAccount(l.ctx, in.GetAccount())
|
||||
if err != nil {
|
||||
|
||||
return nil, ers.DBError(err.Error())
|
||||
}
|
||||
|
||||
return &member.GetAccountInfoResp{
|
||||
Status: &member.BaseResp{
|
||||
Code: domain.CodeOk.ToString(),
|
||||
},
|
||||
Data: &member.CreateLoginUserReq{
|
||||
LoginId: account.Account,
|
||||
Token: account.Token,
|
||||
Platform: account.Platform,
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetUserInfoLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserInfoLogic {
|
||||
return &GetUserInfoLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateStatus 取得會員資訊
|
||||
func (l *GetUserInfoLogic) GetUserInfo(in *member.GetUserInfoReq) (*member.GetUserInfoResp, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &member.GetUserInfoResp{}, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ListMemberLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewListMemberLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListMemberLogic {
|
||||
return &ListMemberLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// ListMember 取得會員列表
|
||||
func (l *ListMemberLogic) ListMember(in *member.ListUserInfoReq) (*member.ListUserInfoResp, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &member.ListUserInfoResp{}, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type UpdateStatusLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewUpdateStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateStatusLogic {
|
||||
return &UpdateStatusLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateStatus 修改狀態
|
||||
func (l *UpdateStatusLogic) UpdateStatus(in *member.UpdateStatusReq) (*member.Response, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &member.Response{}, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type UpdateUserInfoLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewUpdateUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserInfoLogic {
|
||||
return &UpdateUserInfoLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUserInfo 更新 User Info
|
||||
func (l *UpdateUserInfoLogic) UpdateUserInfo(in *member.UpdateUserInfoReq) (*member.Response, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &member.Response{}, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type UpdateUserTokenLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewUpdateUserTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserTokenLogic {
|
||||
return &UpdateUserTokenLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUserToken 更新密碼
|
||||
func (l *UpdateUserTokenLogic) UpdateUserToken(in *member.UpdateTokenReq) (*member.Response, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &member.Response{}, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type VerifyRefreshCodeLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewVerifyRefreshCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *VerifyRefreshCodeLogic {
|
||||
return &VerifyRefreshCodeLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyRefreshCode 驗證忘記密碼 token
|
||||
func (l *VerifyRefreshCodeLogic) VerifyRefreshCode(in *member.VerifyRefreshCodeReq) (*member.Response, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &member.Response{}, nil
|
||||
}
|
|
@ -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,31 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
var _ AccountToUidModel = (*customAccountToUidModel)(nil)
|
||||
|
||||
var (
|
||||
cacheAccountPrefix = "cache:accountToUid:account:"
|
||||
)
|
||||
|
||||
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,151 @@
|
|||
// 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"`
|
||||
}
|
||||
)
|
||||
|
||||
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)
|
||||
}, 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.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,48 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||
"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
|
||||
FindOneByHostName(ctx context.Context, hostName string) (*MachineNode, error)
|
||||
}
|
||||
|
||||
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...),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultMachineNodeModel) FindOneByHostName(ctx context.Context, hostName string) (*MachineNode, error) {
|
||||
machineNodeIdKey := fmt.Sprintf("%s%v", cacheMachineNodeIdPrefix, hostName)
|
||||
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 `host_name` = ? limit 1", machineNodeRows, m.table)
|
||||
return conn.QueryRowCtx(ctx, v, query, hostName)
|
||||
})
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
|
@ -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,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,161 @@
|
|||
// 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`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
|
||||
userTableRowsWithPlaceHolder = strings.Join(stringx.Remove(userTableFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), "=?,") + "=?"
|
||||
|
||||
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"`
|
||||
RoleId string `db:"role_id"`
|
||||
Language string `db:"language"`
|
||||
Currency string `db:"currency"`
|
||||
NickName string `db:"nick_name"`
|
||||
Gender int64 `db:"gender"` // 0. 不願透露, 1 男 2 女
|
||||
Birthday int64 `db:"birthday"`
|
||||
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.RoleId, data.Language, data.Currency, data.NickName, data.Gender, data.Birthday)
|
||||
}, 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.RoleId, newData.Language, newData.Currency, newData.NickName, newData.Gender, newData.Birthday, 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
|
|
@ -0,0 +1,95 @@
|
|||
// Code generated by goctl. DO NOT EDIT.
|
||||
// Source: member.proto
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/logic"
|
||||
"member/internal/svc"
|
||||
)
|
||||
|
||||
type AccountServer struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
member.UnimplementedAccountServer
|
||||
}
|
||||
|
||||
func NewAccountServer(svcCtx *svc.ServiceContext) *AccountServer {
|
||||
return &AccountServer{
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateUserAccount 建立帳號與密碼 -> 可登入,但可不可以做其他事情看業務流程,也可以只註冊就好
|
||||
func (s *AccountServer) CreateUserAccount(ctx context.Context, in *member.CreateLoginUserReq) (*member.Response, error) {
|
||||
l := logic.NewCreateUserAccountLogic(ctx, s.svcCtx)
|
||||
return l.CreateUserAccount(in)
|
||||
}
|
||||
|
||||
// GetUserAccountInfo 取得帳號密碼資料
|
||||
func (s *AccountServer) GetUserAccountInfo(ctx context.Context, in *member.GetUIDByAccountReq) (*member.GetAccountInfoResp, error) {
|
||||
l := logic.NewGetUserAccountInfoLogic(ctx, s.svcCtx)
|
||||
return l.GetUserAccountInfo(in)
|
||||
}
|
||||
|
||||
// UpdateUserToken 更新密碼
|
||||
func (s *AccountServer) UpdateUserToken(ctx context.Context, in *member.UpdateTokenReq) (*member.Response, error) {
|
||||
l := logic.NewUpdateUserTokenLogic(ctx, s.svcCtx)
|
||||
return l.UpdateUserToken(in)
|
||||
}
|
||||
|
||||
// GetUidByAccount 用帳號換取 UID
|
||||
func (s *AccountServer) GetUidByAccount(ctx context.Context, in *member.GetUIDByAccountReq) (*member.GetUidByAccountResp, error) {
|
||||
l := logic.NewGetUidByAccountLogic(ctx, s.svcCtx)
|
||||
return l.GetUidByAccount(in)
|
||||
}
|
||||
|
||||
// BindAccount 綁定帳號 -> account bind to UID
|
||||
func (s *AccountServer) BindAccount(ctx context.Context, in *member.BindingUserReq) (*member.Response, error) {
|
||||
l := logic.NewBindAccountLogic(ctx, s.svcCtx)
|
||||
return l.BindAccount(in)
|
||||
}
|
||||
|
||||
// BindUserInfo 初次,綁定 User Info
|
||||
func (s *AccountServer) BindUserInfo(ctx context.Context, in *member.CreateUserInfoReq) (*member.Response, error) {
|
||||
l := logic.NewBindUserInfoLogic(ctx, s.svcCtx)
|
||||
return l.BindUserInfo(in)
|
||||
}
|
||||
|
||||
// UpdateUserInfo 更新 User Info
|
||||
func (s *AccountServer) UpdateUserInfo(ctx context.Context, in *member.UpdateUserInfoReq) (*member.Response, error) {
|
||||
l := logic.NewUpdateUserInfoLogic(ctx, s.svcCtx)
|
||||
return l.UpdateUserInfo(in)
|
||||
}
|
||||
|
||||
// UpdateStatus 修改狀態
|
||||
func (s *AccountServer) UpdateStatus(ctx context.Context, in *member.UpdateStatusReq) (*member.Response, error) {
|
||||
l := logic.NewUpdateStatusLogic(ctx, s.svcCtx)
|
||||
return l.UpdateStatus(in)
|
||||
}
|
||||
|
||||
// UpdateStatus 取得會員資訊
|
||||
func (s *AccountServer) GetUserInfo(ctx context.Context, in *member.GetUserInfoReq) (*member.GetUserInfoResp, error) {
|
||||
l := logic.NewGetUserInfoLogic(ctx, s.svcCtx)
|
||||
return l.GetUserInfo(in)
|
||||
}
|
||||
|
||||
// ListMember 取得會員列表
|
||||
func (s *AccountServer) ListMember(ctx context.Context, in *member.ListUserInfoReq) (*member.ListUserInfoResp, error) {
|
||||
l := logic.NewListMemberLogic(ctx, s.svcCtx)
|
||||
return l.ListMember(in)
|
||||
}
|
||||
|
||||
// GenerateRefreshCode 這個帳號驗證碼(十分鐘),通用的
|
||||
func (s *AccountServer) GenerateRefreshCode(ctx context.Context, in *member.GenerateRefreshCodeReq) (*member.GenerateRefreshCodeResp, error) {
|
||||
l := logic.NewGenerateRefreshCodeLogic(ctx, s.svcCtx)
|
||||
return l.GenerateRefreshCode(in)
|
||||
}
|
||||
|
||||
// VerifyRefreshCode 驗證忘記密碼 token
|
||||
func (s *AccountServer) VerifyRefreshCode(ctx context.Context, in *member.VerifyRefreshCodeReq) (*member.Response, error) {
|
||||
l := logic.NewVerifyRefreshCodeLogic(ctx, s.svcCtx)
|
||||
return l.VerifyRefreshCode(in)
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/bwmarrin/snowflake"
|
||||
sf "member/internal/lib/snackflow"
|
||||
"member/internal/model"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type machineNode struct {
|
||||
MachineNodeID int64 `json:"machine_node_id"`
|
||||
}
|
||||
|
||||
type MachineNodeCreateParams struct {
|
||||
HostName string
|
||||
}
|
||||
|
||||
func NewMachineNode(node model.MachineNodeModel) int64 {
|
||||
ctx := context.Background()
|
||||
nodeName := os.Getenv("POD_NAME")
|
||||
if os.Getenv("POD_NAME") == "" {
|
||||
nodeName = "default_node"
|
||||
}
|
||||
|
||||
machine, err := node.FindOneByHostName(ctx, nodeName)
|
||||
if err != nil {
|
||||
result, err := node.Insert(ctx, &model.MachineNode{
|
||||
CreateTime: time.Now().Unix(),
|
||||
HostName: nodeName,
|
||||
})
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
return machine.Id
|
||||
}
|
||||
|
||||
func GetMachineNodeID(machineNodeID int64) int64 {
|
||||
// Snowflake 公式,工作機器 ID 佔用 10bit,最多容納 1024節點,
|
||||
// 故用 % 1024 取餘數做 ring
|
||||
const nodeMax = 1024
|
||||
|
||||
return machineNodeID % nodeMax
|
||||
}
|
||||
|
||||
func newSnackFlowNode(node model.MachineNodeModel) (*snowflake.Node, error) {
|
||||
|
||||
nodeID := NewMachineNode(node)
|
||||
ringNodeID := GetMachineNodeID(nodeID)
|
||||
s := sf.New(sf.WithMachineNodeID(ringNodeID))
|
||||
n, err := s.NewNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package svc
|
||||
|
||||
import (
|
||||
"github.com/bwmarrin/snowflake"
|
||||
"member/internal/config"
|
||||
"member/internal/domain"
|
||||
"member/internal/lib/required"
|
||||
"member/internal/model"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
ers "member/internal/lib/error"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
|
||||
Validate *validator.Validate
|
||||
AccountModel model.AccountModel
|
||||
UserModel model.UserTableModel
|
||||
AccountToUidModel model.AccountToUidModel
|
||||
SnackFlowGen *snowflake.Node
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
sqlConn := sqlx.NewMysql(c.DB.DsnString)
|
||||
// 設置
|
||||
ers.Scope = domain.Scope
|
||||
|
||||
n, err := newSnackFlowNode(model.NewMachineNodeModel(sqlConn, c.Cache))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
Validate: required.MustValidator(required.WithAccount("account")),
|
||||
UserModel: model.NewUserTableModel(sqlConn, c.Cache),
|
||||
AccountToUidModel: model.NewAccountToUidModel(sqlConn, c.Cache),
|
||||
AccountModel: model.NewAccountModel(sqlConn, c.Cache),
|
||||
SnackFlowGen: n,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func HashPassword(password string, cost int) (string, error) {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), cost)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
func CheckPasswordHash(password, hash string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func GetHashingCost(hashedPassword []byte) int {
|
||||
cost, _ := bcrypt.Cost(hashedPassword)
|
||||
return cost
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
.PHONY: help test-race lint sec-scan gci-format db-mysql-init docker-image-build db-mysql-down generate
|
||||
|
||||
help: ## show how tot use this tools
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
|
||||
PROJECT_NAME?=member
|
||||
|
||||
########
|
||||
# test #
|
||||
########
|
||||
|
||||
test-race: ## launch all tests with race detection
|
||||
go test ./... -cover -race
|
||||
|
||||
########
|
||||
# lint #
|
||||
########
|
||||
|
||||
lint: ## lints the entire codebase
|
||||
@golangci-lint run ./... --config=./.golangci.yaml
|
||||
|
||||
#######
|
||||
# sec #
|
||||
#######
|
||||
|
||||
sec-scan: trivy-scan vuln-scan ## scan for security and vulnerability issues
|
||||
|
||||
trivy-scan: ## scan for sec issues with trivy (trivy binary needed)
|
||||
trivy fs --exit-code 1 --no-progress --severity CRITICAL ./
|
||||
|
||||
vuln-scan: ## scan for vulnerability issues with govulncheck (govulncheck binary needed)
|
||||
govulncheck ./...
|
||||
|
||||
######
|
||||
# db #
|
||||
######
|
||||
MYSQL_SQL_PATH="./database/migrations/mysql"
|
||||
|
||||
db-mysql-init:
|
||||
@( \
|
||||
printf "Enter migrate name: "; read -r MIGRATE_NAME && \
|
||||
migrate create -ext sql -dir ${MYSQL_SQL_PATH} $${MIGRATE_NAME} \
|
||||
)
|
||||
|
||||
db-mysql-up:
|
||||
@( \
|
||||
printf "Enter pass for db: \n"; read -rs DB_PASSWORD && \
|
||||
printf "Enter port(3306...): \n"; read -r DB_PORT &&\
|
||||
migrate --database "mysql://root:$${DB_PASSWORD}@tcp(localhost:$${DB_PORT})/$(PROJECT_NAME)?charset=utf8&parseTime=True&loc=Local" --path ${MYSQL_SQL_PATH} up \
|
||||
)
|
||||
|
||||
db-mysql-down:
|
||||
@( \
|
||||
printf "Enter pass for db: \n"; read -s DB_PASSWORD && \
|
||||
printf "Enter port(3306...): \n"; read -r DB_PORT &&\
|
||||
migrate --database "mysql://root:$${DB_PASSWORD}@tcp(localhost:$${DB_PORT})/$(PROJECT_NAME)?charset=utf8&parseTime=True&loc=Local" --path ${MYSQL_SQL_PATH} down \
|
||||
)
|
||||
|
||||
SQL_FILE_TIMESTAMP=$(shell date '+%Y%m%d%H%M%S')
|
||||
|
||||
gen-migrate-sql:
|
||||
@( \
|
||||
printf "Enter file name: "; read -r FILE_NAME; \
|
||||
touch database/migrations/mysql/$(SQL_FILE_TIMESTAMP)_$$FILE_NAME.up.sql; \
|
||||
touch database/migrations/mysql/$(SQL_FILE_TIMESTAMP)_$$FILE_NAME.down.sql; \
|
||||
)
|
||||
|
||||
###########
|
||||
# GCI #
|
||||
###########
|
||||
gci-format:
|
||||
gci write --skip-generated -s standard -s default -s "prefix(yt.com/backend)" -s "prefix($(PROJECT_NAME))" ./
|
||||
|
||||
#########
|
||||
# build #
|
||||
#########
|
||||
|
||||
GitCommit=$(shell git rev-parse HEAD)
|
||||
Date=$(shell date -Iseconds)
|
||||
|
||||
build:
|
||||
@( \
|
||||
printf "Enter file name: "; read -r VERSION; \
|
||||
go build -ldflags "-s -w -X 'main.Version=$$VERSION' -X 'main.Built=$(Date)' -X 'main.GitCommit=$(GitCommit)'" -o ./bin/$(PROJECT_NAME) ./cmd/$(PROJECT_NAME) \
|
||||
)
|
||||
|
||||
docker-image-build:
|
||||
docker build \
|
||||
-f ./build/Dockerfile \
|
||||
-t $(PROJECT_NAME) \
|
||||
--platform linux/amd64 \
|
||||
--build-arg BUILT=$(Date) \
|
||||
--build-arg GIT_COMMIT=$(GitCommit) \
|
||||
--ssh default=$$HOME/.ssh/id_rsa \
|
||||
./
|
||||
|
||||
generate:
|
||||
@( \
|
||||
printf "Generate protobuf .... "; \
|
||||
# goctl rpc protoc ./generate/protobuf/member.proto --style=go_zero --go_out=./gen_result/pb --go-grpc_out=./gen_result/pb --zrpc_out=. \
|
||||
# printf "Generate docker .... "; \
|
||||
# goctl docker --go member.go --exe member --version 1.22 --tz Asia/Taipei --remote code.30cm.net --base gcr.io/distroless/static-debian12
|
||||
# goctl model mysql ddl -c yes -s ./generate/database/mysql/20230529020011_account_uid_table.up.sql --style go_zero -d ./internal/model
|
||||
|
||||
)
|
|
@ -0,0 +1,43 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"member/internal/lib/middleware"
|
||||
|
||||
"member/gen_result/pb/member"
|
||||
"member/internal/config"
|
||||
"member/internal/server"
|
||||
"member/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/conf"
|
||||
"github.com/zeromicro/go-zero/core/service"
|
||||
"github.com/zeromicro/go-zero/zrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
var configFile = flag.String("f", "etc/member.yaml", "the config file")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
var c config.Config
|
||||
conf.MustLoad(*configFile, &c)
|
||||
ctx := svc.NewServiceContext(c)
|
||||
|
||||
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
|
||||
member.RegisterAccountServer(grpcServer, server.NewAccountServer(ctx))
|
||||
|
||||
if c.Mode == service.DevMode || c.Mode == service.TestMode {
|
||||
reflection.Register(grpcServer)
|
||||
}
|
||||
})
|
||||
defer s.Stop()
|
||||
|
||||
// 加入勿中間件
|
||||
s.AddUnaryInterceptors(middleware.TimeoutMiddleware)
|
||||
|
||||
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
|
||||
s.Start()
|
||||
}
|
Loading…
Reference in New Issue