feat/member #1

Merged
daniel.w merged 5 commits from feat/member into main 2024-08-02 02:20:17 +00:00
22 changed files with 656 additions and 47 deletions
Showing only changes of commit 5c98a03391 - Show all commits

View File

@ -136,7 +136,7 @@ message VerifyRefreshCodeReq {
} }
message UpdateStatusReq { message UpdateStatusReq {
string account = 1; string uid = 1;
MemberStatus status = 2; MemberStatus status = 2;
} }

View File

@ -2,6 +2,7 @@ package config
import ( import (
"github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/zrpc" "github.com/zeromicro/go-zero/zrpc"
) )
@ -12,8 +13,8 @@ type Config struct {
DsnString string DsnString string
} }
Cache cache.CacheConf Cache cache.CacheConf
Bcrypt struct { Bcrypt struct {
Cost int Cost int
} }
RedisCluster redis.RedisConf
} }

View File

@ -1,5 +1,7 @@
package domain package domain
const ( const (
DefaultPageSize = 100
DefaultPageIndex = 0
Scope = 10 Scope = 10
) )

View File

@ -3,12 +3,13 @@ package error
import ( import (
"errors" "errors"
"fmt" "fmt"
"member/internal/lib/error/code"
"strings"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
_ "github.com/zeromicro/go-zero/core/logx" _ "github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"member/internal/lib/error/code"
"strings"
) )
func newErr(scope, detail uint32, msg string) *Err { func newErr(scope, detail uint32, msg string) *Err {

View File

@ -4,15 +4,16 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"member/internal/lib/error/code"
"reflect"
"strconv"
"testing"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"member/internal/lib/error/code"
"reflect"
"strconv"
"testing"
) )
func TestFromGRPCError_GivenStatusWithCodeAndMessage_ShouldReturnErr(t *testing.T) { func TestFromGRPCError_GivenStatusWithCodeAndMessage_ShouldReturnErr(t *testing.T) {

View File

@ -3,12 +3,13 @@ package error
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"member/internal/lib/error/code" "member/internal/lib/error/code"
"net/http" "net/http"
"testing" "testing"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
) )
func TestCode_GivenNilReceiver_CodeReturnOK_CodeStrReturns00000(t *testing.T) { func TestCode_GivenNilReceiver_CodeReturnOK_CodeStrReturns00000(t *testing.T) {

View File

@ -3,10 +3,11 @@ package middleware
import ( import (
"context" "context"
"errors" "errors"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc"
ers "member/internal/lib/error" ers "member/internal/lib/error"
"time" "time"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc"
) )
const defaultTimeout = 30 * time.Second const defaultTimeout = 30 * time.Second

View File

@ -2,6 +2,14 @@ package logic
import ( import (
"context" "context"
"errors"
"member/internal/domain"
ers "member/internal/lib/error"
"member/internal/lib/required"
"member/internal/model"
"time"
"github.com/go-sql-driver/mysql"
"member/gen_result/pb/member" "member/gen_result/pb/member"
"member/internal/svc" "member/internal/svc"
@ -23,9 +31,69 @@ func NewBindUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Bind
} }
} }
type createUserInfo struct {
Uid string `validate:"required"`
VerifyType int32 `validate:"required,oneof=0 1 2 3"`
AlarmType int32 `validate:"required,oneof=0 1 2"`
Status int32 `validate:"required,oneof=1 2 3 4 5 6"`
RoleId string `validate:"required"`
Language string `validate:"required"`
Currency string `validate:"required"`
NickName string
Gender int8
Birthday int64
}
// BindUserInfo 初次,綁定 User Info // BindUserInfo 初次,綁定 User Info
func (l *BindUserInfoLogic) BindUserInfo(in *member.CreateUserInfoReq) (*member.Response, error) { func (l *BindUserInfoLogic) BindUserInfo(in *member.CreateUserInfoReq) (*member.Response, error) {
// todo: add your logic here and delete this line // 驗證資料
err := required.ValidateAll(l.svcCtx.Validate, &createUserInfo{
Uid: in.GetUid(),
VerifyType: int32(in.GetVerifyType()),
AlarmType: int32(in.GetAlarmType()),
Status: int32(in.GetStatus()),
RoleId: in.GetRoleId(),
Language: in.GetLanguage(),
Currency: in.GetCurrency(),
NickName: in.GetNickName(),
Gender: int8(in.GetGender()),
Birthday: in.GetBirthday(),
})
if err != nil {
return nil, ers.InvalidFormat(err.Error())
}
return &member.Response{}, nil now := time.Now().UTC().Unix()
_, err = l.svcCtx.UserModel.Insert(l.ctx, &model.UserTable{
Uid: in.GetUid(),
VerifyType: int64(in.GetVerifyType()),
AlarmType: int64(in.GetAlarmType()),
Status: int64(in.GetStatus()),
RoleId: in.GetRoleId(),
Language: in.GetLanguage(),
Currency: in.GetCurrency(),
NickName: in.GetNickName(),
Gender: int64(in.GetGender()),
Birthday: in.GetBirthday(),
CreateTime: now,
UpdateTime: now,
})
if err != nil {
// 新增進去
var mysqlErr *mysql.MySQLError
if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
// 處理重複條目錯誤
return nil, ers.DBDuplicate(in.GetUid())
}
return nil, ers.DBError(err.Error())
}
return &member.Response{
Status: &member.BaseResp{
Code: domain.CodeOk.ToString(),
Message: "success",
Error: "",
},
}, nil
} }

