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