View File

@ -4,8 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/go-sql-driver/mysql"
"github.com/zeromicro/go-zero/core/logx"
"member/gen_result/pb/member" "member/gen_result/pb/member"
ers "member/internal/lib/error" ers "member/internal/lib/error"
"member/internal/lib/required" "member/internal/lib/required"
@ -13,6 +11,9 @@ import (
"member/internal/svc" "member/internal/svc"
"member/internal/utils" "member/internal/utils"
"time" "time"
"github.com/go-sql-driver/mysql"
"github.com/zeromicro/go-zero/core/logx"
) )
type CreateUserAccountLogic struct { type CreateUserAccountLogic struct {

View File

@ -2,9 +2,15 @@ package logic
import ( import (
"context" "context"
"crypto/rand"
"fmt"
"math/big"
"member/gen_result/pb/member" "member/gen_result/pb/member"
"member/internal/domain"
ers "member/internal/lib/error"
"member/internal/lib/required"
"member/internal/svc" "member/internal/svc"
"strconv"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )
@ -23,9 +29,101 @@ func NewGenerateRefreshCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext
} }
} }
type generateRefreshCodeReq struct {
Account string `json:"account" validate:"account"`
// CodeType 1 email 2 phone
CodeType int32 `json:"code_type" validate:"required,oneof=1 2 3"`
}
var codeMap = map[int32]string{
1: "email",
2: "phone",
}
func getCodeNameByCode(code int32) (string, bool) {
res, ok := codeMap[code]
if !ok {
return "", false
}
return res, true
}
func generateVerifyCode(digits int) (string, error) {
if digits <= 0 {
// 預設為六位數
digits = 6
}
// 計算最大值 (10^digits - 1)
exp := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(digits)), nil)
// 生成隨機數
randomNumber, err := rand.Int(rand.Reader, exp)
if err != nil {
return "", err
}
// 將隨機數轉換為 string
verifyCode := strconv.Itoa(int(randomNumber.Int64()))
// 如果隨機數的位數少於指定的位數,則補 0
if len(verifyCode) < digits {
verifyCode = fmt.Sprintf("%0*d", digits, randomNumber)
}
return verifyCode, nil
}
// GenerateRefreshCode 這個帳號驗證碼(十分鐘),通用的 // GenerateRefreshCode 這個帳號驗證碼(十分鐘),通用的
func (l *GenerateRefreshCodeLogic) GenerateRefreshCode(in *member.GenerateRefreshCodeReq) (*member.GenerateRefreshCodeResp, error) { func (l *GenerateRefreshCodeLogic) GenerateRefreshCode(in *member.GenerateRefreshCodeReq) (*member.GenerateRefreshCodeResp, error) {
// todo: add your logic here and delete this line err := required.ValidateAll(l.svcCtx.Validate, &generateRefreshCodeReq{
Account: in.GetAccount(),
CodeType: in.GetCodeType(),
})
if err != nil {
return nil, ers.InvalidFormat(err.Error())
}
return &member.GenerateRefreshCodeResp{}, nil checkType, status := getCodeNameByCode(in.GetCodeType())
if !status {
return nil, ers.InvalidFormat(fmt.Errorf("failed to get correct code type").Error())
}
rk := fmt.Sprintf("verify:%s:%s", checkType, in.GetAccount())
// 拿過就不要再拿了
get, err := l.svcCtx.Redis.Get(rk)
if err != nil {
return nil, ers.DBError("failed to connect to redis", err.Error())
}
if get != "" {
return &member.GenerateRefreshCodeResp{
Status: &member.BaseResp{
Code: domain.CodeOk.ToString(),
Message: "success",
Error: "",
},
Data: &member.VerifyCode{
VerifyCode: get,
},
}, nil
}
code, err := generateVerifyCode(6)
if !status {
return nil, ers.ArkInternal(err.Error())
}
err = l.svcCtx.Redis.Setex(rk, code, 600)
if err != nil {
return nil, ers.ArkInternal(err.Error())
}
return &member.GenerateRefreshCodeResp{
Status: &member.BaseResp{
Code: domain.CodeOk.ToString(),
Message: "success",
Error: "",
},
Data: &member.VerifyCode{
VerifyCode: code,
},
}, nil
} }

View File

@ -2,6 +2,9 @@ package logic
import ( import (
"context" "context"
"member/internal/domain"
ers "member/internal/lib/error"
"member/internal/lib/required"
"member/gen_result/pb/member" "member/gen_result/pb/member"
"member/internal/svc" "member/internal/svc"
@ -23,9 +26,32 @@ func NewGetUidByAccountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *G
} }
} }
type getUidByAccountReq struct {
Account string `json:"account" validate:"account"`
}
// GetUidByAccount 用帳號換取 UID // GetUidByAccount 用帳號換取 UID
func (l *GetUidByAccountLogic) GetUidByAccount(in *member.GetUIDByAccountReq) (*member.GetUidByAccountResp, error) { func (l *GetUidByAccountLogic) GetUidByAccount(in *member.GetUIDByAccountReq) (*member.GetUidByAccountResp, error) {
// todo: add your logic here and delete this line err := required.ValidateAll(l.svcCtx.Validate, &getUidByAccountReq{
Account: in.GetAccount(),
})
if err != nil {
return nil, ers.InvalidFormat(err.Error())
}
return &member.GetUidByAccountResp{}, nil account, err := l.svcCtx.AccountToUidModel.FindOneByAccount(l.ctx, in.GetAccount())
if err != nil {
return nil, err
}
return &member.GetUidByAccountResp{
Status: &member.BaseResp{
Code: domain.CodeOk.ToString(),
Message: "success",
Error: "",
},
Data: &member.UID{
Uid: account.Uid,
},
}, nil
} }

View File

@ -2,8 +2,9 @@ package logic
import ( import (
"context" "context"
"member/gen_result/pb/member" "member/gen_result/pb/member"
ers "member/internal/lib/error"
"member/internal/model"
"member/internal/svc" "member/internal/svc"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
@ -23,9 +24,33 @@ func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUs
} }
} }
// UpdateStatus 取得會員資訊 type getUserInfoReq struct {
func (l *GetUserInfoLogic) GetUserInfo(in *member.GetUserInfoReq) (*member.GetUserInfoResp, error) { Uid string `json:"account" validate:"account"`
// todo: add your logic here and delete this line }
return &member.GetUserInfoResp{}, nil // GetUserInfo 取得會員資訊
func (l *GetUserInfoLogic) GetUserInfo(in *member.GetUserInfoReq) (*member.GetUserInfoResp, error) {
var entity *model.UserTable
var err error
if in.GetUid() != "" {
entity, err = l.svcCtx.UserModel.FindOneByUid(l.ctx, in.GetUid())
if err != nil {
return nil, ers.DBError(err.Error())
}
} else {
if in.GetNickName() != "" {
entity, err = l.svcCtx.UserModel.FindOneByNickName(l.ctx, in.GetNickName())
if err != nil {
return nil, ers.DBError(err.Error())
}
}
}
if entity != nil {
return &member.GetUserInfoResp{}, nil
}
return nil, ers.ResourceNotFound("filed to get user info")
} }

View File

@ -2,9 +2,13 @@ package logic
import ( import (
"context" "context"
"fmt"
"member/gen_result/pb/member" "member/gen_result/pb/member"
"member/internal/domain"
ers "member/internal/lib/error"
"member/internal/model"
"member/internal/svc" "member/internal/svc"
"strconv"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )
@ -23,9 +27,141 @@ func NewListMemberLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListMe
} }
} }
func parse(in *member.ListUserInfoReq) (model.UserQueryParams, error) {
var filter model.UserQueryParams
// 設置 PageSize
filter.PageSize = getValidPageSize(in.PageSize)
// 設置 PageIndex
filter.PageIndex = getValidPageIndex(in.PageIndex)
// 設置 RoleId
if in.RoleId != nil {
filter.RoleId = in.RoleId
}
// 設置 VerifyType
svt := fmt.Sprintf("%d", in.GetVerifyType().Number())
vt, err := getInt32FromStringPointer(&svt)
if err != nil {
return model.UserQueryParams{}, err
}
if vt != nil {
filter.VerifyType = vt
}
// 設置 AlarmType
sat := fmt.Sprintf("%d", in.GetAlarmType().Number())
at, err := getInt32FromStringPointer(&sat)
if err != nil {
return model.UserQueryParams{}, err
}
if at != nil {
filter.AlarmType = at
}
// 設置 Status
sst := fmt.Sprintf("%d", in.GetStatus().Number())
st, err := getInt32FromStringPointer(&sst)
if err != nil {
return model.UserQueryParams{}, err
}
if st != nil {
filter.Status = st
}
// 設置 CreateStartTime
if in.CreateStartTime != nil {
ct := in.GetCreateStartTime()
filter.CreateStartTime = &ct
}
// 設置 CreateEndTime
if in.CreateEndTime != nil {
et := in.GetCreateEndTime()
filter.CreateEndTime = &et
}
return filter, nil
}
func getValidPageSize(pageSize int64) int64 {
if pageSize <= 0 {
return domain.DefaultPageSize
}
return pageSize
}
func getValidPageIndex(pageIndex int64) int64 {
if pageIndex <= 0 {
return domain.DefaultPageIndex
}
return pageIndex
}
func getInt32FromStringPointer(strPtr *string) (*int32, error) {
if strPtr == nil {
return nil, nil
}
value, err := strconv.Atoi(*strPtr)
if err != nil {
return nil, err
}
int32Value := int32(value)
if int32Value == 0 {
return nil, nil
}
return &int32Value, nil
}
// ListMember 取得會員列表 // ListMember 取得會員列表
func (l *ListMemberLogic) ListMember(in *member.ListUserInfoReq) (*member.ListUserInfoResp, error) { func (l *ListMemberLogic) ListMember(in *member.ListUserInfoReq) (*member.ListUserInfoResp, error) {
// todo: add your logic here and delete this line params, err := parse(in)
if err != nil {
return nil, ers.InvalidFormat("failed to get correct param", err.Error())
}
return &member.ListUserInfoResp{}, nil members, err := l.svcCtx.UserModel.ListMembers(l.ctx, &params)
if err != nil {
return nil, ers.DBError(err.Error())
}
count, err := l.svcCtx.UserModel.Count(l.ctx)
if err != nil {
return nil, ers.DBError(err.Error())
}
var data = make([]*member.UserInfo, 0, len(members))
for _, item := range members {
g := uint32(item.Gender)
data = append(data, &member.UserInfo{
Uid: item.Uid,
VerifyType: member.VerifyType(item.VerifyType),
AlarmType: member.AlarmType(item.AlarmType),
Status: member.MemberStatus(item.Status),
RoleId: item.RoleId,
Language: item.Language,
Currency: item.Currency,
NickName: &item.NickName,
Gender: &g,
Birthday: &item.Birthday,
})
}
return &member.ListUserInfoResp{
Status: &member.BaseResp{
Code: domain.CodeOk.ToString(),
Message: "success",
Error: "",
},
Data: data,
Page: &member.Pager{
Total: count,
Size: in.PageSize,
Index: in.PageIndex,
},
}, nil
} }

View File

@ -2,6 +2,9 @@ package logic
import ( import (
"context" "context"
"member/internal/domain"
ers "member/internal/lib/error"
"member/internal/lib/required"
"member/gen_result/pb/member" "member/gen_result/pb/member"
"member/internal/svc" "member/internal/svc"
@ -23,9 +26,31 @@ func NewUpdateStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Upda
} }
} }
type updateStatusReq struct {
UID string `json:"uid" validate:"required"`
Status int32 `json:"status" validate:"required,oneof=1 2 3 4 5 6"`
}
// UpdateStatus 修改狀態 // UpdateStatus 修改狀態
func (l *UpdateStatusLogic) UpdateStatus(in *member.UpdateStatusReq) (*member.Response, error) { func (l *UpdateStatusLogic) UpdateStatus(in *member.UpdateStatusReq) (*member.Response, error) {
// todo: add your logic here and delete this line err := required.ValidateAll(l.svcCtx.Validate, &updateStatusReq{
UID: in.GetUid(),
Status: int32(in.GetStatus()),
})
if err != nil {
return nil, ers.InvalidFormat(err.Error())
}
return &member.Response{}, nil err = l.svcCtx.UserModel.UpdateStatus(l.ctx, in.GetUid(), int32(in.GetStatus()))
if err != nil {
return nil, ers.DBError(err.Error())
}
return &member.Response{
Status: &member.BaseResp{
Code: domain.CodeOk.ToString(),
Message: "success",
Error: "",
},
}, nil
} }

View File

@ -2,6 +2,11 @@ package logic
import ( import (
"context" "context"
"fmt"
"member/internal/domain"
ers "member/internal/lib/error"
"member/internal/lib/required"
"member/internal/utils"
"member/gen_result/pb/member" "member/gen_result/pb/member"
"member/internal/svc" "member/internal/svc"
@ -23,9 +28,36 @@ func NewUpdateUserTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *U
} }
} }
type updateUserTokenReq struct {
LoginId string `json:"login_id" validate:"account"`
Token string `json:"token" validate:"required"`
}
// UpdateUserToken 更新密碼 // UpdateUserToken 更新密碼
func (l *UpdateUserTokenLogic) UpdateUserToken(in *member.UpdateTokenReq) (*member.Response, error) { func (l *UpdateUserTokenLogic) UpdateUserToken(in *member.UpdateTokenReq) (*member.Response, error) {
// todo: add your logic here and delete this line err := required.ValidateAll(l.svcCtx.Validate, &updateUserTokenReq{
LoginId: in.GetAccount(),
Token: in.GetToken(),
})
if err != nil {
return nil, ers.InvalidFormat(err.Error())
}
return &member.Response{}, nil 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()))
}
err = l.svcCtx.AccountModel.UpdateTokenByLoginID(l.ctx, in.GetAccount(), token)
if err != nil {
return nil, ers.DBError(err.Error())
}
return &member.Response{
Status: &member.BaseResp{
Code: domain.CodeOk.ToString(),
Message: "success",
Error: "",
},
}, nil
} }

View File

@ -2,6 +2,10 @@ package logic
import ( import (
"context" "context"
"fmt"
"member/internal/domain"
ers "member/internal/lib/error"
"member/internal/lib/required"
"member/gen_result/pb/member" "member/gen_result/pb/member"
"member/internal/svc" "member/internal/svc"
@ -25,7 +29,42 @@ func NewVerifyRefreshCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext)
// VerifyRefreshCode 驗證忘記密碼 token // VerifyRefreshCode 驗證忘記密碼 token
func (l *VerifyRefreshCodeLogic) VerifyRefreshCode(in *member.VerifyRefreshCodeReq) (*member.Response, error) { func (l *VerifyRefreshCodeLogic) VerifyRefreshCode(in *member.VerifyRefreshCodeReq) (*member.Response, error) {
// todo: add your logic here and delete this line err := required.ValidateAll(l.svcCtx.Validate, &generateRefreshCodeReq{
Account: in.GetAccount(),
CodeType: in.GetCodeType(),
})
if err != nil {
return nil, ers.InvalidFormat(err.Error())
}
return &member.Response{}, nil checkType, status := getCodeNameByCode(in.GetCodeType())
if !status {
return nil, ers.InvalidFormat(fmt.Errorf("failed to get correct code type").Error())
}
rk := fmt.Sprintf("verify:%s:%s", checkType, in.GetAccount())
get, err := l.svcCtx.Redis.Get(rk)
if err != nil {
return nil, ers.DBError("failed to connect to redis", err.Error())
}
if get == "" {
return nil, ers.DBError("failed to get data from redis")
}
if get != fmt.Sprintf("%d", in.CodeType) {
return nil, ers.ArkInternal("Illegible Verify Code")
}
_, err = l.svcCtx.Redis.Del(rk)
if err != nil {
return nil, ers.DBError("failed to del redis key", rk)
}
return &member.Response{
Status: &member.BaseResp{
Code: domain.CodeOk.ToString(),
Message: "success",
Error: "",
},
}, nil
} }

View File

@ -1,6 +1,9 @@
package model package model
import ( import (
"context"
"database/sql"
"fmt"
"github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/core/stores/sqlx"
) )
@ -12,6 +15,7 @@ type (
// and implement the added methods in customAccountModel. // and implement the added methods in customAccountModel.
AccountModel interface { AccountModel interface {
accountModel accountModel
UpdateTokenByLoginID(ctx context.Context, account string, token string) error
} }
customAccountModel struct { customAccountModel struct {
@ -19,6 +23,21 @@ type (
} }
) )
func (m *defaultAccountModel) UpdateTokenByLoginID(ctx context.Context, account string, token string) error {
data, err := m.FindOneByAccount(ctx, account)
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 `token` = ? where `id` = ?", m.table, accountRowsWithPlaceHolder)
return conn.ExecCtx(ctx, query, token, data.Id)
}, accountAccountKey, accountIdKey)
return err
}
// NewAccountModel returns a model for the database table. // NewAccountModel returns a model for the database table.
func NewAccountModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) AccountModel { func NewAccountModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) AccountModel {
return &customAccountModel{ return &customAccountModel{

View File

@ -3,6 +3,7 @@ package model
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlc" "github.com/zeromicro/go-zero/core/stores/sqlc"
"github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/core/stores/sqlx"

View File

@ -1,8 +1,13 @@
package model package model
import ( import (
"context"
"database/sql"
"fmt"
"github.com/zeromicro/go-zero/core/stores/cache" "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/stores/sqlx"
"strings"
) )
var _ UserTableModel = (*customUserTableModel)(nil) var _ UserTableModel = (*customUserTableModel)(nil)
@ -12,11 +17,26 @@ type (
// and implement the added methods in customUserTableModel. // and implement the added methods in customUserTableModel.
UserTableModel interface { UserTableModel interface {
userTableModel userTableModel
FindOneByNickName(ctx context.Context, uid string) (*UserTable, error)
ListMembers(ctx context.Context, params *UserQueryParams) ([]*UserTable, error)
Count(ctx context.Context) (int64, error)
UpdateStatus(ctx context.Context, uid string, status int32) error
} }
customUserTableModel struct { customUserTableModel struct {
*defaultUserTableModel *defaultUserTableModel
} }
UserQueryParams struct {
RoleId *string
VerifyType *int32
AlarmType *int32
Status *int32
CreateStartTime *int64
CreateEndTime *int64
PageSize int64
PageIndex int64
}
) )
// NewUserTableModel returns a model for the database table. // NewUserTableModel returns a model for the database table.
@ -25,3 +45,104 @@ func NewUserTableModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Optio
defaultUserTableModel: newUserTableModel(conn, c, opts...), defaultUserTableModel: newUserTableModel(conn, c, opts...),
} }
} }
func (m *defaultUserTableModel) FindOneByNickName(ctx context.Context, nickName string) (*UserTable, error) {
userTableUidKey := fmt.Sprintf("%s%v", cacheUserTableUidPrefix, nickName)
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 `nick_name` = ? limit 1", userTableRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, nickName); 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) ListMembers(ctx context.Context, params *UserQueryParams) ([]*UserTable, error) {
query := fmt.Sprintf("select %s from %s", userTableRows, m.table)
var args = make([]any, 0, 10)
var conditions []string
if params.RoleId != nil {
conditions = append(conditions, "role_id = ?")
args = append(args, *params.RoleId)
}
if params.VerifyType != nil {
conditions = append(conditions, "verify_type = ?")
args = append(args, *params.VerifyType)
}
if params.AlarmType != nil {
conditions = append(conditions, "alarm_type = ?")
args = append(args, *params.AlarmType)
}
if params.Status != nil {
conditions = append(conditions, "status = ?")
args = append(args, *params.Status)
}
if params.CreateStartTime != nil {
conditions = append(conditions, "create_time >= ?")
args = append(args, *params.CreateStartTime)
}
if params.CreateEndTime != nil {
conditions = append(conditions, "create_time <= ?")
args = append(args, *params.CreateEndTime)
}
// 加入條件到查詢語句中
if len(conditions) > 0 {
query += " WHERE " + strings.Join(conditions, " AND ")
}
// 分頁處理
offset := (params.PageIndex - 1) * params.PageSize
query += " LIMIT ? OFFSET ?"
args = append(args, params.PageSize, offset)
var users []*UserTable
fmt.Println("query:", query)
fmt.Println("args:", args)
err := m.QueryRowsNoCacheCtx(ctx, &users, query, args...)
if err != nil {
return nil, err
}
return users, nil
}
func (m *defaultUserTableModel) Count(ctx context.Context) (int64, error) {
var count int64
query := fmt.Sprintf("select count(*) from %s", m.table)
err := m.QueryRowNoCacheCtx(ctx, &count, query)
if err != nil {
return 0, err
}
return count, nil
}
func (m *defaultUserTableModel) UpdateStatus(ctx context.Context, uid string, status int32) error {
data, err := m.FindOneByUid(ctx, uid)
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 `status` = ? where `id` = ?", m.table)
return conn.ExecCtx(ctx, query, status, data.Id)
}, userTableIdKey, userTableUidKey)
return err
}

View File

@ -18,8 +18,8 @@ import (
var ( var (
userTableFieldNames = builder.RawFieldNames(&UserTable{}) userTableFieldNames = builder.RawFieldNames(&UserTable{})
userTableRows = strings.Join(userTableFieldNames, ",") userTableRows = strings.Join(userTableFieldNames, ",")
userTableRowsExpectAutoSet = strings.Join(stringx.Remove(userTableFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",") userTableRowsExpectAutoSet = strings.Join(stringx.Remove(userTableFieldNames, "`id`"), ",")
userTableRowsWithPlaceHolder = 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`"), "=?,") + "=?"
cacheUserTableIdPrefix = "cache:userTable:id:" cacheUserTableIdPrefix = "cache:userTable:id:"
cacheUserTableUidPrefix = "cache:userTable:uid:" cacheUserTableUidPrefix = "cache:userTable:uid:"
@ -126,8 +126,8 @@ func (m *defaultUserTableModel) Insert(ctx context.Context, data *UserTable) (sq
userTableIdKey := fmt.Sprintf("%s%v", cacheUserTableIdPrefix, data.Id) userTableIdKey := fmt.Sprintf("%s%v", cacheUserTableIdPrefix, data.Id)
userTableUidKey := fmt.Sprintf("%s%v", cacheUserTableUidPrefix, data.Uid) 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) { 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) 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) 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, data.CreateTime, data.UpdateTime)
}, userTableIdKey, userTableUidKey) }, userTableIdKey, userTableUidKey)
return ret, err return ret, err
} }
@ -142,7 +142,7 @@ func (m *defaultUserTableModel) Update(ctx context.Context, newData *UserTable)
userTableUidKey := fmt.Sprintf("%s%v", cacheUserTableUidPrefix, data.Uid) userTableUidKey := fmt.Sprintf("%s%v", cacheUserTableUidPrefix, data.Uid)
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { _, 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) 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) 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.CreateTime, newData.UpdateTime, newData.Id)
}, userTableIdKey, userTableUidKey) }, userTableIdKey, userTableUidKey)
return err return err
} }

View File

@ -2,11 +2,12 @@ package svc
import ( import (
"context" "context"
"github.com/bwmarrin/snowflake"
sf "member/internal/lib/snackflow" sf "member/internal/lib/snackflow"
"member/internal/model" "member/internal/model"
"os" "os"
"time" "time"
"github.com/bwmarrin/snowflake"
) )
type machineNode struct { type machineNode struct {

View File

@ -1,20 +1,24 @@
package svc package svc
import ( import (
"github.com/bwmarrin/snowflake" "github.com/zeromicro/go-zero/core/stores/redis"
"member/internal/config" "member/internal/config"
"member/internal/domain" "member/internal/domain"
"member/internal/lib/required" "member/internal/lib/required"
"member/internal/model" "member/internal/model"
"github.com/bwmarrin/snowflake"
ers "member/internal/lib/error"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/core/stores/sqlx"
ers "member/internal/lib/error"
) )
type ServiceContext struct { type ServiceContext struct {
Config config.Config Config config.Config
Redis redis.Redis
Validate *validator.Validate Validate *validator.Validate
AccountModel model.AccountModel AccountModel model.AccountModel
UserModel model.UserTableModel UserModel model.UserTableModel
@ -32,8 +36,14 @@ func NewServiceContext(c config.Config) *ServiceContext {
panic(err) panic(err)
} }
newRedis, err := redis.NewRedis(c.RedisCluster, redis.Cluster())
if err != nil {
panic(err)
}
return &ServiceContext{ return &ServiceContext{
Config: c, Config: c,
Redis: *newRedis,
Validate: required.MustValidator(required.WithAccount("account")), Validate: required.MustValidator(required.WithAccount("account")),
UserModel: model.NewUserTableModel(sqlConn, c.Cache), UserModel: model.NewUserTableModel(sqlConn, c.Cache),
AccountToUidModel: model.NewAccountToUidModel(sqlConn, c.Cache), AccountToUidModel: model.NewAccountToUidModel(sqlConn, c.Cache),