diff --git a/app/email/api/internal/handler/email/forgotPasswordHandler.go b/app/email/api/internal/handler/email/forgotPasswordHandler.go
new file mode 100644
index 0000000..ae22950
--- /dev/null
+++ b/app/email/api/internal/handler/email/forgotPasswordHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package email
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/email/api/internal/logic/email"
+ "juwan-backend/app/email/api/internal/svc"
+ "juwan-backend/app/email/api/internal/types"
+)
+
+// 忘记密码-发送验证码
+func ForgotPasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.ForgotPasswordReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := email.NewForgotPasswordLogic(r.Context(), svcCtx)
+ resp, err := l.ForgotPassword(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/email/api/internal/handler/routes.go b/app/email/api/internal/handler/routes.go
index cc896c3..36a5f5a 100644
--- a/app/email/api/internal/handler/routes.go
+++ b/app/email/api/internal/handler/routes.go
@@ -25,6 +25,18 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
},
}...,
),
- rest.WithPrefix("/api/email"),
+ rest.WithPrefix("/api/v1/email"),
+ )
+
+ server.AddRoutes(
+ []rest.Route{
+ {
+ // 忘记密码-发送验证码
+ Method: http.MethodPost,
+ Path: "/forgot-password/send",
+ Handler: email.ForgotPasswordHandler(serverCtx),
+ },
+ },
+ rest.WithPrefix("/api/v1/auth"),
)
}
diff --git a/app/email/api/internal/handler/user/forgotPasswordHandler.go b/app/email/api/internal/handler/user/forgotPasswordHandler.go
new file mode 100644
index 0000000..9f7cd16
--- /dev/null
+++ b/app/email/api/internal/handler/user/forgotPasswordHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/email/api/internal/logic/user"
+ "juwan-backend/app/email/api/internal/svc"
+ "juwan-backend/app/email/api/internal/types"
+)
+
+// 忘记密码-发送验证码
+func ForgotPasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.ForgotPasswordReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := user.NewForgotPasswordLogic(r.Context(), svcCtx)
+ resp, err := l.ForgotPassword(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/email/api/internal/logic/email/forgotPasswordLogic.go b/app/email/api/internal/logic/email/forgotPasswordLogic.go
new file mode 100644
index 0000000..2bd1c8b
--- /dev/null
+++ b/app/email/api/internal/logic/email/forgotPasswordLogic.go
@@ -0,0 +1,34 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package email
+
+import (
+ "context"
+
+ "juwan-backend/app/email/api/internal/svc"
+ "juwan-backend/app/email/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type ForgotPasswordLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 忘记密码-发送验证码
+func NewForgotPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ForgotPasswordLogic {
+ return &ForgotPasswordLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *ForgotPasswordLogic) ForgotPassword(req *types.ForgotPasswordReq) (resp *types.EmptyResp, err error) {
+ // todo: add your logic here and delete this line
+
+ return
+}
diff --git a/app/email/api/internal/logic/user/forgotPasswordLogic.go b/app/email/api/internal/logic/user/forgotPasswordLogic.go
new file mode 100644
index 0000000..8edaee0
--- /dev/null
+++ b/app/email/api/internal/logic/user/forgotPasswordLogic.go
@@ -0,0 +1,34 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "context"
+
+ "juwan-backend/app/email/api/internal/svc"
+ "juwan-backend/app/email/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type ForgotPasswordLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 忘记密码-发送验证码
+func NewForgotPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ForgotPasswordLogic {
+ return &ForgotPasswordLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *ForgotPasswordLogic) ForgotPassword(req *types.ForgotPasswordReq) (resp *types.EmptyResp, err error) {
+ // todo: add your logic here and delete this line
+
+ return
+}
diff --git a/app/email/api/internal/types/types.go b/app/email/api/internal/types/types.go
index 57b59f2..81929ab 100644
--- a/app/email/api/internal/types/types.go
+++ b/app/email/api/internal/types/types.go
@@ -3,6 +3,13 @@
package types
+type EmptyResp struct {
+}
+
+type ForgotPasswordReq struct {
+ Email string `json:"email"`
+}
+
type SendVerificationCodeReq struct {
Email string `json:"email" binding:"required,email"`
Scene string `json:"scene" binding:"required,oneof=register login reset_password bind_email"`
diff --git a/app/objectstory/api/etc/file-api.yaml b/app/objectstory/api/etc/file-api.yaml
new file mode 100644
index 0000000..e65e032
--- /dev/null
+++ b/app/objectstory/api/etc/file-api.yaml
@@ -0,0 +1,3 @@
+Name: file-api
+Host: 0.0.0.0
+Port: 8888
diff --git a/app/objectstory/api/file.go b/app/objectstory/api/file.go
new file mode 100644
index 0000000..87302be
--- /dev/null
+++ b/app/objectstory/api/file.go
@@ -0,0 +1,34 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "juwan-backend/app/objectstory/api/internal/config"
+ "juwan-backend/app/objectstory/api/internal/handler"
+ "juwan-backend/app/objectstory/api/internal/svc"
+
+ "github.com/zeromicro/go-zero/core/conf"
+ "github.com/zeromicro/go-zero/rest"
+)
+
+var configFile = flag.String("f", "etc/file-api.yaml", "the config file")
+
+func main() {
+ flag.Parse()
+
+ var c config.Config
+ conf.MustLoad(*configFile, &c)
+
+ server := rest.MustNewServer(c.RestConf)
+ defer server.Stop()
+
+ ctx := svc.NewServiceContext(c)
+ handler.RegisterHandlers(server, ctx)
+
+ fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
+ server.Start()
+}
diff --git a/app/objectstory/api/internal/config/config.go b/app/objectstory/api/internal/config/config.go
new file mode 100644
index 0000000..33aaf22
--- /dev/null
+++ b/app/objectstory/api/internal/config/config.go
@@ -0,0 +1,14 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package config
+
+import "github.com/zeromicro/go-zero/rest"
+
+type Config struct {
+ rest.RestConf
+ Logger struct {
+ AccessSecret string
+ AccessExpire int64
+ }
+}
diff --git a/app/objectstory/api/internal/handler/file/getFileHandler.go b/app/objectstory/api/internal/handler/file/getFileHandler.go
new file mode 100644
index 0000000..5182473
--- /dev/null
+++ b/app/objectstory/api/internal/handler/file/getFileHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package file
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/objectstory/api/internal/logic/file"
+ "juwan-backend/app/objectstory/api/internal/svc"
+ "juwan-backend/app/objectstory/api/internal/types"
+)
+
+// 文件获取接口 (如果是私有文件,通过此接口获取或重定向)
+func GetFileHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.GetFileReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := file.NewGetFileLogic(r.Context(), svcCtx)
+ err := l.GetFile(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.Ok(w)
+ }
+ }
+}
diff --git a/app/objectstory/api/internal/handler/file/uploadHandler.go b/app/objectstory/api/internal/handler/file/uploadHandler.go
new file mode 100644
index 0000000..ebe0f0d
--- /dev/null
+++ b/app/objectstory/api/internal/handler/file/uploadHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package file
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/objectstory/api/internal/logic/file"
+ "juwan-backend/app/objectstory/api/internal/svc"
+ "juwan-backend/app/objectstory/api/internal/types"
+)
+
+// 文件上传接口
+func UploadHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.UploadReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := file.NewUploadLogic(r.Context(), svcCtx)
+ resp, err := l.Upload(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/objectstory/api/internal/handler/routes.go b/app/objectstory/api/internal/handler/routes.go
new file mode 100644
index 0000000..63c042e
--- /dev/null
+++ b/app/objectstory/api/internal/handler/routes.go
@@ -0,0 +1,37 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.9.2
+
+package handler
+
+import (
+ "net/http"
+
+ file "juwan-backend/app/objectstory/api/internal/handler/file"
+ "juwan-backend/app/objectstory/api/internal/svc"
+
+ "github.com/zeromicro/go-zero/rest"
+)
+
+func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
+ server.AddRoutes(
+ rest.WithMiddlewares(
+ []rest.Middleware{serverCtx.FileSizeLimit},
+ []rest.Route{
+ {
+ // 文件获取接口 (如果是私有文件,通过此接口获取或重定向)
+ Method: http.MethodGet,
+ Path: "/files/:fileId",
+ Handler: file.GetFileHandler(serverCtx),
+ },
+ {
+ // 文件上传接口
+ Method: http.MethodPost,
+ Path: "/upload",
+ Handler: file.UploadHandler(serverCtx),
+ },
+ }...,
+ ),
+ rest.WithJwt(serverCtx.Config.Logger.AccessSecret),
+ rest.WithPrefix("/api/v1"),
+ )
+}
diff --git a/app/objectstory/api/internal/logic/file/getFileLogic.go b/app/objectstory/api/internal/logic/file/getFileLogic.go
new file mode 100644
index 0000000..f482810
--- /dev/null
+++ b/app/objectstory/api/internal/logic/file/getFileLogic.go
@@ -0,0 +1,34 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package file
+
+import (
+ "context"
+
+ "juwan-backend/app/objectstory/api/internal/svc"
+ "juwan-backend/app/objectstory/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetFileLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 文件获取接口 (如果是私有文件,通过此接口获取或重定向)
+func NewGetFileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFileLogic {
+ return &GetFileLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *GetFileLogic) GetFile(req *types.GetFileReq) error {
+ // todo: add your logic here and delete this line
+
+ return nil
+}
diff --git a/app/objectstory/api/internal/logic/file/uploadLogic.go b/app/objectstory/api/internal/logic/file/uploadLogic.go
new file mode 100644
index 0000000..8494b4b
--- /dev/null
+++ b/app/objectstory/api/internal/logic/file/uploadLogic.go
@@ -0,0 +1,34 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package file
+
+import (
+ "context"
+
+ "juwan-backend/app/objectstory/api/internal/svc"
+ "juwan-backend/app/objectstory/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type UploadLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 文件上传接口
+func NewUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadLogic {
+ return &UploadLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *UploadLogic) Upload(req *types.UploadReq) (resp *types.UploadResp, err error) {
+ // todo: add your logic here and delete this line
+
+ return
+}
diff --git a/app/objectstory/api/internal/middleware/filesizelimitMiddleware.go b/app/objectstory/api/internal/middleware/filesizelimitMiddleware.go
new file mode 100644
index 0000000..8aecbc7
--- /dev/null
+++ b/app/objectstory/api/internal/middleware/filesizelimitMiddleware.go
@@ -0,0 +1,22 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package middleware
+
+import "net/http"
+
+type FileSizeLimitMiddleware struct {
+}
+
+func NewFileSizeLimitMiddleware() *FileSizeLimitMiddleware {
+ return &FileSizeLimitMiddleware{}
+}
+
+func (m *FileSizeLimitMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ // TODO generate middleware implement function, delete after code implementation
+
+ // Passthrough to next handler if need
+ next(w, r)
+ }
+}
diff --git a/app/objectstory/api/internal/svc/serviceContext.go b/app/objectstory/api/internal/svc/serviceContext.go
new file mode 100644
index 0000000..2edfc00
--- /dev/null
+++ b/app/objectstory/api/internal/svc/serviceContext.go
@@ -0,0 +1,22 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package svc
+
+import (
+ "github.com/zeromicro/go-zero/rest"
+ "juwan-backend/app/objectstory/api/internal/config"
+ "juwan-backend/app/objectstory/api/internal/middleware"
+)
+
+type ServiceContext struct {
+ Config config.Config
+ FileSizeLimit rest.Middleware
+}
+
+func NewServiceContext(c config.Config) *ServiceContext {
+ return &ServiceContext{
+ Config: c,
+ FileSizeLimit: middleware.NewFileSizeLimitMiddleware().Handle,
+ }
+}
diff --git a/app/objectstory/api/internal/types/types.go b/app/objectstory/api/internal/types/types.go
new file mode 100644
index 0000000..b0bc84d
--- /dev/null
+++ b/app/objectstory/api/internal/types/types.go
@@ -0,0 +1,16 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.9.2
+
+package types
+
+type GetFileReq struct {
+ FileId string `path:"fileId"`
+}
+
+type UploadReq struct {
+ Type string `form:"type,options=avatar|chat|post|verification|dispute"` // 文件类型限制
+}
+
+type UploadResp struct {
+ Url string `json:"url"` // 返回 CDN 地址或访问地址
+}
diff --git a/app/objectstory/rpc/etc/file.yaml b/app/objectstory/rpc/etc/file.yaml
new file mode 100644
index 0000000..dd71650
--- /dev/null
+++ b/app/objectstory/rpc/etc/file.yaml
@@ -0,0 +1,6 @@
+Name: file.rpc
+ListenOn: 0.0.0.0:8080
+Etcd:
+ Hosts:
+ - 127.0.0.1:2379
+ Key: file.rpc
diff --git a/app/objectstory/rpc/file.go b/app/objectstory/rpc/file.go
new file mode 100644
index 0000000..e45e019
--- /dev/null
+++ b/app/objectstory/rpc/file.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "juwan-backend/app/objectstory/rpc/internal/config"
+ "juwan-backend/app/objectstory/rpc/internal/server"
+ "juwan-backend/app/objectstory/rpc/internal/svc"
+ "juwan-backend/app/objectstory/rpc/pb"
+
+ "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/file.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) {
+ pb.RegisterFileServiceServer(grpcServer, server.NewFileServiceServer(ctx))
+
+ if c.Mode == service.DevMode || c.Mode == service.TestMode {
+ reflection.Register(grpcServer)
+ }
+ })
+ defer s.Stop()
+
+ fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
+ s.Start()
+}
diff --git a/app/objectstory/rpc/fileservice/fileService.go b/app/objectstory/rpc/fileservice/fileService.go
new file mode 100644
index 0000000..b024ebe
--- /dev/null
+++ b/app/objectstory/rpc/fileservice/fileService.go
@@ -0,0 +1,50 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.9.2
+// Source: objectstory.proto
+
+package fileservice
+
+import (
+ "context"
+
+ "juwan-backend/app/objectstory/rpc/pb"
+
+ "github.com/zeromicro/go-zero/zrpc"
+ "google.golang.org/grpc"
+)
+
+type (
+ GetFileUrlReq = pb.GetFileUrlReq
+ GetFileUrlResp = pb.GetFileUrlResp
+ UploadFileMetadataReq = pb.UploadFileMetadataReq
+ UploadFileResp = pb.UploadFileResp
+
+ FileService interface {
+ // 简单上传(适合小文件,或保存元数据)
+ Upload(ctx context.Context, in *UploadFileMetadataReq, opts ...grpc.CallOption) (*UploadFileResp, error)
+ // 获取文件访问链接(处理私有文件的鉴权)
+ GetFileUrl(ctx context.Context, in *GetFileUrlReq, opts ...grpc.CallOption) (*GetFileUrlResp, error)
+ }
+
+ defaultFileService struct {
+ cli zrpc.Client
+ }
+)
+
+func NewFileService(cli zrpc.Client) FileService {
+ return &defaultFileService{
+ cli: cli,
+ }
+}
+
+// 简单上传(适合小文件,或保存元数据)
+func (m *defaultFileService) Upload(ctx context.Context, in *UploadFileMetadataReq, opts ...grpc.CallOption) (*UploadFileResp, error) {
+ client := pb.NewFileServiceClient(m.cli.Conn())
+ return client.Upload(ctx, in, opts...)
+}
+
+// 获取文件访问链接(处理私有文件的鉴权)
+func (m *defaultFileService) GetFileUrl(ctx context.Context, in *GetFileUrlReq, opts ...grpc.CallOption) (*GetFileUrlResp, error) {
+ client := pb.NewFileServiceClient(m.cli.Conn())
+ return client.GetFileUrl(ctx, in, opts...)
+}
diff --git a/app/objectstory/rpc/internal/config/config.go b/app/objectstory/rpc/internal/config/config.go
new file mode 100644
index 0000000..c1f85b9
--- /dev/null
+++ b/app/objectstory/rpc/internal/config/config.go
@@ -0,0 +1,7 @@
+package config
+
+import "github.com/zeromicro/go-zero/zrpc"
+
+type Config struct {
+ zrpc.RpcServerConf
+}
diff --git a/app/objectstory/rpc/internal/logic/getFileUrlLogic.go b/app/objectstory/rpc/internal/logic/getFileUrlLogic.go
new file mode 100644
index 0000000..3509d4f
--- /dev/null
+++ b/app/objectstory/rpc/internal/logic/getFileUrlLogic.go
@@ -0,0 +1,31 @@
+package logic
+
+import (
+ "context"
+
+ "juwan-backend/app/objectstory/rpc/internal/svc"
+ "juwan-backend/app/objectstory/rpc/pb"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetFileUrlLogic struct {
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+ logx.Logger
+}
+
+func NewGetFileUrlLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFileUrlLogic {
+ return &GetFileUrlLogic{
+ ctx: ctx,
+ svcCtx: svcCtx,
+ Logger: logx.WithContext(ctx),
+ }
+}
+
+// 获取文件访问链接(处理私有文件的鉴权)
+func (l *GetFileUrlLogic) GetFileUrl(in *pb.GetFileUrlReq) (*pb.GetFileUrlResp, error) {
+ // todo: add your logic here and delete this line
+
+ return &pb.GetFileUrlResp{}, nil
+}
diff --git a/app/objectstory/rpc/internal/logic/uploadLogic.go b/app/objectstory/rpc/internal/logic/uploadLogic.go
new file mode 100644
index 0000000..685075c
--- /dev/null
+++ b/app/objectstory/rpc/internal/logic/uploadLogic.go
@@ -0,0 +1,31 @@
+package logic
+
+import (
+ "context"
+
+ "juwan-backend/app/objectstory/rpc/internal/svc"
+ "juwan-backend/app/objectstory/rpc/pb"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type UploadLogic struct {
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+ logx.Logger
+}
+
+func NewUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadLogic {
+ return &UploadLogic{
+ ctx: ctx,
+ svcCtx: svcCtx,
+ Logger: logx.WithContext(ctx),
+ }
+}
+
+// 简单上传(适合小文件,或保存元数据)
+func (l *UploadLogic) Upload(in *pb.UploadFileMetadataReq) (*pb.UploadFileResp, error) {
+ // todo: add your logic here and delete this line
+
+ return &pb.UploadFileResp{}, nil
+}
diff --git a/app/objectstory/rpc/internal/server/fileServiceServer.go b/app/objectstory/rpc/internal/server/fileServiceServer.go
new file mode 100644
index 0000000..423acb0
--- /dev/null
+++ b/app/objectstory/rpc/internal/server/fileServiceServer.go
@@ -0,0 +1,36 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.9.2
+// Source: objectstory.proto
+
+package server
+
+import (
+ "context"
+
+ "juwan-backend/app/objectstory/rpc/internal/logic"
+ "juwan-backend/app/objectstory/rpc/internal/svc"
+ "juwan-backend/app/objectstory/rpc/pb"
+)
+
+type FileServiceServer struct {
+ svcCtx *svc.ServiceContext
+ pb.UnimplementedFileServiceServer
+}
+
+func NewFileServiceServer(svcCtx *svc.ServiceContext) *FileServiceServer {
+ return &FileServiceServer{
+ svcCtx: svcCtx,
+ }
+}
+
+// 简单上传(适合小文件,或保存元数据)
+func (s *FileServiceServer) Upload(ctx context.Context, in *pb.UploadFileMetadataReq) (*pb.UploadFileResp, error) {
+ l := logic.NewUploadLogic(ctx, s.svcCtx)
+ return l.Upload(in)
+}
+
+// 获取文件访问链接(处理私有文件的鉴权)
+func (s *FileServiceServer) GetFileUrl(ctx context.Context, in *pb.GetFileUrlReq) (*pb.GetFileUrlResp, error) {
+ l := logic.NewGetFileUrlLogic(ctx, s.svcCtx)
+ return l.GetFileUrl(in)
+}
diff --git a/app/objectstory/rpc/internal/svc/serviceContext.go b/app/objectstory/rpc/internal/svc/serviceContext.go
new file mode 100644
index 0000000..38212dd
--- /dev/null
+++ b/app/objectstory/rpc/internal/svc/serviceContext.go
@@ -0,0 +1,13 @@
+package svc
+
+import "juwan-backend/app/objectstory/rpc/internal/config"
+
+type ServiceContext struct {
+ Config config.Config
+}
+
+func NewServiceContext(c config.Config) *ServiceContext {
+ return &ServiceContext{
+ Config: c,
+ }
+}
diff --git a/app/objectstory/rpc/pb/objectstory.pb.go b/app/objectstory/rpc/pb/objectstory.pb.go
new file mode 100644
index 0000000..5e4fc52
--- /dev/null
+++ b/app/objectstory/rpc/pb/objectstory.pb.go
@@ -0,0 +1,326 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v5.29.6
+// source: objectstory.proto
+
+package pb
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// 文件上传的元数据信息
+type UploadFileMetadataReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ FileName string `protobuf:"bytes,1,opt,name=fileName,proto3" json:"fileName,omitempty"`
+ FileSize int64 `protobuf:"varint,2,opt,name=fileSize,proto3" json:"fileSize,omitempty"`
+ FileType string `protobuf:"bytes,3,opt,name=fileType,proto3" json:"fileType,omitempty"` // avatar, chat, etc.
+ UserId string `protobuf:"bytes,4,opt,name=userId,proto3" json:"userId,omitempty"`
+ FileData []byte `protobuf:"bytes,5,opt,name=fileData,proto3" json:"fileData,omitempty"` // 如果文件很小可以直接传,大文件建议API层直接传S3,RPC只传元数据
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *UploadFileMetadataReq) Reset() {
+ *x = UploadFileMetadataReq{}
+ mi := &file_objectstory_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *UploadFileMetadataReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UploadFileMetadataReq) ProtoMessage() {}
+
+func (x *UploadFileMetadataReq) ProtoReflect() protoreflect.Message {
+ mi := &file_objectstory_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use UploadFileMetadataReq.ProtoReflect.Descriptor instead.
+func (*UploadFileMetadataReq) Descriptor() ([]byte, []int) {
+ return file_objectstory_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *UploadFileMetadataReq) GetFileName() string {
+ if x != nil {
+ return x.FileName
+ }
+ return ""
+}
+
+func (x *UploadFileMetadataReq) GetFileSize() int64 {
+ if x != nil {
+ return x.FileSize
+ }
+ return 0
+}
+
+func (x *UploadFileMetadataReq) GetFileType() string {
+ if x != nil {
+ return x.FileType
+ }
+ return ""
+}
+
+func (x *UploadFileMetadataReq) GetUserId() string {
+ if x != nil {
+ return x.UserId
+ }
+ return ""
+}
+
+func (x *UploadFileMetadataReq) GetFileData() []byte {
+ if x != nil {
+ return x.FileData
+ }
+ return nil
+}
+
+type UploadFileResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
+ FileId string `protobuf:"bytes,2,opt,name=fileId,proto3" json:"fileId,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *UploadFileResp) Reset() {
+ *x = UploadFileResp{}
+ mi := &file_objectstory_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *UploadFileResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UploadFileResp) ProtoMessage() {}
+
+func (x *UploadFileResp) ProtoReflect() protoreflect.Message {
+ mi := &file_objectstory_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use UploadFileResp.ProtoReflect.Descriptor instead.
+func (*UploadFileResp) Descriptor() ([]byte, []int) {
+ return file_objectstory_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *UploadFileResp) GetUrl() string {
+ if x != nil {
+ return x.Url
+ }
+ return ""
+}
+
+func (x *UploadFileResp) GetFileId() string {
+ if x != nil {
+ return x.FileId
+ }
+ return ""
+}
+
+type GetFileUrlReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ FileId string `protobuf:"bytes,1,opt,name=fileId,proto3" json:"fileId,omitempty"`
+ UserId string `protobuf:"bytes,2,opt,name=userId,proto3" json:"userId,omitempty"` // 用于鉴权
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *GetFileUrlReq) Reset() {
+ *x = GetFileUrlReq{}
+ mi := &file_objectstory_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *GetFileUrlReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetFileUrlReq) ProtoMessage() {}
+
+func (x *GetFileUrlReq) ProtoReflect() protoreflect.Message {
+ mi := &file_objectstory_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetFileUrlReq.ProtoReflect.Descriptor instead.
+func (*GetFileUrlReq) Descriptor() ([]byte, []int) {
+ return file_objectstory_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *GetFileUrlReq) GetFileId() string {
+ if x != nil {
+ return x.FileId
+ }
+ return ""
+}
+
+func (x *GetFileUrlReq) GetUserId() string {
+ if x != nil {
+ return x.UserId
+ }
+ return ""
+}
+
+type GetFileUrlResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // 可能是带签名的临时 URL
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *GetFileUrlResp) Reset() {
+ *x = GetFileUrlResp{}
+ mi := &file_objectstory_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *GetFileUrlResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetFileUrlResp) ProtoMessage() {}
+
+func (x *GetFileUrlResp) ProtoReflect() protoreflect.Message {
+ mi := &file_objectstory_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetFileUrlResp.ProtoReflect.Descriptor instead.
+func (*GetFileUrlResp) Descriptor() ([]byte, []int) {
+ return file_objectstory_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *GetFileUrlResp) GetUrl() string {
+ if x != nil {
+ return x.Url
+ }
+ return ""
+}
+
+var File_objectstory_proto protoreflect.FileDescriptor
+
+const file_objectstory_proto_rawDesc = "" +
+ "\n" +
+ "\x11objectstory.proto\x12\x04file\"\x9f\x01\n" +
+ "\x15UploadFileMetadataReq\x12\x1a\n" +
+ "\bfileName\x18\x01 \x01(\tR\bfileName\x12\x1a\n" +
+ "\bfileSize\x18\x02 \x01(\x03R\bfileSize\x12\x1a\n" +
+ "\bfileType\x18\x03 \x01(\tR\bfileType\x12\x16\n" +
+ "\x06userId\x18\x04 \x01(\tR\x06userId\x12\x1a\n" +
+ "\bfileData\x18\x05 \x01(\fR\bfileData\":\n" +
+ "\x0eUploadFileResp\x12\x10\n" +
+ "\x03url\x18\x01 \x01(\tR\x03url\x12\x16\n" +
+ "\x06fileId\x18\x02 \x01(\tR\x06fileId\"?\n" +
+ "\rGetFileUrlReq\x12\x16\n" +
+ "\x06fileId\x18\x01 \x01(\tR\x06fileId\x12\x16\n" +
+ "\x06userId\x18\x02 \x01(\tR\x06userId\"\"\n" +
+ "\x0eGetFileUrlResp\x12\x10\n" +
+ "\x03url\x18\x01 \x01(\tR\x03url2\x83\x01\n" +
+ "\vFileService\x12;\n" +
+ "\x06Upload\x12\x1b.file.UploadFileMetadataReq\x1a\x14.file.UploadFileResp\x127\n" +
+ "\n" +
+ "GetFileUrl\x12\x13.file.GetFileUrlReq\x1a\x14.file.GetFileUrlRespB\x06Z\x04./pbb\x06proto3"
+
+var (
+ file_objectstory_proto_rawDescOnce sync.Once
+ file_objectstory_proto_rawDescData []byte
+)
+
+func file_objectstory_proto_rawDescGZIP() []byte {
+ file_objectstory_proto_rawDescOnce.Do(func() {
+ file_objectstory_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_objectstory_proto_rawDesc), len(file_objectstory_proto_rawDesc)))
+ })
+ return file_objectstory_proto_rawDescData
+}
+
+var file_objectstory_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_objectstory_proto_goTypes = []any{
+ (*UploadFileMetadataReq)(nil), // 0: file.UploadFileMetadataReq
+ (*UploadFileResp)(nil), // 1: file.UploadFileResp
+ (*GetFileUrlReq)(nil), // 2: file.GetFileUrlReq
+ (*GetFileUrlResp)(nil), // 3: file.GetFileUrlResp
+}
+var file_objectstory_proto_depIdxs = []int32{
+ 0, // 0: file.FileService.Upload:input_type -> file.UploadFileMetadataReq
+ 2, // 1: file.FileService.GetFileUrl:input_type -> file.GetFileUrlReq
+ 1, // 2: file.FileService.Upload:output_type -> file.UploadFileResp
+ 3, // 3: file.FileService.GetFileUrl:output_type -> file.GetFileUrlResp
+ 2, // [2:4] is the sub-list for method output_type
+ 0, // [0:2] is the sub-list for method input_type
+ 0, // [0:0] is the sub-list for extension type_name
+ 0, // [0:0] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_objectstory_proto_init() }
+func file_objectstory_proto_init() {
+ if File_objectstory_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_objectstory_proto_rawDesc), len(file_objectstory_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 1,
+ },
+ GoTypes: file_objectstory_proto_goTypes,
+ DependencyIndexes: file_objectstory_proto_depIdxs,
+ MessageInfos: file_objectstory_proto_msgTypes,
+ }.Build()
+ File_objectstory_proto = out.File
+ file_objectstory_proto_goTypes = nil
+ file_objectstory_proto_depIdxs = nil
+}
diff --git a/app/objectstory/rpc/pb/objectstory_grpc.pb.go b/app/objectstory/rpc/pb/objectstory_grpc.pb.go
new file mode 100644
index 0000000..c88400b
--- /dev/null
+++ b/app/objectstory/rpc/pb/objectstory_grpc.pb.go
@@ -0,0 +1,163 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.6.1
+// - protoc v5.29.6
+// source: objectstory.proto
+
+package pb
+
+import (
+ context "context"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
+
+const (
+ FileService_Upload_FullMethodName = "/file.FileService/Upload"
+ FileService_GetFileUrl_FullMethodName = "/file.FileService/GetFileUrl"
+)
+
+// FileServiceClient is the client API for FileService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type FileServiceClient interface {
+ // 简单上传(适合小文件,或保存元数据)
+ Upload(ctx context.Context, in *UploadFileMetadataReq, opts ...grpc.CallOption) (*UploadFileResp, error)
+ // 获取文件访问链接(处理私有文件的鉴权)
+ GetFileUrl(ctx context.Context, in *GetFileUrlReq, opts ...grpc.CallOption) (*GetFileUrlResp, error)
+}
+
+type fileServiceClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewFileServiceClient(cc grpc.ClientConnInterface) FileServiceClient {
+ return &fileServiceClient{cc}
+}
+
+func (c *fileServiceClient) Upload(ctx context.Context, in *UploadFileMetadataReq, opts ...grpc.CallOption) (*UploadFileResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(UploadFileResp)
+ err := c.cc.Invoke(ctx, FileService_Upload_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *fileServiceClient) GetFileUrl(ctx context.Context, in *GetFileUrlReq, opts ...grpc.CallOption) (*GetFileUrlResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(GetFileUrlResp)
+ err := c.cc.Invoke(ctx, FileService_GetFileUrl_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// FileServiceServer is the server API for FileService service.
+// All implementations must embed UnimplementedFileServiceServer
+// for forward compatibility.
+type FileServiceServer interface {
+ // 简单上传(适合小文件,或保存元数据)
+ Upload(context.Context, *UploadFileMetadataReq) (*UploadFileResp, error)
+ // 获取文件访问链接(处理私有文件的鉴权)
+ GetFileUrl(context.Context, *GetFileUrlReq) (*GetFileUrlResp, error)
+ mustEmbedUnimplementedFileServiceServer()
+}
+
+// UnimplementedFileServiceServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedFileServiceServer struct{}
+
+func (UnimplementedFileServiceServer) Upload(context.Context, *UploadFileMetadataReq) (*UploadFileResp, error) {
+ return nil, status.Error(codes.Unimplemented, "method Upload not implemented")
+}
+func (UnimplementedFileServiceServer) GetFileUrl(context.Context, *GetFileUrlReq) (*GetFileUrlResp, error) {
+ return nil, status.Error(codes.Unimplemented, "method GetFileUrl not implemented")
+}
+func (UnimplementedFileServiceServer) mustEmbedUnimplementedFileServiceServer() {}
+func (UnimplementedFileServiceServer) testEmbeddedByValue() {}
+
+// UnsafeFileServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to FileServiceServer will
+// result in compilation errors.
+type UnsafeFileServiceServer interface {
+ mustEmbedUnimplementedFileServiceServer()
+}
+
+func RegisterFileServiceServer(s grpc.ServiceRegistrar, srv FileServiceServer) {
+ // If the following call panics, it indicates UnimplementedFileServiceServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
+ s.RegisterService(&FileService_ServiceDesc, srv)
+}
+
+func _FileService_Upload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(UploadFileMetadataReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(FileServiceServer).Upload(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: FileService_Upload_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(FileServiceServer).Upload(ctx, req.(*UploadFileMetadataReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _FileService_GetFileUrl_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(GetFileUrlReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(FileServiceServer).GetFileUrl(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: FileService_GetFileUrl_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(FileServiceServer).GetFileUrl(ctx, req.(*GetFileUrlReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// FileService_ServiceDesc is the grpc.ServiceDesc for FileService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var FileService_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "file.FileService",
+ HandlerType: (*FileServiceServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "Upload",
+ Handler: _FileService_Upload_Handler,
+ },
+ {
+ MethodName: "GetFileUrl",
+ Handler: _FileService_GetFileUrl_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "objectstory.proto",
+}
diff --git a/app/user_verifications/rpc/etc/pb.yaml b/app/user_verifications/rpc/etc/pb.yaml
index 8faa7f7..097bc22 100644
--- a/app/user_verifications/rpc/etc/pb.yaml
+++ b/app/user_verifications/rpc/etc/pb.yaml
@@ -3,8 +3,12 @@ ListenOn: 0.0.0.0:8080
DataSource: "${DB_URI}?sslmode=disable"
+UserVeriRpcConf :
+ Target: k8s://juwan/user_verifications-rpc-svc.juwan:8080
+
SnowflakeRpcConf:
- Target: k8s://juwan/snowflake-svc:8080
+ Target: k8s://juwan/snowflake-svc.juwan:8080
+
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
@@ -20,9 +24,5 @@ CacheConf:
Pass: "${REDIS_PASSWORD}"
User: "default"
-Jwt:
- SecretKey: "${JWT_SECRET_KEY}"
- Issuer: "juwan-user-rpc"
-
Log:
Level: info
diff --git a/app/user_verifications/rpc/internal/config/config.go b/app/user_verifications/rpc/internal/config/config.go
index c1f85b9..f951f06 100644
--- a/app/user_verifications/rpc/internal/config/config.go
+++ b/app/user_verifications/rpc/internal/config/config.go
@@ -1,7 +1,17 @@
package config
-import "github.com/zeromicro/go-zero/zrpc"
+import (
+ "github.com/zeromicro/go-zero/core/stores/cache"
+ "github.com/zeromicro/go-zero/zrpc"
+)
type Config struct {
zrpc.RpcServerConf
+ DB struct {
+ Master string
+ Slave string
+ }
+ CacheConf cache.CacheConf
+ UserVeriRpcConf zrpc.RpcClientConf
+ SnowflakeRpcConf zrpc.RpcClientConf
}
diff --git a/app/user_verifications/rpc/internal/logic/addUserVerificationsLogic.go b/app/user_verifications/rpc/internal/logic/addUserVerificationsLogic.go
index a0c859a..28b56b9 100644
--- a/app/user_verifications/rpc/internal/logic/addUserVerificationsLogic.go
+++ b/app/user_verifications/rpc/internal/logic/addUserVerificationsLogic.go
@@ -2,6 +2,10 @@ package logic
import (
"context"
+ "encoding/json"
+ "errors"
+ "juwan-backend/app/snowflake/rpc/snowflake"
+ "juwan-backend/app/user_verifications/rpc/internal/models/schema"
"juwan-backend/app/user_verifications/rpc/internal/svc"
"juwan-backend/app/user_verifications/rpc/pb"
@@ -25,7 +29,28 @@ func NewAddUserVerificationsLogic(ctx context.Context, svcCtx *svc.ServiceContex
// -----------------------userVerifications-----------------------
func (l *AddUserVerificationsLogic) AddUserVerifications(in *pb.AddUserVerificationsReq) (*pb.AddUserVerificationsResp, error) {
- // todo: add your logic here and delete this line
+ nextIdResp, err := l.svcCtx.SnowflakeRpc.NextId(l.ctx, &snowflake.NextIdReq{})
+ if err != nil {
+ return nil, err
+ }
+
+ materials := schema.MaterialStruct{}
+ err = json.Unmarshal([]byte(in.Materials), &materials)
+ if err != nil {
+ logx.Errorf("Unmarshal %v materials failed: %s", in.Materials, err)
+ return nil, errors.New("bad input materials")
+ }
+
+ err = l.svcCtx.UserVeriModelRW.Create().
+ SetID(nextIdResp.Id).
+ SetUserID(in.UserId).
+ SetRole(in.Role).
+ SetStatus("padding").
+ SetMaterials(materials).
+ Exec(l.ctx)
+ if err != nil {
+ return nil, err
+ }
return &pb.AddUserVerificationsResp{}, nil
}
diff --git a/app/user_verifications/rpc/internal/logic/delUserVerificationsLogic.go b/app/user_verifications/rpc/internal/logic/delUserVerificationsLogic.go
index 4209d18..2a0d334 100644
--- a/app/user_verifications/rpc/internal/logic/delUserVerificationsLogic.go
+++ b/app/user_verifications/rpc/internal/logic/delUserVerificationsLogic.go
@@ -25,6 +25,9 @@ func NewDelUserVerificationsLogic(ctx context.Context, svcCtx *svc.ServiceContex
func (l *DelUserVerificationsLogic) DelUserVerifications(in *pb.DelUserVerificationsReq) (*pb.DelUserVerificationsResp, error) {
// todo: add your logic here and delete this line
-
+ err := l.svcCtx.UserVeriModelRW.DeleteOneID(in.Id).Exec(l.ctx)
+ if err != nil {
+ return nil, err
+ }
return &pb.DelUserVerificationsResp{}, nil
}
diff --git a/app/user_verifications/rpc/internal/logic/getUserVerificationsByIdLogic.go b/app/user_verifications/rpc/internal/logic/getUserVerificationsByIdLogic.go
index 4698785..9765584 100644
--- a/app/user_verifications/rpc/internal/logic/getUserVerificationsByIdLogic.go
+++ b/app/user_verifications/rpc/internal/logic/getUserVerificationsByIdLogic.go
@@ -2,10 +2,12 @@ package logic
import (
"context"
+ "errors"
"juwan-backend/app/user_verifications/rpc/internal/svc"
"juwan-backend/app/user_verifications/rpc/pb"
+ "github.com/jinzhu/copier"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -24,7 +26,23 @@ func NewGetUserVerificationsByIdLogic(ctx context.Context, svcCtx *svc.ServiceCo
}
func (l *GetUserVerificationsByIdLogic) GetUserVerificationsById(in *pb.GetUserVerificationsByIdReq) (*pb.GetUserVerificationsByIdResp, error) {
- // todo: add your logic here and delete this line
+ userVerification, err := l.svcCtx.UserVeriModelRO.Get(l.ctx, in.Id)
+ if err != nil {
+ logx.Errorf("GetUserVerificationsById err: %v", err)
+ return nil, errors.New("get VerificationsById err")
+ }
+ pbVerification := pb.UserVerifications{}
+ err = copier.Copy(&pbVerification, userVerification)
+ if err != nil {
+ logx.Errorf("copier copy err: %v", err)
+ return nil, errors.New("copy Verification err")
+ }
+ createAt := userVerification.CreatedAt.Unix()
+ updateAt := userVerification.UpdatedAt.Unix()
+ pbVerification.CreatedAt = createAt
+ pbVerification.UpdatedAt = updateAt
- return &pb.GetUserVerificationsByIdResp{}, nil
+ return &pb.GetUserVerificationsByIdResp{
+ UserVerifications: &pbVerification,
+ }, nil
}
diff --git a/app/user_verifications/rpc/internal/logic/searchUserVerificationsLogic.go b/app/user_verifications/rpc/internal/logic/searchUserVerificationsLogic.go
index 3ee5d05..a3507c0 100644
--- a/app/user_verifications/rpc/internal/logic/searchUserVerificationsLogic.go
+++ b/app/user_verifications/rpc/internal/logic/searchUserVerificationsLogic.go
@@ -2,10 +2,13 @@ package logic
import (
"context"
-
+ "errors"
+ "juwan-backend/app/user_verifications/rpc/internal/models"
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
"juwan-backend/app/user_verifications/rpc/internal/svc"
"juwan-backend/app/user_verifications/rpc/pb"
+ "github.com/jinzhu/copier"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -24,7 +27,50 @@ func NewSearchUserVerificationsLogic(ctx context.Context, svcCtx *svc.ServiceCon
}
func (l *SearchUserVerificationsLogic) SearchUserVerifications(in *pb.SearchUserVerificationsReq) (*pb.SearchUserVerificationsResp, error) {
- // todo: add your logic here and delete this line
-
- return &pb.SearchUserVerificationsResp{}, nil
+ if in.Limit > 1000 {
+ logx.Errorf("Limit exceeds max limit: %d", in.Limit)
+ return nil, errors.New("limit exceeds max limit")
+ }
+ verifications, err := l.svcCtx.UserVeriModelRO.Query().Where(userverifications.Or(
+ userverifications.UserIDEQ(in.UserId),
+ userverifications.StatusEQ(in.Status),
+ userverifications.Role(in.Role),
+ )).
+ Offset(int(in.Page * in.Limit)).
+ Limit(int(in.Limit)).
+ All(l.ctx)
+ if err != nil {
+ logx.Errorf("Get all verifications err: %s", err.Error())
+ return nil, errors.New("get all verifications err")
+ }
+ return &pb.SearchUserVerificationsResp{
+ UserVerifications: convertModelUserVerificationsToProto(verifications),
+ }, nil
+}
+
+func convertModelUserVerificationToProto(modelUserVerification *models.UserVerifications) *pb.UserVerifications {
+
+ if modelUserVerification == nil {
+ return nil
+ }
+ out := &pb.UserVerifications{}
+ err := copier.Copy(out, modelUserVerification)
+ if err != nil {
+ logx.Errorf("copy modelUserVerification err: %s", err.Error())
+ return out
+ }
+
+ out.CreatedAt = modelUserVerification.CreatedAt.Unix()
+ out.UpdatedAt = modelUserVerification.UpdatedAt.Unix()
+ return out
+}
+
+func convertModelUserVerificationsToProto(modelUserVerifications []*models.UserVerifications) []*pb.UserVerifications {
+
+ out := make([]*pb.UserVerifications, 0, len(modelUserVerifications))
+ for _, modelUserVerification := range modelUserVerifications {
+ out = append(out, convertModelUserVerificationToProto(modelUserVerification))
+ }
+
+ return out
}
diff --git a/app/user_verifications/rpc/internal/logic/updateUserVerificationsLogic.go b/app/user_verifications/rpc/internal/logic/updateUserVerificationsLogic.go
index 15e7fb1..a4439dd 100644
--- a/app/user_verifications/rpc/internal/logic/updateUserVerificationsLogic.go
+++ b/app/user_verifications/rpc/internal/logic/updateUserVerificationsLogic.go
@@ -2,6 +2,9 @@ package logic
import (
"context"
+ "encoding/json"
+ "errors"
+ "juwan-backend/app/user_verifications/rpc/internal/models/schema"
"juwan-backend/app/user_verifications/rpc/internal/svc"
"juwan-backend/app/user_verifications/rpc/pb"
@@ -24,7 +27,31 @@ func NewUpdateUserVerificationsLogic(ctx context.Context, svcCtx *svc.ServiceCon
}
func (l *UpdateUserVerificationsLogic) UpdateUserVerifications(in *pb.UpdateUserVerificationsReq) (*pb.UpdateUserVerificationsResp, error) {
- // todo: add your logic here and delete this line
+ var materials *schema.MaterialStruct
+ materials = nil
+ if in.Materials != nil {
+ err := json.Unmarshal([]byte(*in.Materials), &materials)
+ if err != nil {
+ logx.Errorf("Unmarshal materials failed, err:%v.", err)
+ return nil, errors.New("bad input materials")
+ }
+ if len(materials.GameScreenshots) > 20 {
+ logx.Errorf("User %v upload oo many game screenshots: %d", in.UserId, len(materials.GameScreenshots))
+ return nil, errors.New("too many game screenshots")
+ }
+ }
+
+ _, err := l.svcCtx.UserVeriModelRW.UpdateOneID(in.Id).
+ SetNillableRejectReason(in.RejectReason).
+ SetNillableMaterials(materials).
+ SetNillableRole(in.Role).
+ SetNillableStatus(in.Status).
+ SetReviewedBy(in.ReviewedBy).
+ Save(l.ctx)
+ if err != nil {
+ logx.Errorf("save user verifications failed, err:%v.", err)
+ return nil, errors.New("save user verifications failed")
+ }
return &pb.UpdateUserVerificationsResp{}, nil
}
diff --git a/app/user_verifications/rpc/internal/models/client.go b/app/user_verifications/rpc/internal/models/client.go
new file mode 100644
index 0000000..4ef0ebb
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/client.go
@@ -0,0 +1,341 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "log"
+ "reflect"
+
+ "juwan-backend/app/user_verifications/rpc/internal/models/migrate"
+
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect"
+ "entgo.io/ent/dialect/sql"
+)
+
+// Client is the client that holds all ent builders.
+type Client struct {
+ config
+ // Schema is the client for creating, migrating and dropping schema.
+ Schema *migrate.Schema
+ // UserVerifications is the client for interacting with the UserVerifications builders.
+ UserVerifications *UserVerificationsClient
+}
+
+// NewClient creates a new client configured with the given options.
+func NewClient(opts ...Option) *Client {
+ client := &Client{config: newConfig(opts...)}
+ client.init()
+ return client
+}
+
+func (c *Client) init() {
+ c.Schema = migrate.NewSchema(c.driver)
+ c.UserVerifications = NewUserVerificationsClient(c.config)
+}
+
+type (
+ // config is the configuration for the client and its builder.
+ config struct {
+ // driver used for executing database requests.
+ driver dialect.Driver
+ // debug enable a debug logging.
+ debug bool
+ // log used for logging on debug mode.
+ log func(...any)
+ // hooks to execute on mutations.
+ hooks *hooks
+ // interceptors to execute on queries.
+ inters *inters
+ }
+ // Option function to configure the client.
+ Option func(*config)
+)
+
+// newConfig creates a new config for the client.
+func newConfig(opts ...Option) config {
+ cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}}
+ cfg.options(opts...)
+ return cfg
+}
+
+// options applies the options on the config object.
+func (c *config) options(opts ...Option) {
+ for _, opt := range opts {
+ opt(c)
+ }
+ if c.debug {
+ c.driver = dialect.Debug(c.driver, c.log)
+ }
+}
+
+// Debug enables debug logging on the ent.Driver.
+func Debug() Option {
+ return func(c *config) {
+ c.debug = true
+ }
+}
+
+// Log sets the logging function for debug mode.
+func Log(fn func(...any)) Option {
+ return func(c *config) {
+ c.log = fn
+ }
+}
+
+// Driver configures the client driver.
+func Driver(driver dialect.Driver) Option {
+ return func(c *config) {
+ c.driver = driver
+ }
+}
+
+// Open opens a database/sql.DB specified by the driver name and
+// the data source name, and returns a new client attached to it.
+// Optional parameters can be added for configuring the client.
+func Open(driverName, dataSourceName string, options ...Option) (*Client, error) {
+ switch driverName {
+ case dialect.MySQL, dialect.Postgres, dialect.SQLite:
+ drv, err := sql.Open(driverName, dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+ return NewClient(append(options, Driver(drv))...), nil
+ default:
+ return nil, fmt.Errorf("unsupported driver: %q", driverName)
+ }
+}
+
+// ErrTxStarted is returned when trying to start a new transaction from a transactional client.
+var ErrTxStarted = errors.New("models: cannot start a transaction within a transaction")
+
+// Tx returns a new transactional client. The provided context
+// is used until the transaction is committed or rolled back.
+func (c *Client) Tx(ctx context.Context) (*Tx, error) {
+ if _, ok := c.driver.(*txDriver); ok {
+ return nil, ErrTxStarted
+ }
+ tx, err := newTx(ctx, c.driver)
+ if err != nil {
+ return nil, fmt.Errorf("models: starting a transaction: %w", err)
+ }
+ cfg := c.config
+ cfg.driver = tx
+ return &Tx{
+ ctx: ctx,
+ config: cfg,
+ UserVerifications: NewUserVerificationsClient(cfg),
+ }, nil
+}
+
+// BeginTx returns a transactional client with specified options.
+func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
+ if _, ok := c.driver.(*txDriver); ok {
+ return nil, errors.New("ent: cannot start a transaction within a transaction")
+ }
+ tx, err := c.driver.(interface {
+ BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error)
+ }).BeginTx(ctx, opts)
+ if err != nil {
+ return nil, fmt.Errorf("ent: starting a transaction: %w", err)
+ }
+ cfg := c.config
+ cfg.driver = &txDriver{tx: tx, drv: c.driver}
+ return &Tx{
+ ctx: ctx,
+ config: cfg,
+ UserVerifications: NewUserVerificationsClient(cfg),
+ }, nil
+}
+
+// Debug returns a new debug-client. It's used to get verbose logging on specific operations.
+//
+// client.Debug().
+// UserVerifications.
+// Query().
+// Count(ctx)
+func (c *Client) Debug() *Client {
+ if c.debug {
+ return c
+ }
+ cfg := c.config
+ cfg.driver = dialect.Debug(c.driver, c.log)
+ client := &Client{config: cfg}
+ client.init()
+ return client
+}
+
+// Close closes the database connection and prevents new queries from starting.
+func (c *Client) Close() error {
+ return c.driver.Close()
+}
+
+// Use adds the mutation hooks to all the entity clients.
+// In order to add hooks to a specific client, call: `client.Node.Use(...)`.
+func (c *Client) Use(hooks ...Hook) {
+ c.UserVerifications.Use(hooks...)
+}
+
+// Intercept adds the query interceptors to all the entity clients.
+// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`.
+func (c *Client) Intercept(interceptors ...Interceptor) {
+ c.UserVerifications.Intercept(interceptors...)
+}
+
+// Mutate implements the ent.Mutator interface.
+func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
+ switch m := m.(type) {
+ case *UserVerificationsMutation:
+ return c.UserVerifications.mutate(ctx, m)
+ default:
+ return nil, fmt.Errorf("models: unknown mutation type %T", m)
+ }
+}
+
+// UserVerificationsClient is a client for the UserVerifications schema.
+type UserVerificationsClient struct {
+ config
+}
+
+// NewUserVerificationsClient returns a client for the UserVerifications from the given config.
+func NewUserVerificationsClient(c config) *UserVerificationsClient {
+ return &UserVerificationsClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `userverifications.Hooks(f(g(h())))`.
+func (c *UserVerificationsClient) Use(hooks ...Hook) {
+ c.hooks.UserVerifications = append(c.hooks.UserVerifications, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `userverifications.Intercept(f(g(h())))`.
+func (c *UserVerificationsClient) Intercept(interceptors ...Interceptor) {
+ c.inters.UserVerifications = append(c.inters.UserVerifications, interceptors...)
+}
+
+// Create returns a builder for creating a UserVerifications entity.
+func (c *UserVerificationsClient) Create() *UserVerificationsCreate {
+ mutation := newUserVerificationsMutation(c.config, OpCreate)
+ return &UserVerificationsCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of UserVerifications entities.
+func (c *UserVerificationsClient) CreateBulk(builders ...*UserVerificationsCreate) *UserVerificationsCreateBulk {
+ return &UserVerificationsCreateBulk{config: c.config, builders: builders}
+}
+
+// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
+// a builder and applies setFunc on it.
+func (c *UserVerificationsClient) MapCreateBulk(slice any, setFunc func(*UserVerificationsCreate, int)) *UserVerificationsCreateBulk {
+ rv := reflect.ValueOf(slice)
+ if rv.Kind() != reflect.Slice {
+ return &UserVerificationsCreateBulk{err: fmt.Errorf("calling to UserVerificationsClient.MapCreateBulk with wrong type %T, need slice", slice)}
+ }
+ builders := make([]*UserVerificationsCreate, rv.Len())
+ for i := 0; i < rv.Len(); i++ {
+ builders[i] = c.Create()
+ setFunc(builders[i], i)
+ }
+ return &UserVerificationsCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for UserVerifications.
+func (c *UserVerificationsClient) Update() *UserVerificationsUpdate {
+ mutation := newUserVerificationsMutation(c.config, OpUpdate)
+ return &UserVerificationsUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *UserVerificationsClient) UpdateOne(_m *UserVerifications) *UserVerificationsUpdateOne {
+ mutation := newUserVerificationsMutation(c.config, OpUpdateOne, withUserVerifications(_m))
+ return &UserVerificationsUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *UserVerificationsClient) UpdateOneID(id int64) *UserVerificationsUpdateOne {
+ mutation := newUserVerificationsMutation(c.config, OpUpdateOne, withUserVerificationsID(id))
+ return &UserVerificationsUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for UserVerifications.
+func (c *UserVerificationsClient) Delete() *UserVerificationsDelete {
+ mutation := newUserVerificationsMutation(c.config, OpDelete)
+ return &UserVerificationsDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *UserVerificationsClient) DeleteOne(_m *UserVerifications) *UserVerificationsDeleteOne {
+ return c.DeleteOneID(_m.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *UserVerificationsClient) DeleteOneID(id int64) *UserVerificationsDeleteOne {
+ builder := c.Delete().Where(userverifications.ID(id))
+ builder.mutation.id = &id
+ builder.mutation.op = OpDeleteOne
+ return &UserVerificationsDeleteOne{builder}
+}
+
+// Query returns a query builder for UserVerifications.
+func (c *UserVerificationsClient) Query() *UserVerificationsQuery {
+ return &UserVerificationsQuery{
+ config: c.config,
+ ctx: &QueryContext{Type: TypeUserVerifications},
+ inters: c.Interceptors(),
+ }
+}
+
+// Get returns a UserVerifications entity by its id.
+func (c *UserVerificationsClient) Get(ctx context.Context, id int64) (*UserVerifications, error) {
+ return c.Query().Where(userverifications.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *UserVerificationsClient) GetX(ctx context.Context, id int64) *UserVerifications {
+ obj, err := c.Get(ctx, id)
+ if err != nil {
+ panic(err)
+ }
+ return obj
+}
+
+// Hooks returns the client hooks.
+func (c *UserVerificationsClient) Hooks() []Hook {
+ return c.hooks.UserVerifications
+}
+
+// Interceptors returns the client interceptors.
+func (c *UserVerificationsClient) Interceptors() []Interceptor {
+ return c.inters.UserVerifications
+}
+
+func (c *UserVerificationsClient) mutate(ctx context.Context, m *UserVerificationsMutation) (Value, error) {
+ switch m.Op() {
+ case OpCreate:
+ return (&UserVerificationsCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpUpdate:
+ return (&UserVerificationsUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpUpdateOne:
+ return (&UserVerificationsUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpDelete, OpDeleteOne:
+ return (&UserVerificationsDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+ default:
+ return nil, fmt.Errorf("models: unknown UserVerifications mutation op: %q", m.Op())
+ }
+}
+
+// hooks and interceptors per client, for fast access.
+type (
+ hooks struct {
+ UserVerifications []ent.Hook
+ }
+ inters struct {
+ UserVerifications []ent.Interceptor
+ }
+)
diff --git a/app/user_verifications/rpc/internal/models/ent.go b/app/user_verifications/rpc/internal/models/ent.go
new file mode 100644
index 0000000..9d6ac26
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/ent.go
@@ -0,0 +1,608 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
+ "reflect"
+ "sync"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+)
+
+// ent aliases to avoid import conflicts in user's code.
+type (
+ Op = ent.Op
+ Hook = ent.Hook
+ Value = ent.Value
+ Query = ent.Query
+ QueryContext = ent.QueryContext
+ Querier = ent.Querier
+ QuerierFunc = ent.QuerierFunc
+ Interceptor = ent.Interceptor
+ InterceptFunc = ent.InterceptFunc
+ Traverser = ent.Traverser
+ TraverseFunc = ent.TraverseFunc
+ Policy = ent.Policy
+ Mutator = ent.Mutator
+ Mutation = ent.Mutation
+ MutateFunc = ent.MutateFunc
+)
+
+type clientCtxKey struct{}
+
+// FromContext returns a Client stored inside a context, or nil if there isn't one.
+func FromContext(ctx context.Context) *Client {
+ c, _ := ctx.Value(clientCtxKey{}).(*Client)
+ return c
+}
+
+// NewContext returns a new context with the given Client attached.
+func NewContext(parent context.Context, c *Client) context.Context {
+ return context.WithValue(parent, clientCtxKey{}, c)
+}
+
+type txCtxKey struct{}
+
+// TxFromContext returns a Tx stored inside a context, or nil if there isn't one.
+func TxFromContext(ctx context.Context) *Tx {
+ tx, _ := ctx.Value(txCtxKey{}).(*Tx)
+ return tx
+}
+
+// NewTxContext returns a new context with the given Tx attached.
+func NewTxContext(parent context.Context, tx *Tx) context.Context {
+ return context.WithValue(parent, txCtxKey{}, tx)
+}
+
+// OrderFunc applies an ordering on the sql selector.
+// Deprecated: Use Asc/Desc functions or the package builders instead.
+type OrderFunc func(*sql.Selector)
+
+var (
+ initCheck sync.Once
+ columnCheck sql.ColumnCheck
+)
+
+// checkColumn checks if the column exists in the given table.
+func checkColumn(t, c string) error {
+ initCheck.Do(func() {
+ columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
+ userverifications.Table: userverifications.ValidColumn,
+ })
+ })
+ return columnCheck(t, c)
+}
+
+// Asc applies the given fields in ASC order.
+func Asc(fields ...string) func(*sql.Selector) {
+ return func(s *sql.Selector) {
+ for _, f := range fields {
+ if err := checkColumn(s.TableName(), f); err != nil {
+ s.AddError(&ValidationError{Name: f, err: fmt.Errorf("models: %w", err)})
+ }
+ s.OrderBy(sql.Asc(s.C(f)))
+ }
+ }
+}
+
+// Desc applies the given fields in DESC order.
+func Desc(fields ...string) func(*sql.Selector) {
+ return func(s *sql.Selector) {
+ for _, f := range fields {
+ if err := checkColumn(s.TableName(), f); err != nil {
+ s.AddError(&ValidationError{Name: f, err: fmt.Errorf("models: %w", err)})
+ }
+ s.OrderBy(sql.Desc(s.C(f)))
+ }
+ }
+}
+
+// AggregateFunc applies an aggregation step on the group-by traversal/selector.
+type AggregateFunc func(*sql.Selector) string
+
+// As is a pseudo aggregation function for renaming another other functions with custom names. For example:
+//
+// GroupBy(field1, field2).
+// Aggregate(models.As(models.Sum(field1), "sum_field1"), (models.As(models.Sum(field2), "sum_field2")).
+// Scan(ctx, &v)
+func As(fn AggregateFunc, end string) AggregateFunc {
+ return func(s *sql.Selector) string {
+ return sql.As(fn(s), end)
+ }
+}
+
+// Count applies the "count" aggregation function on each group.
+func Count() AggregateFunc {
+ return func(s *sql.Selector) string {
+ return sql.Count("*")
+ }
+}
+
+// Max applies the "max" aggregation function on the given field of each group.
+func Max(field string) AggregateFunc {
+ return func(s *sql.Selector) string {
+ if err := checkColumn(s.TableName(), field); err != nil {
+ s.AddError(&ValidationError{Name: field, err: fmt.Errorf("models: %w", err)})
+ return ""
+ }
+ return sql.Max(s.C(field))
+ }
+}
+
+// Mean applies the "mean" aggregation function on the given field of each group.
+func Mean(field string) AggregateFunc {
+ return func(s *sql.Selector) string {
+ if err := checkColumn(s.TableName(), field); err != nil {
+ s.AddError(&ValidationError{Name: field, err: fmt.Errorf("models: %w", err)})
+ return ""
+ }
+ return sql.Avg(s.C(field))
+ }
+}
+
+// Min applies the "min" aggregation function on the given field of each group.
+func Min(field string) AggregateFunc {
+ return func(s *sql.Selector) string {
+ if err := checkColumn(s.TableName(), field); err != nil {
+ s.AddError(&ValidationError{Name: field, err: fmt.Errorf("models: %w", err)})
+ return ""
+ }
+ return sql.Min(s.C(field))
+ }
+}
+
+// Sum applies the "sum" aggregation function on the given field of each group.
+func Sum(field string) AggregateFunc {
+ return func(s *sql.Selector) string {
+ if err := checkColumn(s.TableName(), field); err != nil {
+ s.AddError(&ValidationError{Name: field, err: fmt.Errorf("models: %w", err)})
+ return ""
+ }
+ return sql.Sum(s.C(field))
+ }
+}
+
+// ValidationError returns when validating a field or edge fails.
+type ValidationError struct {
+ Name string // Field or edge name.
+ err error
+}
+
+// Error implements the error interface.
+func (e *ValidationError) Error() string {
+ return e.err.Error()
+}
+
+// Unwrap implements the errors.Wrapper interface.
+func (e *ValidationError) Unwrap() error {
+ return e.err
+}
+
+// IsValidationError returns a boolean indicating whether the error is a validation error.
+func IsValidationError(err error) bool {
+ if err == nil {
+ return false
+ }
+ var e *ValidationError
+ return errors.As(err, &e)
+}
+
+// NotFoundError returns when trying to fetch a specific entity and it was not found in the database.
+type NotFoundError struct {
+ label string
+}
+
+// Error implements the error interface.
+func (e *NotFoundError) Error() string {
+ return "models: " + e.label + " not found"
+}
+
+// IsNotFound returns a boolean indicating whether the error is a not found error.
+func IsNotFound(err error) bool {
+ if err == nil {
+ return false
+ }
+ var e *NotFoundError
+ return errors.As(err, &e)
+}
+
+// MaskNotFound masks not found error.
+func MaskNotFound(err error) error {
+ if IsNotFound(err) {
+ return nil
+ }
+ return err
+}
+
+// NotSingularError returns when trying to fetch a singular entity and more then one was found in the database.
+type NotSingularError struct {
+ label string
+}
+
+// Error implements the error interface.
+func (e *NotSingularError) Error() string {
+ return "models: " + e.label + " not singular"
+}
+
+// IsNotSingular returns a boolean indicating whether the error is a not singular error.
+func IsNotSingular(err error) bool {
+ if err == nil {
+ return false
+ }
+ var e *NotSingularError
+ return errors.As(err, &e)
+}
+
+// NotLoadedError returns when trying to get a node that was not loaded by the query.
+type NotLoadedError struct {
+ edge string
+}
+
+// Error implements the error interface.
+func (e *NotLoadedError) Error() string {
+ return "models: " + e.edge + " edge was not loaded"
+}
+
+// IsNotLoaded returns a boolean indicating whether the error is a not loaded error.
+func IsNotLoaded(err error) bool {
+ if err == nil {
+ return false
+ }
+ var e *NotLoadedError
+ return errors.As(err, &e)
+}
+
+// ConstraintError returns when trying to create/update one or more entities and
+// one or more of their constraints failed. For example, violation of edge or
+// field uniqueness.
+type ConstraintError struct {
+ msg string
+ wrap error
+}
+
+// Error implements the error interface.
+func (e ConstraintError) Error() string {
+ return "models: constraint failed: " + e.msg
+}
+
+// Unwrap implements the errors.Wrapper interface.
+func (e *ConstraintError) Unwrap() error {
+ return e.wrap
+}
+
+// IsConstraintError returns a boolean indicating whether the error is a constraint failure.
+func IsConstraintError(err error) bool {
+ if err == nil {
+ return false
+ }
+ var e *ConstraintError
+ return errors.As(err, &e)
+}
+
+// selector embedded by the different Select/GroupBy builders.
+type selector struct {
+ label string
+ flds *[]string
+ fns []AggregateFunc
+ scan func(context.Context, any) error
+}
+
+// ScanX is like Scan, but panics if an error occurs.
+func (s *selector) ScanX(ctx context.Context, v any) {
+ if err := s.scan(ctx, v); err != nil {
+ panic(err)
+ }
+}
+
+// Strings returns list of strings from a selector. It is only allowed when selecting one field.
+func (s *selector) Strings(ctx context.Context) ([]string, error) {
+ if len(*s.flds) > 1 {
+ return nil, errors.New("models: Strings is not achievable when selecting more than 1 field")
+ }
+ var v []string
+ if err := s.scan(ctx, &v); err != nil {
+ return nil, err
+ }
+ return v, nil
+}
+
+// StringsX is like Strings, but panics if an error occurs.
+func (s *selector) StringsX(ctx context.Context) []string {
+ v, err := s.Strings(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// String returns a single string from a selector. It is only allowed when selecting one field.
+func (s *selector) String(ctx context.Context) (_ string, err error) {
+ var v []string
+ if v, err = s.Strings(ctx); err != nil {
+ return
+ }
+ switch len(v) {
+ case 1:
+ return v[0], nil
+ case 0:
+ err = &NotFoundError{s.label}
+ default:
+ err = fmt.Errorf("models: Strings returned %d results when one was expected", len(v))
+ }
+ return
+}
+
+// StringX is like String, but panics if an error occurs.
+func (s *selector) StringX(ctx context.Context) string {
+ v, err := s.String(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Ints returns list of ints from a selector. It is only allowed when selecting one field.
+func (s *selector) Ints(ctx context.Context) ([]int, error) {
+ if len(*s.flds) > 1 {
+ return nil, errors.New("models: Ints is not achievable when selecting more than 1 field")
+ }
+ var v []int
+ if err := s.scan(ctx, &v); err != nil {
+ return nil, err
+ }
+ return v, nil
+}
+
+// IntsX is like Ints, but panics if an error occurs.
+func (s *selector) IntsX(ctx context.Context) []int {
+ v, err := s.Ints(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Int returns a single int from a selector. It is only allowed when selecting one field.
+func (s *selector) Int(ctx context.Context) (_ int, err error) {
+ var v []int
+ if v, err = s.Ints(ctx); err != nil {
+ return
+ }
+ switch len(v) {
+ case 1:
+ return v[0], nil
+ case 0:
+ err = &NotFoundError{s.label}
+ default:
+ err = fmt.Errorf("models: Ints returned %d results when one was expected", len(v))
+ }
+ return
+}
+
+// IntX is like Int, but panics if an error occurs.
+func (s *selector) IntX(ctx context.Context) int {
+ v, err := s.Int(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Float64s returns list of float64s from a selector. It is only allowed when selecting one field.
+func (s *selector) Float64s(ctx context.Context) ([]float64, error) {
+ if len(*s.flds) > 1 {
+ return nil, errors.New("models: Float64s is not achievable when selecting more than 1 field")
+ }
+ var v []float64
+ if err := s.scan(ctx, &v); err != nil {
+ return nil, err
+ }
+ return v, nil
+}
+
+// Float64sX is like Float64s, but panics if an error occurs.
+func (s *selector) Float64sX(ctx context.Context) []float64 {
+ v, err := s.Float64s(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Float64 returns a single float64 from a selector. It is only allowed when selecting one field.
+func (s *selector) Float64(ctx context.Context) (_ float64, err error) {
+ var v []float64
+ if v, err = s.Float64s(ctx); err != nil {
+ return
+ }
+ switch len(v) {
+ case 1:
+ return v[0], nil
+ case 0:
+ err = &NotFoundError{s.label}
+ default:
+ err = fmt.Errorf("models: Float64s returned %d results when one was expected", len(v))
+ }
+ return
+}
+
+// Float64X is like Float64, but panics if an error occurs.
+func (s *selector) Float64X(ctx context.Context) float64 {
+ v, err := s.Float64(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Bools returns list of bools from a selector. It is only allowed when selecting one field.
+func (s *selector) Bools(ctx context.Context) ([]bool, error) {
+ if len(*s.flds) > 1 {
+ return nil, errors.New("models: Bools is not achievable when selecting more than 1 field")
+ }
+ var v []bool
+ if err := s.scan(ctx, &v); err != nil {
+ return nil, err
+ }
+ return v, nil
+}
+
+// BoolsX is like Bools, but panics if an error occurs.
+func (s *selector) BoolsX(ctx context.Context) []bool {
+ v, err := s.Bools(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Bool returns a single bool from a selector. It is only allowed when selecting one field.
+func (s *selector) Bool(ctx context.Context) (_ bool, err error) {
+ var v []bool
+ if v, err = s.Bools(ctx); err != nil {
+ return
+ }
+ switch len(v) {
+ case 1:
+ return v[0], nil
+ case 0:
+ err = &NotFoundError{s.label}
+ default:
+ err = fmt.Errorf("models: Bools returned %d results when one was expected", len(v))
+ }
+ return
+}
+
+// BoolX is like Bool, but panics if an error occurs.
+func (s *selector) BoolX(ctx context.Context) bool {
+ v, err := s.Bool(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// withHooks invokes the builder operation with the given hooks, if any.
+func withHooks[V Value, M any, PM interface {
+ *M
+ Mutation
+}](ctx context.Context, exec func(context.Context) (V, error), mutation PM, hooks []Hook) (value V, err error) {
+ if len(hooks) == 0 {
+ return exec(ctx)
+ }
+ var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+ mutationT, ok := any(m).(PM)
+ if !ok {
+ return nil, fmt.Errorf("unexpected mutation type %T", m)
+ }
+ // Set the mutation to the builder.
+ *mutation = *mutationT
+ return exec(ctx)
+ })
+ for i := len(hooks) - 1; i >= 0; i-- {
+ if hooks[i] == nil {
+ return value, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
+ }
+ mut = hooks[i](mut)
+ }
+ v, err := mut.Mutate(ctx, mutation)
+ if err != nil {
+ return value, err
+ }
+ nv, ok := v.(V)
+ if !ok {
+ return value, fmt.Errorf("unexpected node type %T returned from %T", v, mutation)
+ }
+ return nv, nil
+}
+
+// setContextOp returns a new context with the given QueryContext attached (including its op) in case it does not exist.
+func setContextOp(ctx context.Context, qc *QueryContext, op string) context.Context {
+ if ent.QueryFromContext(ctx) == nil {
+ qc.Op = op
+ ctx = ent.NewQueryContext(ctx, qc)
+ }
+ return ctx
+}
+
+func querierAll[V Value, Q interface {
+ sqlAll(context.Context, ...queryHook) (V, error)
+}]() Querier {
+ return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
+ query, ok := q.(Q)
+ if !ok {
+ return nil, fmt.Errorf("unexpected query type %T", q)
+ }
+ return query.sqlAll(ctx)
+ })
+}
+
+func querierCount[Q interface {
+ sqlCount(context.Context) (int, error)
+}]() Querier {
+ return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
+ query, ok := q.(Q)
+ if !ok {
+ return nil, fmt.Errorf("unexpected query type %T", q)
+ }
+ return query.sqlCount(ctx)
+ })
+}
+
+func withInterceptors[V Value](ctx context.Context, q Query, qr Querier, inters []Interceptor) (v V, err error) {
+ for i := len(inters) - 1; i >= 0; i-- {
+ qr = inters[i].Intercept(qr)
+ }
+ rv, err := qr.Query(ctx, q)
+ if err != nil {
+ return v, err
+ }
+ vt, ok := rv.(V)
+ if !ok {
+ return v, fmt.Errorf("unexpected type %T returned from %T. expected type: %T", vt, q, v)
+ }
+ return vt, nil
+}
+
+func scanWithInterceptors[Q1 ent.Query, Q2 interface {
+ sqlScan(context.Context, Q1, any) error
+}](ctx context.Context, rootQuery Q1, selectOrGroup Q2, inters []Interceptor, v any) error {
+ rv := reflect.ValueOf(v)
+ var qr Querier = QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
+ query, ok := q.(Q1)
+ if !ok {
+ return nil, fmt.Errorf("unexpected query type %T", q)
+ }
+ if err := selectOrGroup.sqlScan(ctx, query, v); err != nil {
+ return nil, err
+ }
+ if k := rv.Kind(); k == reflect.Pointer && rv.Elem().CanInterface() {
+ return rv.Elem().Interface(), nil
+ }
+ return v, nil
+ })
+ for i := len(inters) - 1; i >= 0; i-- {
+ qr = inters[i].Intercept(qr)
+ }
+ vv, err := qr.Query(ctx, rootQuery)
+ if err != nil {
+ return err
+ }
+ switch rv2 := reflect.ValueOf(vv); {
+ case rv.IsNil(), rv2.IsNil(), rv.Kind() != reflect.Pointer:
+ case rv.Type() == rv2.Type():
+ rv.Elem().Set(rv2.Elem())
+ case rv.Elem().Type() == rv2.Type():
+ rv.Elem().Set(rv2)
+ }
+ return nil
+}
+
+// queryHook describes an internal hook for the different sqlAll methods.
+type queryHook func(context.Context, *sqlgraph.QuerySpec)
diff --git a/app/user_verifications/rpc/internal/models/enttest/enttest.go b/app/user_verifications/rpc/internal/models/enttest/enttest.go
new file mode 100644
index 0000000..b594991
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/enttest/enttest.go
@@ -0,0 +1,85 @@
+// Code generated by ent, DO NOT EDIT.
+
+package enttest
+
+import (
+ "context"
+
+ "juwan-backend/app/user_verifications/rpc/internal/models"
+ // required by schema hooks.
+ _ "juwan-backend/app/user_verifications/rpc/internal/models/runtime"
+
+ "juwan-backend/app/user_verifications/rpc/internal/models/migrate"
+
+ "entgo.io/ent/dialect/sql/schema"
+)
+
+type (
+ // TestingT is the interface that is shared between
+ // testing.T and testing.B and used by enttest.
+ TestingT interface {
+ FailNow()
+ Error(...any)
+ }
+
+ // Option configures client creation.
+ Option func(*options)
+
+ options struct {
+ opts []models.Option
+ migrateOpts []schema.MigrateOption
+ }
+)
+
+// WithOptions forwards options to client creation.
+func WithOptions(opts ...models.Option) Option {
+ return func(o *options) {
+ o.opts = append(o.opts, opts...)
+ }
+}
+
+// WithMigrateOptions forwards options to auto migration.
+func WithMigrateOptions(opts ...schema.MigrateOption) Option {
+ return func(o *options) {
+ o.migrateOpts = append(o.migrateOpts, opts...)
+ }
+}
+
+func newOptions(opts []Option) *options {
+ o := &options{}
+ for _, opt := range opts {
+ opt(o)
+ }
+ return o
+}
+
+// Open calls models.Open and auto-run migration.
+func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *models.Client {
+ o := newOptions(opts)
+ c, err := models.Open(driverName, dataSourceName, o.opts...)
+ if err != nil {
+ t.Error(err)
+ t.FailNow()
+ }
+ migrateSchema(t, c, o)
+ return c
+}
+
+// NewClient calls models.NewClient and auto-run migration.
+func NewClient(t TestingT, opts ...Option) *models.Client {
+ o := newOptions(opts)
+ c := models.NewClient(o.opts...)
+ migrateSchema(t, c, o)
+ return c
+}
+func migrateSchema(t TestingT, c *models.Client, o *options) {
+ tables, err := schema.CopyTables(migrate.Tables)
+ if err != nil {
+ t.Error(err)
+ t.FailNow()
+ }
+ if err := migrate.Create(context.Background(), c.Schema, tables, o.migrateOpts...); err != nil {
+ t.Error(err)
+ t.FailNow()
+ }
+}
diff --git a/app/user_verifications/rpc/internal/models/hook/hook.go b/app/user_verifications/rpc/internal/models/hook/hook.go
new file mode 100644
index 0000000..b61436d
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/hook/hook.go
@@ -0,0 +1,198 @@
+// Code generated by ent, DO NOT EDIT.
+
+package hook
+
+import (
+ "context"
+ "fmt"
+ "juwan-backend/app/user_verifications/rpc/internal/models"
+)
+
+// The UserVerificationsFunc type is an adapter to allow the use of ordinary
+// function as UserVerifications mutator.
+type UserVerificationsFunc func(context.Context, *models.UserVerificationsMutation) (models.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f UserVerificationsFunc) Mutate(ctx context.Context, m models.Mutation) (models.Value, error) {
+ if mv, ok := m.(*models.UserVerificationsMutation); ok {
+ return f(ctx, mv)
+ }
+ return nil, fmt.Errorf("unexpected mutation type %T. expect *models.UserVerificationsMutation", m)
+}
+
+// Condition is a hook condition function.
+type Condition func(context.Context, models.Mutation) bool
+
+// And groups conditions with the AND operator.
+func And(first, second Condition, rest ...Condition) Condition {
+ return func(ctx context.Context, m models.Mutation) bool {
+ if !first(ctx, m) || !second(ctx, m) {
+ return false
+ }
+ for _, cond := range rest {
+ if !cond(ctx, m) {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+// Or groups conditions with the OR operator.
+func Or(first, second Condition, rest ...Condition) Condition {
+ return func(ctx context.Context, m models.Mutation) bool {
+ if first(ctx, m) || second(ctx, m) {
+ return true
+ }
+ for _, cond := range rest {
+ if cond(ctx, m) {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+// Not negates a given condition.
+func Not(cond Condition) Condition {
+ return func(ctx context.Context, m models.Mutation) bool {
+ return !cond(ctx, m)
+ }
+}
+
+// HasOp is a condition testing mutation operation.
+func HasOp(op models.Op) Condition {
+ return func(_ context.Context, m models.Mutation) bool {
+ return m.Op().Is(op)
+ }
+}
+
+// HasAddedFields is a condition validating `.AddedField` on fields.
+func HasAddedFields(field string, fields ...string) Condition {
+ return func(_ context.Context, m models.Mutation) bool {
+ if _, exists := m.AddedField(field); !exists {
+ return false
+ }
+ for _, field := range fields {
+ if _, exists := m.AddedField(field); !exists {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+// HasClearedFields is a condition validating `.FieldCleared` on fields.
+func HasClearedFields(field string, fields ...string) Condition {
+ return func(_ context.Context, m models.Mutation) bool {
+ if exists := m.FieldCleared(field); !exists {
+ return false
+ }
+ for _, field := range fields {
+ if exists := m.FieldCleared(field); !exists {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+// HasFields is a condition validating `.Field` on fields.
+func HasFields(field string, fields ...string) Condition {
+ return func(_ context.Context, m models.Mutation) bool {
+ if _, exists := m.Field(field); !exists {
+ return false
+ }
+ for _, field := range fields {
+ if _, exists := m.Field(field); !exists {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+// If executes the given hook under condition.
+//
+// hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...)))
+func If(hk models.Hook, cond Condition) models.Hook {
+ return func(next models.Mutator) models.Mutator {
+ return models.MutateFunc(func(ctx context.Context, m models.Mutation) (models.Value, error) {
+ if cond(ctx, m) {
+ return hk(next).Mutate(ctx, m)
+ }
+ return next.Mutate(ctx, m)
+ })
+ }
+}
+
+// On executes the given hook only for the given operation.
+//
+// hook.On(Log, models.Delete|models.Create)
+func On(hk models.Hook, op models.Op) models.Hook {
+ return If(hk, HasOp(op))
+}
+
+// Unless skips the given hook only for the given operation.
+//
+// hook.Unless(Log, models.Update|models.UpdateOne)
+func Unless(hk models.Hook, op models.Op) models.Hook {
+ return If(hk, Not(HasOp(op)))
+}
+
+// FixedError is a hook returning a fixed error.
+func FixedError(err error) models.Hook {
+ return func(models.Mutator) models.Mutator {
+ return models.MutateFunc(func(context.Context, models.Mutation) (models.Value, error) {
+ return nil, err
+ })
+ }
+}
+
+// Reject returns a hook that rejects all operations that match op.
+//
+// func (T) Hooks() []models.Hook {
+// return []models.Hook{
+// Reject(models.Delete|models.Update),
+// }
+// }
+func Reject(op models.Op) models.Hook {
+ hk := FixedError(fmt.Errorf("%s operation is not allowed", op))
+ return On(hk, op)
+}
+
+// Chain acts as a list of hooks and is effectively immutable.
+// Once created, it will always hold the same set of hooks in the same order.
+type Chain struct {
+ hooks []models.Hook
+}
+
+// NewChain creates a new chain of hooks.
+func NewChain(hooks ...models.Hook) Chain {
+ return Chain{append([]models.Hook(nil), hooks...)}
+}
+
+// Hook chains the list of hooks and returns the final hook.
+func (c Chain) Hook() models.Hook {
+ return func(mutator models.Mutator) models.Mutator {
+ for i := len(c.hooks) - 1; i >= 0; i-- {
+ mutator = c.hooks[i](mutator)
+ }
+ return mutator
+ }
+}
+
+// Append extends a chain, adding the specified hook
+// as the last ones in the mutation flow.
+func (c Chain) Append(hooks ...models.Hook) Chain {
+ newHooks := make([]models.Hook, 0, len(c.hooks)+len(hooks))
+ newHooks = append(newHooks, c.hooks...)
+ newHooks = append(newHooks, hooks...)
+ return Chain{newHooks}
+}
+
+// Extend extends a chain, adding the specified chain
+// as the last ones in the mutation flow.
+func (c Chain) Extend(chain Chain) Chain {
+ return c.Append(chain.hooks...)
+}
diff --git a/app/user_verifications/rpc/internal/models/migrate/migrate.go b/app/user_verifications/rpc/internal/models/migrate/migrate.go
new file mode 100644
index 0000000..1956a6b
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/migrate/migrate.go
@@ -0,0 +1,64 @@
+// Code generated by ent, DO NOT EDIT.
+
+package migrate
+
+import (
+ "context"
+ "fmt"
+ "io"
+
+ "entgo.io/ent/dialect"
+ "entgo.io/ent/dialect/sql/schema"
+)
+
+var (
+ // WithGlobalUniqueID sets the universal ids options to the migration.
+ // If this option is enabled, ent migration will allocate a 1<<32 range
+ // for the ids of each entity (table).
+ // Note that this option cannot be applied on tables that already exist.
+ WithGlobalUniqueID = schema.WithGlobalUniqueID
+ // WithDropColumn sets the drop column option to the migration.
+ // If this option is enabled, ent migration will drop old columns
+ // that were used for both fields and edges. This defaults to false.
+ WithDropColumn = schema.WithDropColumn
+ // WithDropIndex sets the drop index option to the migration.
+ // If this option is enabled, ent migration will drop old indexes
+ // that were defined in the schema. This defaults to false.
+ // Note that unique constraints are defined using `UNIQUE INDEX`,
+ // and therefore, it's recommended to enable this option to get more
+ // flexibility in the schema changes.
+ WithDropIndex = schema.WithDropIndex
+ // WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true.
+ WithForeignKeys = schema.WithForeignKeys
+)
+
+// Schema is the API for creating, migrating and dropping a schema.
+type Schema struct {
+ drv dialect.Driver
+}
+
+// NewSchema creates a new schema client.
+func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} }
+
+// Create creates all schema resources.
+func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error {
+ return Create(ctx, s, Tables, opts...)
+}
+
+// Create creates all table resources using the given schema driver.
+func Create(ctx context.Context, s *Schema, tables []*schema.Table, opts ...schema.MigrateOption) error {
+ migrate, err := schema.NewMigrate(s.drv, opts...)
+ if err != nil {
+ return fmt.Errorf("ent/migrate: %w", err)
+ }
+ return migrate.Create(ctx, tables...)
+}
+
+// WriteTo writes the schema changes to w instead of running them against the database.
+//
+// if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil {
+// log.Fatal(err)
+// }
+func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error {
+ return Create(ctx, &Schema{drv: &schema.WriteDriver{Writer: w, Driver: s.drv}}, Tables, opts...)
+}
diff --git a/app/user_verifications/rpc/internal/models/migrate/schema.go b/app/user_verifications/rpc/internal/models/migrate/schema.go
new file mode 100644
index 0000000..a460dbd
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/migrate/schema.go
@@ -0,0 +1,37 @@
+// Code generated by ent, DO NOT EDIT.
+
+package migrate
+
+import (
+ "entgo.io/ent/dialect/sql/schema"
+ "entgo.io/ent/schema/field"
+)
+
+var (
+ // UserVerificationsColumns holds the columns for the "user_verifications" table.
+ UserVerificationsColumns = []*schema.Column{
+ {Name: "id", Type: field.TypeInt64, Increment: true},
+ {Name: "user_id", Type: field.TypeInt64, Unique: true},
+ {Name: "role", Type: field.TypeString, Unique: true},
+ {Name: "status", Type: field.TypeString, Default: "pending"},
+ {Name: "materials", Type: field.TypeJSON},
+ {Name: "reject_reason", Type: field.TypeString, Default: ""},
+ {Name: "reviewed_by", Type: field.TypeInt64},
+ {Name: "reviewed_at", Type: field.TypeTime},
+ {Name: "created_at", Type: field.TypeTime},
+ {Name: "updated_at", Type: field.TypeTime},
+ }
+ // UserVerificationsTable holds the schema information for the "user_verifications" table.
+ UserVerificationsTable = &schema.Table{
+ Name: "user_verifications",
+ Columns: UserVerificationsColumns,
+ PrimaryKey: []*schema.Column{UserVerificationsColumns[0]},
+ }
+ // Tables holds all the tables in the schema.
+ Tables = []*schema.Table{
+ UserVerificationsTable,
+ }
+)
+
+func init() {
+}
diff --git a/app/user_verifications/rpc/internal/models/mutation.go b/app/user_verifications/rpc/internal/models/mutation.go
new file mode 100644
index 0000000..0a1d297
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/mutation.go
@@ -0,0 +1,862 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "juwan-backend/app/user_verifications/rpc/internal/models/predicate"
+ "juwan-backend/app/user_verifications/rpc/internal/models/schema"
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
+ "sync"
+ "time"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+)
+
+const (
+ // Operation types.
+ OpCreate = ent.OpCreate
+ OpDelete = ent.OpDelete
+ OpDeleteOne = ent.OpDeleteOne
+ OpUpdate = ent.OpUpdate
+ OpUpdateOne = ent.OpUpdateOne
+
+ // Node types.
+ TypeUserVerifications = "UserVerifications"
+)
+
+// UserVerificationsMutation represents an operation that mutates the UserVerifications nodes in the graph.
+type UserVerificationsMutation struct {
+ config
+ op Op
+ typ string
+ id *int64
+ user_id *int64
+ adduser_id *int64
+ role *string
+ status *string
+ materials *schema.MaterialStruct
+ reject_reason *string
+ reviewed_by *int64
+ addreviewed_by *int64
+ reviewed_at *time.Time
+ created_at *time.Time
+ updated_at *time.Time
+ clearedFields map[string]struct{}
+ done bool
+ oldValue func(context.Context) (*UserVerifications, error)
+ predicates []predicate.UserVerifications
+}
+
+var _ ent.Mutation = (*UserVerificationsMutation)(nil)
+
+// userverificationsOption allows management of the mutation configuration using functional options.
+type userverificationsOption func(*UserVerificationsMutation)
+
+// newUserVerificationsMutation creates new mutation for the UserVerifications entity.
+func newUserVerificationsMutation(c config, op Op, opts ...userverificationsOption) *UserVerificationsMutation {
+ m := &UserVerificationsMutation{
+ config: c,
+ op: op,
+ typ: TypeUserVerifications,
+ clearedFields: make(map[string]struct{}),
+ }
+ for _, opt := range opts {
+ opt(m)
+ }
+ return m
+}
+
+// withUserVerificationsID sets the ID field of the mutation.
+func withUserVerificationsID(id int64) userverificationsOption {
+ return func(m *UserVerificationsMutation) {
+ var (
+ err error
+ once sync.Once
+ value *UserVerifications
+ )
+ m.oldValue = func(ctx context.Context) (*UserVerifications, error) {
+ once.Do(func() {
+ if m.done {
+ err = errors.New("querying old values post mutation is not allowed")
+ } else {
+ value, err = m.Client().UserVerifications.Get(ctx, id)
+ }
+ })
+ return value, err
+ }
+ m.id = &id
+ }
+}
+
+// withUserVerifications sets the old UserVerifications of the mutation.
+func withUserVerifications(node *UserVerifications) userverificationsOption {
+ return func(m *UserVerificationsMutation) {
+ m.oldValue = func(context.Context) (*UserVerifications, error) {
+ return node, nil
+ }
+ m.id = &node.ID
+ }
+}
+
+// Client returns a new `ent.Client` from the mutation. If the mutation was
+// executed in a transaction (ent.Tx), a transactional client is returned.
+func (m UserVerificationsMutation) Client() *Client {
+ client := &Client{config: m.config}
+ client.init()
+ return client
+}
+
+// Tx returns an `ent.Tx` for mutations that were executed in transactions;
+// it returns an error otherwise.
+func (m UserVerificationsMutation) Tx() (*Tx, error) {
+ if _, ok := m.driver.(*txDriver); !ok {
+ return nil, errors.New("models: mutation is not running in a transaction")
+ }
+ tx := &Tx{config: m.config}
+ tx.init()
+ return tx, nil
+}
+
+// SetID sets the value of the id field. Note that this
+// operation is only accepted on creation of UserVerifications entities.
+func (m *UserVerificationsMutation) SetID(id int64) {
+ m.id = &id
+}
+
+// ID returns the ID value in the mutation. Note that the ID is only available
+// if it was provided to the builder or after it was returned from the database.
+func (m *UserVerificationsMutation) ID() (id int64, exists bool) {
+ if m.id == nil {
+ return
+ }
+ return *m.id, true
+}
+
+// IDs queries the database and returns the entity ids that match the mutation's predicate.
+// That means, if the mutation is applied within a transaction with an isolation level such
+// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
+// or updated by the mutation.
+func (m *UserVerificationsMutation) IDs(ctx context.Context) ([]int64, error) {
+ switch {
+ case m.op.Is(OpUpdateOne | OpDeleteOne):
+ id, exists := m.ID()
+ if exists {
+ return []int64{id}, nil
+ }
+ fallthrough
+ case m.op.Is(OpUpdate | OpDelete):
+ return m.Client().UserVerifications.Query().Where(m.predicates...).IDs(ctx)
+ default:
+ return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
+ }
+}
+
+// SetUserID sets the "user_id" field.
+func (m *UserVerificationsMutation) SetUserID(i int64) {
+ m.user_id = &i
+ m.adduser_id = nil
+}
+
+// UserID returns the value of the "user_id" field in the mutation.
+func (m *UserVerificationsMutation) UserID() (r int64, exists bool) {
+ v := m.user_id
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldUserID returns the old "user_id" field's value of the UserVerifications entity.
+// If the UserVerifications object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserVerificationsMutation) OldUserID(ctx context.Context) (v int64, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldUserID is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldUserID requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldUserID: %w", err)
+ }
+ return oldValue.UserID, nil
+}
+
+// AddUserID adds i to the "user_id" field.
+func (m *UserVerificationsMutation) AddUserID(i int64) {
+ if m.adduser_id != nil {
+ *m.adduser_id += i
+ } else {
+ m.adduser_id = &i
+ }
+}
+
+// AddedUserID returns the value that was added to the "user_id" field in this mutation.
+func (m *UserVerificationsMutation) AddedUserID() (r int64, exists bool) {
+ v := m.adduser_id
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// ResetUserID resets all changes to the "user_id" field.
+func (m *UserVerificationsMutation) ResetUserID() {
+ m.user_id = nil
+ m.adduser_id = nil
+}
+
+// SetRole sets the "role" field.
+func (m *UserVerificationsMutation) SetRole(s string) {
+ m.role = &s
+}
+
+// Role returns the value of the "role" field in the mutation.
+func (m *UserVerificationsMutation) Role() (r string, exists bool) {
+ v := m.role
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldRole returns the old "role" field's value of the UserVerifications entity.
+// If the UserVerifications object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserVerificationsMutation) OldRole(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldRole is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldRole requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldRole: %w", err)
+ }
+ return oldValue.Role, nil
+}
+
+// ResetRole resets all changes to the "role" field.
+func (m *UserVerificationsMutation) ResetRole() {
+ m.role = nil
+}
+
+// SetStatus sets the "status" field.
+func (m *UserVerificationsMutation) SetStatus(s string) {
+ m.status = &s
+}
+
+// Status returns the value of the "status" field in the mutation.
+func (m *UserVerificationsMutation) Status() (r string, exists bool) {
+ v := m.status
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldStatus returns the old "status" field's value of the UserVerifications entity.
+// If the UserVerifications object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserVerificationsMutation) OldStatus(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldStatus is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldStatus requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldStatus: %w", err)
+ }
+ return oldValue.Status, nil
+}
+
+// ResetStatus resets all changes to the "status" field.
+func (m *UserVerificationsMutation) ResetStatus() {
+ m.status = nil
+}
+
+// SetMaterials sets the "materials" field.
+func (m *UserVerificationsMutation) SetMaterials(ss schema.MaterialStruct) {
+ m.materials = &ss
+}
+
+// Materials returns the value of the "materials" field in the mutation.
+func (m *UserVerificationsMutation) Materials() (r schema.MaterialStruct, exists bool) {
+ v := m.materials
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldMaterials returns the old "materials" field's value of the UserVerifications entity.
+// If the UserVerifications object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserVerificationsMutation) OldMaterials(ctx context.Context) (v schema.MaterialStruct, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldMaterials is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldMaterials requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldMaterials: %w", err)
+ }
+ return oldValue.Materials, nil
+}
+
+// ResetMaterials resets all changes to the "materials" field.
+func (m *UserVerificationsMutation) ResetMaterials() {
+ m.materials = nil
+}
+
+// SetRejectReason sets the "reject_reason" field.
+func (m *UserVerificationsMutation) SetRejectReason(s string) {
+ m.reject_reason = &s
+}
+
+// RejectReason returns the value of the "reject_reason" field in the mutation.
+func (m *UserVerificationsMutation) RejectReason() (r string, exists bool) {
+ v := m.reject_reason
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldRejectReason returns the old "reject_reason" field's value of the UserVerifications entity.
+// If the UserVerifications object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserVerificationsMutation) OldRejectReason(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldRejectReason is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldRejectReason requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldRejectReason: %w", err)
+ }
+ return oldValue.RejectReason, nil
+}
+
+// ResetRejectReason resets all changes to the "reject_reason" field.
+func (m *UserVerificationsMutation) ResetRejectReason() {
+ m.reject_reason = nil
+}
+
+// SetReviewedBy sets the "reviewed_by" field.
+func (m *UserVerificationsMutation) SetReviewedBy(i int64) {
+ m.reviewed_by = &i
+ m.addreviewed_by = nil
+}
+
+// ReviewedBy returns the value of the "reviewed_by" field in the mutation.
+func (m *UserVerificationsMutation) ReviewedBy() (r int64, exists bool) {
+ v := m.reviewed_by
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldReviewedBy returns the old "reviewed_by" field's value of the UserVerifications entity.
+// If the UserVerifications object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserVerificationsMutation) OldReviewedBy(ctx context.Context) (v int64, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldReviewedBy is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldReviewedBy requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldReviewedBy: %w", err)
+ }
+ return oldValue.ReviewedBy, nil
+}
+
+// AddReviewedBy adds i to the "reviewed_by" field.
+func (m *UserVerificationsMutation) AddReviewedBy(i int64) {
+ if m.addreviewed_by != nil {
+ *m.addreviewed_by += i
+ } else {
+ m.addreviewed_by = &i
+ }
+}
+
+// AddedReviewedBy returns the value that was added to the "reviewed_by" field in this mutation.
+func (m *UserVerificationsMutation) AddedReviewedBy() (r int64, exists bool) {
+ v := m.addreviewed_by
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// ResetReviewedBy resets all changes to the "reviewed_by" field.
+func (m *UserVerificationsMutation) ResetReviewedBy() {
+ m.reviewed_by = nil
+ m.addreviewed_by = nil
+}
+
+// SetReviewedAt sets the "reviewed_at" field.
+func (m *UserVerificationsMutation) SetReviewedAt(t time.Time) {
+ m.reviewed_at = &t
+}
+
+// ReviewedAt returns the value of the "reviewed_at" field in the mutation.
+func (m *UserVerificationsMutation) ReviewedAt() (r time.Time, exists bool) {
+ v := m.reviewed_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldReviewedAt returns the old "reviewed_at" field's value of the UserVerifications entity.
+// If the UserVerifications object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserVerificationsMutation) OldReviewedAt(ctx context.Context) (v time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldReviewedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldReviewedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldReviewedAt: %w", err)
+ }
+ return oldValue.ReviewedAt, nil
+}
+
+// ResetReviewedAt resets all changes to the "reviewed_at" field.
+func (m *UserVerificationsMutation) ResetReviewedAt() {
+ m.reviewed_at = nil
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (m *UserVerificationsMutation) SetCreatedAt(t time.Time) {
+ m.created_at = &t
+}
+
+// CreatedAt returns the value of the "created_at" field in the mutation.
+func (m *UserVerificationsMutation) CreatedAt() (r time.Time, exists bool) {
+ v := m.created_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldCreatedAt returns the old "created_at" field's value of the UserVerifications entity.
+// If the UserVerifications object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserVerificationsMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldCreatedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err)
+ }
+ return oldValue.CreatedAt, nil
+}
+
+// ResetCreatedAt resets all changes to the "created_at" field.
+func (m *UserVerificationsMutation) ResetCreatedAt() {
+ m.created_at = nil
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (m *UserVerificationsMutation) SetUpdatedAt(t time.Time) {
+ m.updated_at = &t
+}
+
+// UpdatedAt returns the value of the "updated_at" field in the mutation.
+func (m *UserVerificationsMutation) UpdatedAt() (r time.Time, exists bool) {
+ v := m.updated_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldUpdatedAt returns the old "updated_at" field's value of the UserVerifications entity.
+// If the UserVerifications object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserVerificationsMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldUpdatedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err)
+ }
+ return oldValue.UpdatedAt, nil
+}
+
+// ResetUpdatedAt resets all changes to the "updated_at" field.
+func (m *UserVerificationsMutation) ResetUpdatedAt() {
+ m.updated_at = nil
+}
+
+// Where appends a list predicates to the UserVerificationsMutation builder.
+func (m *UserVerificationsMutation) Where(ps ...predicate.UserVerifications) {
+ m.predicates = append(m.predicates, ps...)
+}
+
+// WhereP appends storage-level predicates to the UserVerificationsMutation builder. Using this method,
+// users can use type-assertion to append predicates that do not depend on any generated package.
+func (m *UserVerificationsMutation) WhereP(ps ...func(*sql.Selector)) {
+ p := make([]predicate.UserVerifications, len(ps))
+ for i := range ps {
+ p[i] = ps[i]
+ }
+ m.Where(p...)
+}
+
+// Op returns the operation name.
+func (m *UserVerificationsMutation) Op() Op {
+ return m.op
+}
+
+// SetOp allows setting the mutation operation.
+func (m *UserVerificationsMutation) SetOp(op Op) {
+ m.op = op
+}
+
+// Type returns the node type of this mutation (UserVerifications).
+func (m *UserVerificationsMutation) Type() string {
+ return m.typ
+}
+
+// Fields returns all fields that were changed during this mutation. Note that in
+// order to get all numeric fields that were incremented/decremented, call
+// AddedFields().
+func (m *UserVerificationsMutation) Fields() []string {
+ fields := make([]string, 0, 9)
+ if m.user_id != nil {
+ fields = append(fields, userverifications.FieldUserID)
+ }
+ if m.role != nil {
+ fields = append(fields, userverifications.FieldRole)
+ }
+ if m.status != nil {
+ fields = append(fields, userverifications.FieldStatus)
+ }
+ if m.materials != nil {
+ fields = append(fields, userverifications.FieldMaterials)
+ }
+ if m.reject_reason != nil {
+ fields = append(fields, userverifications.FieldRejectReason)
+ }
+ if m.reviewed_by != nil {
+ fields = append(fields, userverifications.FieldReviewedBy)
+ }
+ if m.reviewed_at != nil {
+ fields = append(fields, userverifications.FieldReviewedAt)
+ }
+ if m.created_at != nil {
+ fields = append(fields, userverifications.FieldCreatedAt)
+ }
+ if m.updated_at != nil {
+ fields = append(fields, userverifications.FieldUpdatedAt)
+ }
+ return fields
+}
+
+// Field returns the value of a field with the given name. The second boolean
+// return value indicates that this field was not set, or was not defined in the
+// schema.
+func (m *UserVerificationsMutation) Field(name string) (ent.Value, bool) {
+ switch name {
+ case userverifications.FieldUserID:
+ return m.UserID()
+ case userverifications.FieldRole:
+ return m.Role()
+ case userverifications.FieldStatus:
+ return m.Status()
+ case userverifications.FieldMaterials:
+ return m.Materials()
+ case userverifications.FieldRejectReason:
+ return m.RejectReason()
+ case userverifications.FieldReviewedBy:
+ return m.ReviewedBy()
+ case userverifications.FieldReviewedAt:
+ return m.ReviewedAt()
+ case userverifications.FieldCreatedAt:
+ return m.CreatedAt()
+ case userverifications.FieldUpdatedAt:
+ return m.UpdatedAt()
+ }
+ return nil, false
+}
+
+// OldField returns the old value of the field from the database. An error is
+// returned if the mutation operation is not UpdateOne, or the query to the
+// database failed.
+func (m *UserVerificationsMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
+ switch name {
+ case userverifications.FieldUserID:
+ return m.OldUserID(ctx)
+ case userverifications.FieldRole:
+ return m.OldRole(ctx)
+ case userverifications.FieldStatus:
+ return m.OldStatus(ctx)
+ case userverifications.FieldMaterials:
+ return m.OldMaterials(ctx)
+ case userverifications.FieldRejectReason:
+ return m.OldRejectReason(ctx)
+ case userverifications.FieldReviewedBy:
+ return m.OldReviewedBy(ctx)
+ case userverifications.FieldReviewedAt:
+ return m.OldReviewedAt(ctx)
+ case userverifications.FieldCreatedAt:
+ return m.OldCreatedAt(ctx)
+ case userverifications.FieldUpdatedAt:
+ return m.OldUpdatedAt(ctx)
+ }
+ return nil, fmt.Errorf("unknown UserVerifications field %s", name)
+}
+
+// SetField sets the value of a field with the given name. It returns an error if
+// the field is not defined in the schema, or if the type mismatched the field
+// type.
+func (m *UserVerificationsMutation) SetField(name string, value ent.Value) error {
+ switch name {
+ case userverifications.FieldUserID:
+ v, ok := value.(int64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetUserID(v)
+ return nil
+ case userverifications.FieldRole:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetRole(v)
+ return nil
+ case userverifications.FieldStatus:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetStatus(v)
+ return nil
+ case userverifications.FieldMaterials:
+ v, ok := value.(schema.MaterialStruct)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetMaterials(v)
+ return nil
+ case userverifications.FieldRejectReason:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetRejectReason(v)
+ return nil
+ case userverifications.FieldReviewedBy:
+ v, ok := value.(int64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetReviewedBy(v)
+ return nil
+ case userverifications.FieldReviewedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetReviewedAt(v)
+ return nil
+ case userverifications.FieldCreatedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetCreatedAt(v)
+ return nil
+ case userverifications.FieldUpdatedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetUpdatedAt(v)
+ return nil
+ }
+ return fmt.Errorf("unknown UserVerifications field %s", name)
+}
+
+// AddedFields returns all numeric fields that were incremented/decremented during
+// this mutation.
+func (m *UserVerificationsMutation) AddedFields() []string {
+ var fields []string
+ if m.adduser_id != nil {
+ fields = append(fields, userverifications.FieldUserID)
+ }
+ if m.addreviewed_by != nil {
+ fields = append(fields, userverifications.FieldReviewedBy)
+ }
+ return fields
+}
+
+// AddedField returns the numeric value that was incremented/decremented on a field
+// with the given name. The second boolean return value indicates that this field
+// was not set, or was not defined in the schema.
+func (m *UserVerificationsMutation) AddedField(name string) (ent.Value, bool) {
+ switch name {
+ case userverifications.FieldUserID:
+ return m.AddedUserID()
+ case userverifications.FieldReviewedBy:
+ return m.AddedReviewedBy()
+ }
+ return nil, false
+}
+
+// AddField adds the value to the field with the given name. It returns an error if
+// the field is not defined in the schema, or if the type mismatched the field
+// type.
+func (m *UserVerificationsMutation) AddField(name string, value ent.Value) error {
+ switch name {
+ case userverifications.FieldUserID:
+ v, ok := value.(int64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.AddUserID(v)
+ return nil
+ case userverifications.FieldReviewedBy:
+ v, ok := value.(int64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.AddReviewedBy(v)
+ return nil
+ }
+ return fmt.Errorf("unknown UserVerifications numeric field %s", name)
+}
+
+// ClearedFields returns all nullable fields that were cleared during this
+// mutation.
+func (m *UserVerificationsMutation) ClearedFields() []string {
+ return nil
+}
+
+// FieldCleared returns a boolean indicating if a field with the given name was
+// cleared in this mutation.
+func (m *UserVerificationsMutation) FieldCleared(name string) bool {
+ _, ok := m.clearedFields[name]
+ return ok
+}
+
+// ClearField clears the value of the field with the given name. It returns an
+// error if the field is not defined in the schema.
+func (m *UserVerificationsMutation) ClearField(name string) error {
+ return fmt.Errorf("unknown UserVerifications nullable field %s", name)
+}
+
+// ResetField resets all changes in the mutation for the field with the given name.
+// It returns an error if the field is not defined in the schema.
+func (m *UserVerificationsMutation) ResetField(name string) error {
+ switch name {
+ case userverifications.FieldUserID:
+ m.ResetUserID()
+ return nil
+ case userverifications.FieldRole:
+ m.ResetRole()
+ return nil
+ case userverifications.FieldStatus:
+ m.ResetStatus()
+ return nil
+ case userverifications.FieldMaterials:
+ m.ResetMaterials()
+ return nil
+ case userverifications.FieldRejectReason:
+ m.ResetRejectReason()
+ return nil
+ case userverifications.FieldReviewedBy:
+ m.ResetReviewedBy()
+ return nil
+ case userverifications.FieldReviewedAt:
+ m.ResetReviewedAt()
+ return nil
+ case userverifications.FieldCreatedAt:
+ m.ResetCreatedAt()
+ return nil
+ case userverifications.FieldUpdatedAt:
+ m.ResetUpdatedAt()
+ return nil
+ }
+ return fmt.Errorf("unknown UserVerifications field %s", name)
+}
+
+// AddedEdges returns all edge names that were set/added in this mutation.
+func (m *UserVerificationsMutation) AddedEdges() []string {
+ edges := make([]string, 0, 0)
+ return edges
+}
+
+// AddedIDs returns all IDs (to other nodes) that were added for the given edge
+// name in this mutation.
+func (m *UserVerificationsMutation) AddedIDs(name string) []ent.Value {
+ return nil
+}
+
+// RemovedEdges returns all edge names that were removed in this mutation.
+func (m *UserVerificationsMutation) RemovedEdges() []string {
+ edges := make([]string, 0, 0)
+ return edges
+}
+
+// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
+// the given name in this mutation.
+func (m *UserVerificationsMutation) RemovedIDs(name string) []ent.Value {
+ return nil
+}
+
+// ClearedEdges returns all edge names that were cleared in this mutation.
+func (m *UserVerificationsMutation) ClearedEdges() []string {
+ edges := make([]string, 0, 0)
+ return edges
+}
+
+// EdgeCleared returns a boolean which indicates if the edge with the given name
+// was cleared in this mutation.
+func (m *UserVerificationsMutation) EdgeCleared(name string) bool {
+ return false
+}
+
+// ClearEdge clears the value of the edge with the given name. It returns an error
+// if that edge is not defined in the schema.
+func (m *UserVerificationsMutation) ClearEdge(name string) error {
+ return fmt.Errorf("unknown UserVerifications unique edge %s", name)
+}
+
+// ResetEdge resets all changes to the edge with the given name in this mutation.
+// It returns an error if the edge is not defined in the schema.
+func (m *UserVerificationsMutation) ResetEdge(name string) error {
+ return fmt.Errorf("unknown UserVerifications edge %s", name)
+}
diff --git a/app/user_verifications/rpc/internal/models/predicate/predicate.go b/app/user_verifications/rpc/internal/models/predicate/predicate.go
new file mode 100644
index 0000000..ac6f6b8
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/predicate/predicate.go
@@ -0,0 +1,10 @@
+// Code generated by ent, DO NOT EDIT.
+
+package predicate
+
+import (
+ "entgo.io/ent/dialect/sql"
+)
+
+// UserVerifications is the predicate function for userverifications builders.
+type UserVerifications func(*sql.Selector)
diff --git a/app/user_verifications/rpc/internal/models/runtime.go b/app/user_verifications/rpc/internal/models/runtime.go
new file mode 100644
index 0000000..cec2fc2
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/runtime.go
@@ -0,0 +1,24 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "juwan-backend/app/user_verifications/rpc/internal/models/schema"
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
+)
+
+// The init function reads all schema descriptors with runtime code
+// (default values, validators, hooks and policies) and stitches it
+// to their package variables.
+func init() {
+ userverificationsFields := schema.UserVerifications{}.Fields()
+ _ = userverificationsFields
+ // userverificationsDescStatus is the schema descriptor for status field.
+ userverificationsDescStatus := userverificationsFields[3].Descriptor()
+ // userverifications.DefaultStatus holds the default value on creation for the status field.
+ userverifications.DefaultStatus = userverificationsDescStatus.Default.(string)
+ // userverificationsDescRejectReason is the schema descriptor for reject_reason field.
+ userverificationsDescRejectReason := userverificationsFields[5].Descriptor()
+ // userverifications.DefaultRejectReason holds the default value on creation for the reject_reason field.
+ userverifications.DefaultRejectReason = userverificationsDescRejectReason.Default.(string)
+}
diff --git a/app/user_verifications/rpc/internal/models/runtime/runtime.go b/app/user_verifications/rpc/internal/models/runtime/runtime.go
new file mode 100644
index 0000000..e9dda7a
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/runtime/runtime.go
@@ -0,0 +1,10 @@
+// Code generated by ent, DO NOT EDIT.
+
+package runtime
+
+// The schema-stitching logic is generated in juwan-backend/app/user_verifications/rpc/internal/models/runtime.go
+
+const (
+ Version = "v0.14.5" // Version of ent codegen.
+ Sum = "h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=" // Sum of ent codegen.
+)
diff --git a/app/user_verifications/rpc/internal/models/schema/userverifications.go b/app/user_verifications/rpc/internal/models/schema/userverifications.go
new file mode 100644
index 0000000..a429696
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/schema/userverifications.go
@@ -0,0 +1,39 @@
+package schema
+
+import (
+ "entgo.io/ent"
+ "entgo.io/ent/schema/field"
+)
+
+// UserVerifications holds the schema definition for the UserVerifications entity.
+type UserVerifications struct {
+ ent.Schema
+}
+
+type MaterialStruct struct {
+ IdCardFront string `json:"idCardFront"`
+ IdCardBack string `json:"idCardBack"`
+ GameScreenshots []string `json:"gameScreenshots"`
+ VoiceDemo string `json:"voiceDemo"`
+}
+
+// Fields of the UserVerifications.
+func (UserVerifications) Fields() []ent.Field {
+ return []ent.Field{
+ field.Int64("id").Immutable().Unique(),
+ field.Int64("user_id").Immutable().Unique(),
+ field.String("role").Unique(),
+ field.String("status").Default("pending"),
+ field.JSON("materials", MaterialStruct{}),
+ field.String("reject_reason").Default(""),
+ field.Int64("reviewed_by"),
+ field.Time("reviewed_at").Immutable(),
+ field.Time("created_at").Immutable(),
+ field.Time("updated_at").Immutable(),
+ }
+}
+
+// Edges of the UserVerifications.
+func (UserVerifications) Edges() []ent.Edge {
+ return nil
+}
diff --git a/app/user_verifications/rpc/internal/models/tx.go b/app/user_verifications/rpc/internal/models/tx.go
new file mode 100644
index 0000000..8b33a63
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/tx.go
@@ -0,0 +1,210 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "context"
+ "sync"
+
+ "entgo.io/ent/dialect"
+)
+
+// Tx is a transactional client that is created by calling Client.Tx().
+type Tx struct {
+ config
+ // UserVerifications is the client for interacting with the UserVerifications builders.
+ UserVerifications *UserVerificationsClient
+
+ // lazily loaded.
+ client *Client
+ clientOnce sync.Once
+ // ctx lives for the life of the transaction. It is
+ // the same context used by the underlying connection.
+ ctx context.Context
+}
+
+type (
+ // Committer is the interface that wraps the Commit method.
+ Committer interface {
+ Commit(context.Context, *Tx) error
+ }
+
+ // The CommitFunc type is an adapter to allow the use of ordinary
+ // function as a Committer. If f is a function with the appropriate
+ // signature, CommitFunc(f) is a Committer that calls f.
+ CommitFunc func(context.Context, *Tx) error
+
+ // CommitHook defines the "commit middleware". A function that gets a Committer
+ // and returns a Committer. For example:
+ //
+ // hook := func(next ent.Committer) ent.Committer {
+ // return ent.CommitFunc(func(ctx context.Context, tx *ent.Tx) error {
+ // // Do some stuff before.
+ // if err := next.Commit(ctx, tx); err != nil {
+ // return err
+ // }
+ // // Do some stuff after.
+ // return nil
+ // })
+ // }
+ //
+ CommitHook func(Committer) Committer
+)
+
+// Commit calls f(ctx, m).
+func (f CommitFunc) Commit(ctx context.Context, tx *Tx) error {
+ return f(ctx, tx)
+}
+
+// Commit commits the transaction.
+func (tx *Tx) Commit() error {
+ txDriver := tx.config.driver.(*txDriver)
+ var fn Committer = CommitFunc(func(context.Context, *Tx) error {
+ return txDriver.tx.Commit()
+ })
+ txDriver.mu.Lock()
+ hooks := append([]CommitHook(nil), txDriver.onCommit...)
+ txDriver.mu.Unlock()
+ for i := len(hooks) - 1; i >= 0; i-- {
+ fn = hooks[i](fn)
+ }
+ return fn.Commit(tx.ctx, tx)
+}
+
+// OnCommit adds a hook to call on commit.
+func (tx *Tx) OnCommit(f CommitHook) {
+ txDriver := tx.config.driver.(*txDriver)
+ txDriver.mu.Lock()
+ txDriver.onCommit = append(txDriver.onCommit, f)
+ txDriver.mu.Unlock()
+}
+
+type (
+ // Rollbacker is the interface that wraps the Rollback method.
+ Rollbacker interface {
+ Rollback(context.Context, *Tx) error
+ }
+
+ // The RollbackFunc type is an adapter to allow the use of ordinary
+ // function as a Rollbacker. If f is a function with the appropriate
+ // signature, RollbackFunc(f) is a Rollbacker that calls f.
+ RollbackFunc func(context.Context, *Tx) error
+
+ // RollbackHook defines the "rollback middleware". A function that gets a Rollbacker
+ // and returns a Rollbacker. For example:
+ //
+ // hook := func(next ent.Rollbacker) ent.Rollbacker {
+ // return ent.RollbackFunc(func(ctx context.Context, tx *ent.Tx) error {
+ // // Do some stuff before.
+ // if err := next.Rollback(ctx, tx); err != nil {
+ // return err
+ // }
+ // // Do some stuff after.
+ // return nil
+ // })
+ // }
+ //
+ RollbackHook func(Rollbacker) Rollbacker
+)
+
+// Rollback calls f(ctx, m).
+func (f RollbackFunc) Rollback(ctx context.Context, tx *Tx) error {
+ return f(ctx, tx)
+}
+
+// Rollback rollbacks the transaction.
+func (tx *Tx) Rollback() error {
+ txDriver := tx.config.driver.(*txDriver)
+ var fn Rollbacker = RollbackFunc(func(context.Context, *Tx) error {
+ return txDriver.tx.Rollback()
+ })
+ txDriver.mu.Lock()
+ hooks := append([]RollbackHook(nil), txDriver.onRollback...)
+ txDriver.mu.Unlock()
+ for i := len(hooks) - 1; i >= 0; i-- {
+ fn = hooks[i](fn)
+ }
+ return fn.Rollback(tx.ctx, tx)
+}
+
+// OnRollback adds a hook to call on rollback.
+func (tx *Tx) OnRollback(f RollbackHook) {
+ txDriver := tx.config.driver.(*txDriver)
+ txDriver.mu.Lock()
+ txDriver.onRollback = append(txDriver.onRollback, f)
+ txDriver.mu.Unlock()
+}
+
+// Client returns a Client that binds to current transaction.
+func (tx *Tx) Client() *Client {
+ tx.clientOnce.Do(func() {
+ tx.client = &Client{config: tx.config}
+ tx.client.init()
+ })
+ return tx.client
+}
+
+func (tx *Tx) init() {
+ tx.UserVerifications = NewUserVerificationsClient(tx.config)
+}
+
+// txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation.
+// The idea is to support transactions without adding any extra code to the builders.
+// When a builder calls to driver.Tx(), it gets the same dialect.Tx instance.
+// Commit and Rollback are nop for the internal builders and the user must call one
+// of them in order to commit or rollback the transaction.
+//
+// If a closed transaction is embedded in one of the generated entities, and the entity
+// applies a query, for example: UserVerifications.QueryXXX(), the query will be executed
+// through the driver which created this transaction.
+//
+// Note that txDriver is not goroutine safe.
+type txDriver struct {
+ // the driver we started the transaction from.
+ drv dialect.Driver
+ // tx is the underlying transaction.
+ tx dialect.Tx
+ // completion hooks.
+ mu sync.Mutex
+ onCommit []CommitHook
+ onRollback []RollbackHook
+}
+
+// newTx creates a new transactional driver.
+func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) {
+ tx, err := drv.Tx(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return &txDriver{tx: tx, drv: drv}, nil
+}
+
+// Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls
+// from the internal builders. Should be called only by the internal builders.
+func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil }
+
+// Dialect returns the dialect of the driver we started the transaction from.
+func (tx *txDriver) Dialect() string { return tx.drv.Dialect() }
+
+// Close is a nop close.
+func (*txDriver) Close() error { return nil }
+
+// Commit is a nop commit for the internal builders.
+// User must call `Tx.Commit` in order to commit the transaction.
+func (*txDriver) Commit() error { return nil }
+
+// Rollback is a nop rollback for the internal builders.
+// User must call `Tx.Rollback` in order to rollback the transaction.
+func (*txDriver) Rollback() error { return nil }
+
+// Exec calls tx.Exec.
+func (tx *txDriver) Exec(ctx context.Context, query string, args, v any) error {
+ return tx.tx.Exec(ctx, query, args, v)
+}
+
+// Query calls tx.Query.
+func (tx *txDriver) Query(ctx context.Context, query string, args, v any) error {
+ return tx.tx.Query(ctx, query, args, v)
+}
+
+var _ dialect.Driver = (*txDriver)(nil)
diff --git a/app/user_verifications/rpc/internal/models/userverifications.go b/app/user_verifications/rpc/internal/models/userverifications.go
new file mode 100644
index 0000000..4d36b2b
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/userverifications.go
@@ -0,0 +1,200 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "encoding/json"
+ "fmt"
+ "juwan-backend/app/user_verifications/rpc/internal/models/schema"
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
+ "strings"
+ "time"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+)
+
+// UserVerifications is the models entity for the UserVerifications schema.
+type UserVerifications struct {
+ config `json:"-"`
+ // ID of the ent.
+ ID int64 `json:"id,omitempty"`
+ // UserID holds the value of the "user_id" field.
+ UserID int64 `json:"user_id,omitempty"`
+ // Role holds the value of the "role" field.
+ Role string `json:"role,omitempty"`
+ // Status holds the value of the "status" field.
+ Status string `json:"status,omitempty"`
+ // Materials holds the value of the "materials" field.
+ Materials schema.MaterialStruct `json:"materials,omitempty"`
+ // RejectReason holds the value of the "reject_reason" field.
+ RejectReason string `json:"reject_reason,omitempty"`
+ // ReviewedBy holds the value of the "reviewed_by" field.
+ ReviewedBy int64 `json:"reviewed_by,omitempty"`
+ // ReviewedAt holds the value of the "reviewed_at" field.
+ ReviewedAt time.Time `json:"reviewed_at,omitempty"`
+ // CreatedAt holds the value of the "created_at" field.
+ CreatedAt time.Time `json:"created_at,omitempty"`
+ // UpdatedAt holds the value of the "updated_at" field.
+ UpdatedAt time.Time `json:"updated_at,omitempty"`
+ selectValues sql.SelectValues
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*UserVerifications) scanValues(columns []string) ([]any, error) {
+ values := make([]any, len(columns))
+ for i := range columns {
+ switch columns[i] {
+ case userverifications.FieldMaterials:
+ values[i] = new([]byte)
+ case userverifications.FieldID, userverifications.FieldUserID, userverifications.FieldReviewedBy:
+ values[i] = new(sql.NullInt64)
+ case userverifications.FieldRole, userverifications.FieldStatus, userverifications.FieldRejectReason:
+ values[i] = new(sql.NullString)
+ case userverifications.FieldReviewedAt, userverifications.FieldCreatedAt, userverifications.FieldUpdatedAt:
+ values[i] = new(sql.NullTime)
+ default:
+ values[i] = new(sql.UnknownType)
+ }
+ }
+ return values, nil
+}
+
+// assignValues assigns the values that were returned from sql.Rows (after scanning)
+// to the UserVerifications fields.
+func (_m *UserVerifications) assignValues(columns []string, values []any) error {
+ if m, n := len(values), len(columns); m < n {
+ return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
+ }
+ for i := range columns {
+ switch columns[i] {
+ case userverifications.FieldID:
+ value, ok := values[i].(*sql.NullInt64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field id", value)
+ }
+ _m.ID = int64(value.Int64)
+ case userverifications.FieldUserID:
+ if value, ok := values[i].(*sql.NullInt64); !ok {
+ return fmt.Errorf("unexpected type %T for field user_id", values[i])
+ } else if value.Valid {
+ _m.UserID = value.Int64
+ }
+ case userverifications.FieldRole:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field role", values[i])
+ } else if value.Valid {
+ _m.Role = value.String
+ }
+ case userverifications.FieldStatus:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field status", values[i])
+ } else if value.Valid {
+ _m.Status = value.String
+ }
+ case userverifications.FieldMaterials:
+ if value, ok := values[i].(*[]byte); !ok {
+ return fmt.Errorf("unexpected type %T for field materials", values[i])
+ } else if value != nil && len(*value) > 0 {
+ if err := json.Unmarshal(*value, &_m.Materials); err != nil {
+ return fmt.Errorf("unmarshal field materials: %w", err)
+ }
+ }
+ case userverifications.FieldRejectReason:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field reject_reason", values[i])
+ } else if value.Valid {
+ _m.RejectReason = value.String
+ }
+ case userverifications.FieldReviewedBy:
+ if value, ok := values[i].(*sql.NullInt64); !ok {
+ return fmt.Errorf("unexpected type %T for field reviewed_by", values[i])
+ } else if value.Valid {
+ _m.ReviewedBy = value.Int64
+ }
+ case userverifications.FieldReviewedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field reviewed_at", values[i])
+ } else if value.Valid {
+ _m.ReviewedAt = value.Time
+ }
+ case userverifications.FieldCreatedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field created_at", values[i])
+ } else if value.Valid {
+ _m.CreatedAt = value.Time
+ }
+ case userverifications.FieldUpdatedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+ } else if value.Valid {
+ _m.UpdatedAt = value.Time
+ }
+ default:
+ _m.selectValues.Set(columns[i], values[i])
+ }
+ }
+ return nil
+}
+
+// Value returns the ent.Value that was dynamically selected and assigned to the UserVerifications.
+// This includes values selected through modifiers, order, etc.
+func (_m *UserVerifications) Value(name string) (ent.Value, error) {
+ return _m.selectValues.Get(name)
+}
+
+// Update returns a builder for updating this UserVerifications.
+// Note that you need to call UserVerifications.Unwrap() before calling this method if this UserVerifications
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (_m *UserVerifications) Update() *UserVerificationsUpdateOne {
+ return NewUserVerificationsClient(_m.config).UpdateOne(_m)
+}
+
+// Unwrap unwraps the UserVerifications entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
+func (_m *UserVerifications) Unwrap() *UserVerifications {
+ _tx, ok := _m.config.driver.(*txDriver)
+ if !ok {
+ panic("models: UserVerifications is not a transactional entity")
+ }
+ _m.config.driver = _tx.drv
+ return _m
+}
+
+// String implements the fmt.Stringer.
+func (_m *UserVerifications) String() string {
+ var builder strings.Builder
+ builder.WriteString("UserVerifications(")
+ builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
+ builder.WriteString("user_id=")
+ builder.WriteString(fmt.Sprintf("%v", _m.UserID))
+ builder.WriteString(", ")
+ builder.WriteString("role=")
+ builder.WriteString(_m.Role)
+ builder.WriteString(", ")
+ builder.WriteString("status=")
+ builder.WriteString(_m.Status)
+ builder.WriteString(", ")
+ builder.WriteString("materials=")
+ builder.WriteString(fmt.Sprintf("%v", _m.Materials))
+ builder.WriteString(", ")
+ builder.WriteString("reject_reason=")
+ builder.WriteString(_m.RejectReason)
+ builder.WriteString(", ")
+ builder.WriteString("reviewed_by=")
+ builder.WriteString(fmt.Sprintf("%v", _m.ReviewedBy))
+ builder.WriteString(", ")
+ builder.WriteString("reviewed_at=")
+ builder.WriteString(_m.ReviewedAt.Format(time.ANSIC))
+ builder.WriteString(", ")
+ builder.WriteString("created_at=")
+ builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
+ builder.WriteString(", ")
+ builder.WriteString("updated_at=")
+ builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
+ builder.WriteByte(')')
+ return builder.String()
+}
+
+// UserVerificationsSlice is a parsable slice of UserVerifications.
+type UserVerificationsSlice []*UserVerifications
diff --git a/app/user_verifications/rpc/internal/models/userverifications/userverifications.go b/app/user_verifications/rpc/internal/models/userverifications/userverifications.go
new file mode 100644
index 0000000..2a1081b
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/userverifications/userverifications.go
@@ -0,0 +1,113 @@
+// Code generated by ent, DO NOT EDIT.
+
+package userverifications
+
+import (
+ "entgo.io/ent/dialect/sql"
+)
+
+const (
+ // Label holds the string label denoting the userverifications type in the database.
+ Label = "user_verifications"
+ // FieldID holds the string denoting the id field in the database.
+ FieldID = "id"
+ // FieldUserID holds the string denoting the user_id field in the database.
+ FieldUserID = "user_id"
+ // FieldRole holds the string denoting the role field in the database.
+ FieldRole = "role"
+ // FieldStatus holds the string denoting the status field in the database.
+ FieldStatus = "status"
+ // FieldMaterials holds the string denoting the materials field in the database.
+ FieldMaterials = "materials"
+ // FieldRejectReason holds the string denoting the reject_reason field in the database.
+ FieldRejectReason = "reject_reason"
+ // FieldReviewedBy holds the string denoting the reviewed_by field in the database.
+ FieldReviewedBy = "reviewed_by"
+ // FieldReviewedAt holds the string denoting the reviewed_at field in the database.
+ FieldReviewedAt = "reviewed_at"
+ // FieldCreatedAt holds the string denoting the created_at field in the database.
+ FieldCreatedAt = "created_at"
+ // FieldUpdatedAt holds the string denoting the updated_at field in the database.
+ FieldUpdatedAt = "updated_at"
+ // Table holds the table name of the userverifications in the database.
+ Table = "user_verifications"
+)
+
+// Columns holds all SQL columns for userverifications fields.
+var Columns = []string{
+ FieldID,
+ FieldUserID,
+ FieldRole,
+ FieldStatus,
+ FieldMaterials,
+ FieldRejectReason,
+ FieldReviewedBy,
+ FieldReviewedAt,
+ FieldCreatedAt,
+ FieldUpdatedAt,
+}
+
+// ValidColumn reports if the column name is valid (part of the table columns).
+func ValidColumn(column string) bool {
+ for i := range Columns {
+ if column == Columns[i] {
+ return true
+ }
+ }
+ return false
+}
+
+var (
+ // DefaultStatus holds the default value on creation for the "status" field.
+ DefaultStatus string
+ // DefaultRejectReason holds the default value on creation for the "reject_reason" field.
+ DefaultRejectReason string
+)
+
+// OrderOption defines the ordering options for the UserVerifications queries.
+type OrderOption func(*sql.Selector)
+
+// ByID orders the results by the id field.
+func ByID(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldID, opts...).ToFunc()
+}
+
+// ByUserID orders the results by the user_id field.
+func ByUserID(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldUserID, opts...).ToFunc()
+}
+
+// ByRole orders the results by the role field.
+func ByRole(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldRole, opts...).ToFunc()
+}
+
+// ByStatus orders the results by the status field.
+func ByStatus(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldStatus, opts...).ToFunc()
+}
+
+// ByRejectReason orders the results by the reject_reason field.
+func ByRejectReason(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldRejectReason, opts...).ToFunc()
+}
+
+// ByReviewedBy orders the results by the reviewed_by field.
+func ByReviewedBy(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldReviewedBy, opts...).ToFunc()
+}
+
+// ByReviewedAt orders the results by the reviewed_at field.
+func ByReviewedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldReviewedAt, opts...).ToFunc()
+}
+
+// ByCreatedAt orders the results by the created_at field.
+func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
+}
+
+// ByUpdatedAt orders the results by the updated_at field.
+func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
+}
diff --git a/app/user_verifications/rpc/internal/models/userverifications/where.go b/app/user_verifications/rpc/internal/models/userverifications/where.go
new file mode 100644
index 0000000..d50c71b
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/userverifications/where.go
@@ -0,0 +1,505 @@
+// Code generated by ent, DO NOT EDIT.
+
+package userverifications
+
+import (
+ "juwan-backend/app/user_verifications/rpc/internal/models/predicate"
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+)
+
+// ID filters vertices based on their ID field.
+func ID(id int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLTE(FieldID, id))
+}
+
+// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ.
+func UserID(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldUserID, v))
+}
+
+// Role applies equality check predicate on the "role" field. It's identical to RoleEQ.
+func Role(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldRole, v))
+}
+
+// Status applies equality check predicate on the "status" field. It's identical to StatusEQ.
+func Status(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldStatus, v))
+}
+
+// RejectReason applies equality check predicate on the "reject_reason" field. It's identical to RejectReasonEQ.
+func RejectReason(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldRejectReason, v))
+}
+
+// ReviewedBy applies equality check predicate on the "reviewed_by" field. It's identical to ReviewedByEQ.
+func ReviewedBy(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldReviewedBy, v))
+}
+
+// ReviewedAt applies equality check predicate on the "reviewed_at" field. It's identical to ReviewedAtEQ.
+func ReviewedAt(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldReviewedAt, v))
+}
+
+// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
+func CreatedAt(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
+func UpdatedAt(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UserIDEQ applies the EQ predicate on the "user_id" field.
+func UserIDEQ(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldUserID, v))
+}
+
+// UserIDNEQ applies the NEQ predicate on the "user_id" field.
+func UserIDNEQ(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNEQ(FieldUserID, v))
+}
+
+// UserIDIn applies the In predicate on the "user_id" field.
+func UserIDIn(vs ...int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldIn(FieldUserID, vs...))
+}
+
+// UserIDNotIn applies the NotIn predicate on the "user_id" field.
+func UserIDNotIn(vs ...int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNotIn(FieldUserID, vs...))
+}
+
+// UserIDGT applies the GT predicate on the "user_id" field.
+func UserIDGT(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGT(FieldUserID, v))
+}
+
+// UserIDGTE applies the GTE predicate on the "user_id" field.
+func UserIDGTE(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGTE(FieldUserID, v))
+}
+
+// UserIDLT applies the LT predicate on the "user_id" field.
+func UserIDLT(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLT(FieldUserID, v))
+}
+
+// UserIDLTE applies the LTE predicate on the "user_id" field.
+func UserIDLTE(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLTE(FieldUserID, v))
+}
+
+// RoleEQ applies the EQ predicate on the "role" field.
+func RoleEQ(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldRole, v))
+}
+
+// RoleNEQ applies the NEQ predicate on the "role" field.
+func RoleNEQ(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNEQ(FieldRole, v))
+}
+
+// RoleIn applies the In predicate on the "role" field.
+func RoleIn(vs ...string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldIn(FieldRole, vs...))
+}
+
+// RoleNotIn applies the NotIn predicate on the "role" field.
+func RoleNotIn(vs ...string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNotIn(FieldRole, vs...))
+}
+
+// RoleGT applies the GT predicate on the "role" field.
+func RoleGT(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGT(FieldRole, v))
+}
+
+// RoleGTE applies the GTE predicate on the "role" field.
+func RoleGTE(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGTE(FieldRole, v))
+}
+
+// RoleLT applies the LT predicate on the "role" field.
+func RoleLT(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLT(FieldRole, v))
+}
+
+// RoleLTE applies the LTE predicate on the "role" field.
+func RoleLTE(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLTE(FieldRole, v))
+}
+
+// RoleContains applies the Contains predicate on the "role" field.
+func RoleContains(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldContains(FieldRole, v))
+}
+
+// RoleHasPrefix applies the HasPrefix predicate on the "role" field.
+func RoleHasPrefix(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldHasPrefix(FieldRole, v))
+}
+
+// RoleHasSuffix applies the HasSuffix predicate on the "role" field.
+func RoleHasSuffix(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldHasSuffix(FieldRole, v))
+}
+
+// RoleEqualFold applies the EqualFold predicate on the "role" field.
+func RoleEqualFold(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEqualFold(FieldRole, v))
+}
+
+// RoleContainsFold applies the ContainsFold predicate on the "role" field.
+func RoleContainsFold(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldContainsFold(FieldRole, v))
+}
+
+// StatusEQ applies the EQ predicate on the "status" field.
+func StatusEQ(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldStatus, v))
+}
+
+// StatusNEQ applies the NEQ predicate on the "status" field.
+func StatusNEQ(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNEQ(FieldStatus, v))
+}
+
+// StatusIn applies the In predicate on the "status" field.
+func StatusIn(vs ...string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldIn(FieldStatus, vs...))
+}
+
+// StatusNotIn applies the NotIn predicate on the "status" field.
+func StatusNotIn(vs ...string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNotIn(FieldStatus, vs...))
+}
+
+// StatusGT applies the GT predicate on the "status" field.
+func StatusGT(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGT(FieldStatus, v))
+}
+
+// StatusGTE applies the GTE predicate on the "status" field.
+func StatusGTE(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGTE(FieldStatus, v))
+}
+
+// StatusLT applies the LT predicate on the "status" field.
+func StatusLT(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLT(FieldStatus, v))
+}
+
+// StatusLTE applies the LTE predicate on the "status" field.
+func StatusLTE(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLTE(FieldStatus, v))
+}
+
+// StatusContains applies the Contains predicate on the "status" field.
+func StatusContains(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldContains(FieldStatus, v))
+}
+
+// StatusHasPrefix applies the HasPrefix predicate on the "status" field.
+func StatusHasPrefix(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldHasPrefix(FieldStatus, v))
+}
+
+// StatusHasSuffix applies the HasSuffix predicate on the "status" field.
+func StatusHasSuffix(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldHasSuffix(FieldStatus, v))
+}
+
+// StatusEqualFold applies the EqualFold predicate on the "status" field.
+func StatusEqualFold(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEqualFold(FieldStatus, v))
+}
+
+// StatusContainsFold applies the ContainsFold predicate on the "status" field.
+func StatusContainsFold(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldContainsFold(FieldStatus, v))
+}
+
+// RejectReasonEQ applies the EQ predicate on the "reject_reason" field.
+func RejectReasonEQ(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldRejectReason, v))
+}
+
+// RejectReasonNEQ applies the NEQ predicate on the "reject_reason" field.
+func RejectReasonNEQ(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNEQ(FieldRejectReason, v))
+}
+
+// RejectReasonIn applies the In predicate on the "reject_reason" field.
+func RejectReasonIn(vs ...string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldIn(FieldRejectReason, vs...))
+}
+
+// RejectReasonNotIn applies the NotIn predicate on the "reject_reason" field.
+func RejectReasonNotIn(vs ...string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNotIn(FieldRejectReason, vs...))
+}
+
+// RejectReasonGT applies the GT predicate on the "reject_reason" field.
+func RejectReasonGT(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGT(FieldRejectReason, v))
+}
+
+// RejectReasonGTE applies the GTE predicate on the "reject_reason" field.
+func RejectReasonGTE(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGTE(FieldRejectReason, v))
+}
+
+// RejectReasonLT applies the LT predicate on the "reject_reason" field.
+func RejectReasonLT(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLT(FieldRejectReason, v))
+}
+
+// RejectReasonLTE applies the LTE predicate on the "reject_reason" field.
+func RejectReasonLTE(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLTE(FieldRejectReason, v))
+}
+
+// RejectReasonContains applies the Contains predicate on the "reject_reason" field.
+func RejectReasonContains(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldContains(FieldRejectReason, v))
+}
+
+// RejectReasonHasPrefix applies the HasPrefix predicate on the "reject_reason" field.
+func RejectReasonHasPrefix(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldHasPrefix(FieldRejectReason, v))
+}
+
+// RejectReasonHasSuffix applies the HasSuffix predicate on the "reject_reason" field.
+func RejectReasonHasSuffix(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldHasSuffix(FieldRejectReason, v))
+}
+
+// RejectReasonEqualFold applies the EqualFold predicate on the "reject_reason" field.
+func RejectReasonEqualFold(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEqualFold(FieldRejectReason, v))
+}
+
+// RejectReasonContainsFold applies the ContainsFold predicate on the "reject_reason" field.
+func RejectReasonContainsFold(v string) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldContainsFold(FieldRejectReason, v))
+}
+
+// ReviewedByEQ applies the EQ predicate on the "reviewed_by" field.
+func ReviewedByEQ(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldReviewedBy, v))
+}
+
+// ReviewedByNEQ applies the NEQ predicate on the "reviewed_by" field.
+func ReviewedByNEQ(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNEQ(FieldReviewedBy, v))
+}
+
+// ReviewedByIn applies the In predicate on the "reviewed_by" field.
+func ReviewedByIn(vs ...int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldIn(FieldReviewedBy, vs...))
+}
+
+// ReviewedByNotIn applies the NotIn predicate on the "reviewed_by" field.
+func ReviewedByNotIn(vs ...int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNotIn(FieldReviewedBy, vs...))
+}
+
+// ReviewedByGT applies the GT predicate on the "reviewed_by" field.
+func ReviewedByGT(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGT(FieldReviewedBy, v))
+}
+
+// ReviewedByGTE applies the GTE predicate on the "reviewed_by" field.
+func ReviewedByGTE(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGTE(FieldReviewedBy, v))
+}
+
+// ReviewedByLT applies the LT predicate on the "reviewed_by" field.
+func ReviewedByLT(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLT(FieldReviewedBy, v))
+}
+
+// ReviewedByLTE applies the LTE predicate on the "reviewed_by" field.
+func ReviewedByLTE(v int64) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLTE(FieldReviewedBy, v))
+}
+
+// ReviewedAtEQ applies the EQ predicate on the "reviewed_at" field.
+func ReviewedAtEQ(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldReviewedAt, v))
+}
+
+// ReviewedAtNEQ applies the NEQ predicate on the "reviewed_at" field.
+func ReviewedAtNEQ(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNEQ(FieldReviewedAt, v))
+}
+
+// ReviewedAtIn applies the In predicate on the "reviewed_at" field.
+func ReviewedAtIn(vs ...time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldIn(FieldReviewedAt, vs...))
+}
+
+// ReviewedAtNotIn applies the NotIn predicate on the "reviewed_at" field.
+func ReviewedAtNotIn(vs ...time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNotIn(FieldReviewedAt, vs...))
+}
+
+// ReviewedAtGT applies the GT predicate on the "reviewed_at" field.
+func ReviewedAtGT(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGT(FieldReviewedAt, v))
+}
+
+// ReviewedAtGTE applies the GTE predicate on the "reviewed_at" field.
+func ReviewedAtGTE(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGTE(FieldReviewedAt, v))
+}
+
+// ReviewedAtLT applies the LT predicate on the "reviewed_at" field.
+func ReviewedAtLT(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLT(FieldReviewedAt, v))
+}
+
+// ReviewedAtLTE applies the LTE predicate on the "reviewed_at" field.
+func ReviewedAtLTE(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLTE(FieldReviewedAt, v))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// And groups predicates with the AND operator between them.
+func And(predicates ...predicate.UserVerifications) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.UserVerifications) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.UserVerifications) predicate.UserVerifications {
+ return predicate.UserVerifications(sql.NotPredicates(p))
+}
diff --git a/app/user_verifications/rpc/internal/models/userverifications_create.go b/app/user_verifications/rpc/internal/models/userverifications_create.go
new file mode 100644
index 0000000..752207d
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/userverifications_create.go
@@ -0,0 +1,331 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "juwan-backend/app/user_verifications/rpc/internal/models/schema"
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
+ "time"
+
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+)
+
+// UserVerificationsCreate is the builder for creating a UserVerifications entity.
+type UserVerificationsCreate struct {
+ config
+ mutation *UserVerificationsMutation
+ hooks []Hook
+}
+
+// SetUserID sets the "user_id" field.
+func (_c *UserVerificationsCreate) SetUserID(v int64) *UserVerificationsCreate {
+ _c.mutation.SetUserID(v)
+ return _c
+}
+
+// SetRole sets the "role" field.
+func (_c *UserVerificationsCreate) SetRole(v string) *UserVerificationsCreate {
+ _c.mutation.SetRole(v)
+ return _c
+}
+
+// SetStatus sets the "status" field.
+func (_c *UserVerificationsCreate) SetStatus(v string) *UserVerificationsCreate {
+ _c.mutation.SetStatus(v)
+ return _c
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (_c *UserVerificationsCreate) SetNillableStatus(v *string) *UserVerificationsCreate {
+ if v != nil {
+ _c.SetStatus(*v)
+ }
+ return _c
+}
+
+// SetMaterials sets the "materials" field.
+func (_c *UserVerificationsCreate) SetMaterials(v schema.MaterialStruct) *UserVerificationsCreate {
+ _c.mutation.SetMaterials(v)
+ return _c
+}
+
+// SetRejectReason sets the "reject_reason" field.
+func (_c *UserVerificationsCreate) SetRejectReason(v string) *UserVerificationsCreate {
+ _c.mutation.SetRejectReason(v)
+ return _c
+}
+
+// SetNillableRejectReason sets the "reject_reason" field if the given value is not nil.
+func (_c *UserVerificationsCreate) SetNillableRejectReason(v *string) *UserVerificationsCreate {
+ if v != nil {
+ _c.SetRejectReason(*v)
+ }
+ return _c
+}
+
+// SetReviewedBy sets the "reviewed_by" field.
+func (_c *UserVerificationsCreate) SetReviewedBy(v int64) *UserVerificationsCreate {
+ _c.mutation.SetReviewedBy(v)
+ return _c
+}
+
+// SetReviewedAt sets the "reviewed_at" field.
+func (_c *UserVerificationsCreate) SetReviewedAt(v time.Time) *UserVerificationsCreate {
+ _c.mutation.SetReviewedAt(v)
+ return _c
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (_c *UserVerificationsCreate) SetCreatedAt(v time.Time) *UserVerificationsCreate {
+ _c.mutation.SetCreatedAt(v)
+ return _c
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_c *UserVerificationsCreate) SetUpdatedAt(v time.Time) *UserVerificationsCreate {
+ _c.mutation.SetUpdatedAt(v)
+ return _c
+}
+
+// SetID sets the "id" field.
+func (_c *UserVerificationsCreate) SetID(v int64) *UserVerificationsCreate {
+ _c.mutation.SetID(v)
+ return _c
+}
+
+// Mutation returns the UserVerificationsMutation object of the builder.
+func (_c *UserVerificationsCreate) Mutation() *UserVerificationsMutation {
+ return _c.mutation
+}
+
+// Save creates the UserVerifications in the database.
+func (_c *UserVerificationsCreate) Save(ctx context.Context) (*UserVerifications, error) {
+ _c.defaults()
+ return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (_c *UserVerificationsCreate) SaveX(ctx context.Context) *UserVerifications {
+ v, err := _c.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Exec executes the query.
+func (_c *UserVerificationsCreate) Exec(ctx context.Context) error {
+ _, err := _c.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_c *UserVerificationsCreate) ExecX(ctx context.Context) {
+ if err := _c.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_c *UserVerificationsCreate) defaults() {
+ if _, ok := _c.mutation.Status(); !ok {
+ v := userverifications.DefaultStatus
+ _c.mutation.SetStatus(v)
+ }
+ if _, ok := _c.mutation.RejectReason(); !ok {
+ v := userverifications.DefaultRejectReason
+ _c.mutation.SetRejectReason(v)
+ }
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_c *UserVerificationsCreate) check() error {
+ if _, ok := _c.mutation.UserID(); !ok {
+ return &ValidationError{Name: "user_id", err: errors.New(`models: missing required field "UserVerifications.user_id"`)}
+ }
+ if _, ok := _c.mutation.Role(); !ok {
+ return &ValidationError{Name: "role", err: errors.New(`models: missing required field "UserVerifications.role"`)}
+ }
+ if _, ok := _c.mutation.Status(); !ok {
+ return &ValidationError{Name: "status", err: errors.New(`models: missing required field "UserVerifications.status"`)}
+ }
+ if _, ok := _c.mutation.Materials(); !ok {
+ return &ValidationError{Name: "materials", err: errors.New(`models: missing required field "UserVerifications.materials"`)}
+ }
+ if _, ok := _c.mutation.RejectReason(); !ok {
+ return &ValidationError{Name: "reject_reason", err: errors.New(`models: missing required field "UserVerifications.reject_reason"`)}
+ }
+ if _, ok := _c.mutation.ReviewedBy(); !ok {
+ return &ValidationError{Name: "reviewed_by", err: errors.New(`models: missing required field "UserVerifications.reviewed_by"`)}
+ }
+ if _, ok := _c.mutation.ReviewedAt(); !ok {
+ return &ValidationError{Name: "reviewed_at", err: errors.New(`models: missing required field "UserVerifications.reviewed_at"`)}
+ }
+ if _, ok := _c.mutation.CreatedAt(); !ok {
+ return &ValidationError{Name: "created_at", err: errors.New(`models: missing required field "UserVerifications.created_at"`)}
+ }
+ if _, ok := _c.mutation.UpdatedAt(); !ok {
+ return &ValidationError{Name: "updated_at", err: errors.New(`models: missing required field "UserVerifications.updated_at"`)}
+ }
+ return nil
+}
+
+func (_c *UserVerificationsCreate) sqlSave(ctx context.Context) (*UserVerifications, error) {
+ if err := _c.check(); err != nil {
+ return nil, err
+ }
+ _node, _spec := _c.createSpec()
+ if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
+ if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return nil, err
+ }
+ if _spec.ID.Value != _node.ID {
+ id := _spec.ID.Value.(int64)
+ _node.ID = int64(id)
+ }
+ _c.mutation.id = &_node.ID
+ _c.mutation.done = true
+ return _node, nil
+}
+
+func (_c *UserVerificationsCreate) createSpec() (*UserVerifications, *sqlgraph.CreateSpec) {
+ var (
+ _node = &UserVerifications{config: _c.config}
+ _spec = sqlgraph.NewCreateSpec(userverifications.Table, sqlgraph.NewFieldSpec(userverifications.FieldID, field.TypeInt64))
+ )
+ if id, ok := _c.mutation.ID(); ok {
+ _node.ID = id
+ _spec.ID.Value = id
+ }
+ if value, ok := _c.mutation.UserID(); ok {
+ _spec.SetField(userverifications.FieldUserID, field.TypeInt64, value)
+ _node.UserID = value
+ }
+ if value, ok := _c.mutation.Role(); ok {
+ _spec.SetField(userverifications.FieldRole, field.TypeString, value)
+ _node.Role = value
+ }
+ if value, ok := _c.mutation.Status(); ok {
+ _spec.SetField(userverifications.FieldStatus, field.TypeString, value)
+ _node.Status = value
+ }
+ if value, ok := _c.mutation.Materials(); ok {
+ _spec.SetField(userverifications.FieldMaterials, field.TypeJSON, value)
+ _node.Materials = value
+ }
+ if value, ok := _c.mutation.RejectReason(); ok {
+ _spec.SetField(userverifications.FieldRejectReason, field.TypeString, value)
+ _node.RejectReason = value
+ }
+ if value, ok := _c.mutation.ReviewedBy(); ok {
+ _spec.SetField(userverifications.FieldReviewedBy, field.TypeInt64, value)
+ _node.ReviewedBy = value
+ }
+ if value, ok := _c.mutation.ReviewedAt(); ok {
+ _spec.SetField(userverifications.FieldReviewedAt, field.TypeTime, value)
+ _node.ReviewedAt = value
+ }
+ if value, ok := _c.mutation.CreatedAt(); ok {
+ _spec.SetField(userverifications.FieldCreatedAt, field.TypeTime, value)
+ _node.CreatedAt = value
+ }
+ if value, ok := _c.mutation.UpdatedAt(); ok {
+ _spec.SetField(userverifications.FieldUpdatedAt, field.TypeTime, value)
+ _node.UpdatedAt = value
+ }
+ return _node, _spec
+}
+
+// UserVerificationsCreateBulk is the builder for creating many UserVerifications entities in bulk.
+type UserVerificationsCreateBulk struct {
+ config
+ err error
+ builders []*UserVerificationsCreate
+}
+
+// Save creates the UserVerifications entities in the database.
+func (_c *UserVerificationsCreateBulk) Save(ctx context.Context) ([]*UserVerifications, error) {
+ if _c.err != nil {
+ return nil, _c.err
+ }
+ specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
+ nodes := make([]*UserVerifications, len(_c.builders))
+ mutators := make([]Mutator, len(_c.builders))
+ for i := range _c.builders {
+ func(i int, root context.Context) {
+ builder := _c.builders[i]
+ builder.defaults()
+ var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+ mutation, ok := m.(*UserVerificationsMutation)
+ if !ok {
+ return nil, fmt.Errorf("unexpected mutation type %T", m)
+ }
+ if err := builder.check(); err != nil {
+ return nil, err
+ }
+ builder.mutation = mutation
+ var err error
+ nodes[i], specs[i] = builder.createSpec()
+ if i < len(mutators)-1 {
+ _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
+ } else {
+ spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+ // Invoke the actual operation on the latest mutation in the chain.
+ if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
+ if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ mutation.id = &nodes[i].ID
+ if specs[i].ID.Value != nil && nodes[i].ID == 0 {
+ id := specs[i].ID.Value.(int64)
+ nodes[i].ID = int64(id)
+ }
+ mutation.done = true
+ return nodes[i], nil
+ })
+ for i := len(builder.hooks) - 1; i >= 0; i-- {
+ mut = builder.hooks[i](mut)
+ }
+ mutators[i] = mut
+ }(i, ctx)
+ }
+ if len(mutators) > 0 {
+ if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
+ return nil, err
+ }
+ }
+ return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_c *UserVerificationsCreateBulk) SaveX(ctx context.Context) []*UserVerifications {
+ v, err := _c.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Exec executes the query.
+func (_c *UserVerificationsCreateBulk) Exec(ctx context.Context) error {
+ _, err := _c.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_c *UserVerificationsCreateBulk) ExecX(ctx context.Context) {
+ if err := _c.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
diff --git a/app/user_verifications/rpc/internal/models/userverifications_delete.go b/app/user_verifications/rpc/internal/models/userverifications_delete.go
new file mode 100644
index 0000000..6075465
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/userverifications_delete.go
@@ -0,0 +1,88 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "context"
+ "juwan-backend/app/user_verifications/rpc/internal/models/predicate"
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+)
+
+// UserVerificationsDelete is the builder for deleting a UserVerifications entity.
+type UserVerificationsDelete struct {
+ config
+ hooks []Hook
+ mutation *UserVerificationsMutation
+}
+
+// Where appends a list predicates to the UserVerificationsDelete builder.
+func (_d *UserVerificationsDelete) Where(ps ...predicate.UserVerifications) *UserVerificationsDelete {
+ _d.mutation.Where(ps...)
+ return _d
+}
+
+// Exec executes the deletion query and returns how many vertices were deleted.
+func (_d *UserVerificationsDelete) Exec(ctx context.Context) (int, error) {
+ return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_d *UserVerificationsDelete) ExecX(ctx context.Context) int {
+ n, err := _d.Exec(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return n
+}
+
+func (_d *UserVerificationsDelete) sqlExec(ctx context.Context) (int, error) {
+ _spec := sqlgraph.NewDeleteSpec(userverifications.Table, sqlgraph.NewFieldSpec(userverifications.FieldID, field.TypeInt64))
+ if ps := _d.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
+ if err != nil && sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ _d.mutation.done = true
+ return affected, err
+}
+
+// UserVerificationsDeleteOne is the builder for deleting a single UserVerifications entity.
+type UserVerificationsDeleteOne struct {
+ _d *UserVerificationsDelete
+}
+
+// Where appends a list predicates to the UserVerificationsDelete builder.
+func (_d *UserVerificationsDeleteOne) Where(ps ...predicate.UserVerifications) *UserVerificationsDeleteOne {
+ _d._d.mutation.Where(ps...)
+ return _d
+}
+
+// Exec executes the deletion query.
+func (_d *UserVerificationsDeleteOne) Exec(ctx context.Context) error {
+ n, err := _d._d.Exec(ctx)
+ switch {
+ case err != nil:
+ return err
+ case n == 0:
+ return &NotFoundError{userverifications.Label}
+ default:
+ return nil
+ }
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_d *UserVerificationsDeleteOne) ExecX(ctx context.Context) {
+ if err := _d.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
diff --git a/app/user_verifications/rpc/internal/models/userverifications_query.go b/app/user_verifications/rpc/internal/models/userverifications_query.go
new file mode 100644
index 0000000..0a02789
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/userverifications_query.go
@@ -0,0 +1,527 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "context"
+ "fmt"
+ "juwan-backend/app/user_verifications/rpc/internal/models/predicate"
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
+ "math"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+)
+
+// UserVerificationsQuery is the builder for querying UserVerifications entities.
+type UserVerificationsQuery struct {
+ config
+ ctx *QueryContext
+ order []userverifications.OrderOption
+ inters []Interceptor
+ predicates []predicate.UserVerifications
+ // intermediate query (i.e. traversal path).
+ sql *sql.Selector
+ path func(context.Context) (*sql.Selector, error)
+}
+
+// Where adds a new predicate for the UserVerificationsQuery builder.
+func (_q *UserVerificationsQuery) Where(ps ...predicate.UserVerifications) *UserVerificationsQuery {
+ _q.predicates = append(_q.predicates, ps...)
+ return _q
+}
+
+// Limit the number of records to be returned by this query.
+func (_q *UserVerificationsQuery) Limit(limit int) *UserVerificationsQuery {
+ _q.ctx.Limit = &limit
+ return _q
+}
+
+// Offset to start from.
+func (_q *UserVerificationsQuery) Offset(offset int) *UserVerificationsQuery {
+ _q.ctx.Offset = &offset
+ return _q
+}
+
+// Unique configures the query builder to filter duplicate records on query.
+// By default, unique is set to true, and can be disabled using this method.
+func (_q *UserVerificationsQuery) Unique(unique bool) *UserVerificationsQuery {
+ _q.ctx.Unique = &unique
+ return _q
+}
+
+// Order specifies how the records should be ordered.
+func (_q *UserVerificationsQuery) Order(o ...userverifications.OrderOption) *UserVerificationsQuery {
+ _q.order = append(_q.order, o...)
+ return _q
+}
+
+// First returns the first UserVerifications entity from the query.
+// Returns a *NotFoundError when no UserVerifications was found.
+func (_q *UserVerificationsQuery) First(ctx context.Context) (*UserVerifications, error) {
+ nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
+ if err != nil {
+ return nil, err
+ }
+ if len(nodes) == 0 {
+ return nil, &NotFoundError{userverifications.Label}
+ }
+ return nodes[0], nil
+}
+
+// FirstX is like First, but panics if an error occurs.
+func (_q *UserVerificationsQuery) FirstX(ctx context.Context) *UserVerifications {
+ node, err := _q.First(ctx)
+ if err != nil && !IsNotFound(err) {
+ panic(err)
+ }
+ return node
+}
+
+// FirstID returns the first UserVerifications ID from the query.
+// Returns a *NotFoundError when no UserVerifications ID was found.
+func (_q *UserVerificationsQuery) FirstID(ctx context.Context) (id int64, err error) {
+ var ids []int64
+ if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
+ return
+ }
+ if len(ids) == 0 {
+ err = &NotFoundError{userverifications.Label}
+ return
+ }
+ return ids[0], nil
+}
+
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (_q *UserVerificationsQuery) FirstIDX(ctx context.Context) int64 {
+ id, err := _q.FirstID(ctx)
+ if err != nil && !IsNotFound(err) {
+ panic(err)
+ }
+ return id
+}
+
+// Only returns a single UserVerifications entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when more than one UserVerifications entity is found.
+// Returns a *NotFoundError when no UserVerifications entities are found.
+func (_q *UserVerificationsQuery) Only(ctx context.Context) (*UserVerifications, error) {
+ nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
+ if err != nil {
+ return nil, err
+ }
+ switch len(nodes) {
+ case 1:
+ return nodes[0], nil
+ case 0:
+ return nil, &NotFoundError{userverifications.Label}
+ default:
+ return nil, &NotSingularError{userverifications.Label}
+ }
+}
+
+// OnlyX is like Only, but panics if an error occurs.
+func (_q *UserVerificationsQuery) OnlyX(ctx context.Context) *UserVerifications {
+ node, err := _q.Only(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return node
+}
+
+// OnlyID is like Only, but returns the only UserVerifications ID in the query.
+// Returns a *NotSingularError when more than one UserVerifications ID is found.
+// Returns a *NotFoundError when no entities are found.
+func (_q *UserVerificationsQuery) OnlyID(ctx context.Context) (id int64, err error) {
+ var ids []int64
+ if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
+ return
+ }
+ switch len(ids) {
+ case 1:
+ id = ids[0]
+ case 0:
+ err = &NotFoundError{userverifications.Label}
+ default:
+ err = &NotSingularError{userverifications.Label}
+ }
+ return
+}
+
+// OnlyIDX is like OnlyID, but panics if an error occurs.
+func (_q *UserVerificationsQuery) OnlyIDX(ctx context.Context) int64 {
+ id, err := _q.OnlyID(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return id
+}
+
+// All executes the query and returns a list of UserVerificationsSlice.
+func (_q *UserVerificationsQuery) All(ctx context.Context) ([]*UserVerifications, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
+ if err := _q.prepareQuery(ctx); err != nil {
+ return nil, err
+ }
+ qr := querierAll[[]*UserVerifications, *UserVerificationsQuery]()
+ return withInterceptors[[]*UserVerifications](ctx, _q, qr, _q.inters)
+}
+
+// AllX is like All, but panics if an error occurs.
+func (_q *UserVerificationsQuery) AllX(ctx context.Context) []*UserVerifications {
+ nodes, err := _q.All(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return nodes
+}
+
+// IDs executes the query and returns a list of UserVerifications IDs.
+func (_q *UserVerificationsQuery) IDs(ctx context.Context) (ids []int64, err error) {
+ if _q.ctx.Unique == nil && _q.path != nil {
+ _q.Unique(true)
+ }
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
+ if err = _q.Select(userverifications.FieldID).Scan(ctx, &ids); err != nil {
+ return nil, err
+ }
+ return ids, nil
+}
+
+// IDsX is like IDs, but panics if an error occurs.
+func (_q *UserVerificationsQuery) IDsX(ctx context.Context) []int64 {
+ ids, err := _q.IDs(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return ids
+}
+
+// Count returns the count of the given query.
+func (_q *UserVerificationsQuery) Count(ctx context.Context) (int, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
+ if err := _q.prepareQuery(ctx); err != nil {
+ return 0, err
+ }
+ return withInterceptors[int](ctx, _q, querierCount[*UserVerificationsQuery](), _q.inters)
+}
+
+// CountX is like Count, but panics if an error occurs.
+func (_q *UserVerificationsQuery) CountX(ctx context.Context) int {
+ count, err := _q.Count(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return count
+}
+
+// Exist returns true if the query has elements in the graph.
+func (_q *UserVerificationsQuery) Exist(ctx context.Context) (bool, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
+ switch _, err := _q.FirstID(ctx); {
+ case IsNotFound(err):
+ return false, nil
+ case err != nil:
+ return false, fmt.Errorf("models: check existence: %w", err)
+ default:
+ return true, nil
+ }
+}
+
+// ExistX is like Exist, but panics if an error occurs.
+func (_q *UserVerificationsQuery) ExistX(ctx context.Context) bool {
+ exist, err := _q.Exist(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return exist
+}
+
+// Clone returns a duplicate of the UserVerificationsQuery builder, including all associated steps. It can be
+// used to prepare common query builders and use them differently after the clone is made.
+func (_q *UserVerificationsQuery) Clone() *UserVerificationsQuery {
+ if _q == nil {
+ return nil
+ }
+ return &UserVerificationsQuery{
+ config: _q.config,
+ ctx: _q.ctx.Clone(),
+ order: append([]userverifications.OrderOption{}, _q.order...),
+ inters: append([]Interceptor{}, _q.inters...),
+ predicates: append([]predicate.UserVerifications{}, _q.predicates...),
+ // clone intermediate query.
+ sql: _q.sql.Clone(),
+ path: _q.path,
+ }
+}
+
+// GroupBy is used to group vertices by one or more fields/columns.
+// It is often used with aggregate functions, like: count, max, mean, min, sum.
+//
+// Example:
+//
+// var v []struct {
+// UserID int64 `json:"user_id,omitempty"`
+// Count int `json:"count,omitempty"`
+// }
+//
+// client.UserVerifications.Query().
+// GroupBy(userverifications.FieldUserID).
+// Aggregate(models.Count()).
+// Scan(ctx, &v)
+func (_q *UserVerificationsQuery) GroupBy(field string, fields ...string) *UserVerificationsGroupBy {
+ _q.ctx.Fields = append([]string{field}, fields...)
+ grbuild := &UserVerificationsGroupBy{build: _q}
+ grbuild.flds = &_q.ctx.Fields
+ grbuild.label = userverifications.Label
+ grbuild.scan = grbuild.Scan
+ return grbuild
+}
+
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
+//
+// Example:
+//
+// var v []struct {
+// UserID int64 `json:"user_id,omitempty"`
+// }
+//
+// client.UserVerifications.Query().
+// Select(userverifications.FieldUserID).
+// Scan(ctx, &v)
+func (_q *UserVerificationsQuery) Select(fields ...string) *UserVerificationsSelect {
+ _q.ctx.Fields = append(_q.ctx.Fields, fields...)
+ sbuild := &UserVerificationsSelect{UserVerificationsQuery: _q}
+ sbuild.label = userverifications.Label
+ sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
+ return sbuild
+}
+
+// Aggregate returns a UserVerificationsSelect configured with the given aggregations.
+func (_q *UserVerificationsQuery) Aggregate(fns ...AggregateFunc) *UserVerificationsSelect {
+ return _q.Select().Aggregate(fns...)
+}
+
+func (_q *UserVerificationsQuery) prepareQuery(ctx context.Context) error {
+ for _, inter := range _q.inters {
+ if inter == nil {
+ return fmt.Errorf("models: uninitialized interceptor (forgotten import models/runtime?)")
+ }
+ if trv, ok := inter.(Traverser); ok {
+ if err := trv.Traverse(ctx, _q); err != nil {
+ return err
+ }
+ }
+ }
+ for _, f := range _q.ctx.Fields {
+ if !userverifications.ValidColumn(f) {
+ return &ValidationError{Name: f, err: fmt.Errorf("models: invalid field %q for query", f)}
+ }
+ }
+ if _q.path != nil {
+ prev, err := _q.path(ctx)
+ if err != nil {
+ return err
+ }
+ _q.sql = prev
+ }
+ return nil
+}
+
+func (_q *UserVerificationsQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*UserVerifications, error) {
+ var (
+ nodes = []*UserVerifications{}
+ _spec = _q.querySpec()
+ )
+ _spec.ScanValues = func(columns []string) ([]any, error) {
+ return (*UserVerifications).scanValues(nil, columns)
+ }
+ _spec.Assign = func(columns []string, values []any) error {
+ node := &UserVerifications{config: _q.config}
+ nodes = append(nodes, node)
+ return node.assignValues(columns, values)
+ }
+ for i := range hooks {
+ hooks[i](ctx, _spec)
+ }
+ if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
+ return nil, err
+ }
+ if len(nodes) == 0 {
+ return nodes, nil
+ }
+ return nodes, nil
+}
+
+func (_q *UserVerificationsQuery) sqlCount(ctx context.Context) (int, error) {
+ _spec := _q.querySpec()
+ _spec.Node.Columns = _q.ctx.Fields
+ if len(_q.ctx.Fields) > 0 {
+ _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
+ }
+ return sqlgraph.CountNodes(ctx, _q.driver, _spec)
+}
+
+func (_q *UserVerificationsQuery) querySpec() *sqlgraph.QuerySpec {
+ _spec := sqlgraph.NewQuerySpec(userverifications.Table, userverifications.Columns, sqlgraph.NewFieldSpec(userverifications.FieldID, field.TypeInt64))
+ _spec.From = _q.sql
+ if unique := _q.ctx.Unique; unique != nil {
+ _spec.Unique = *unique
+ } else if _q.path != nil {
+ _spec.Unique = true
+ }
+ if fields := _q.ctx.Fields; len(fields) > 0 {
+ _spec.Node.Columns = make([]string, 0, len(fields))
+ _spec.Node.Columns = append(_spec.Node.Columns, userverifications.FieldID)
+ for i := range fields {
+ if fields[i] != userverifications.FieldID {
+ _spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+ }
+ }
+ }
+ if ps := _q.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if limit := _q.ctx.Limit; limit != nil {
+ _spec.Limit = *limit
+ }
+ if offset := _q.ctx.Offset; offset != nil {
+ _spec.Offset = *offset
+ }
+ if ps := _q.order; len(ps) > 0 {
+ _spec.Order = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ return _spec
+}
+
+func (_q *UserVerificationsQuery) sqlQuery(ctx context.Context) *sql.Selector {
+ builder := sql.Dialect(_q.driver.Dialect())
+ t1 := builder.Table(userverifications.Table)
+ columns := _q.ctx.Fields
+ if len(columns) == 0 {
+ columns = userverifications.Columns
+ }
+ selector := builder.Select(t1.Columns(columns...)...).From(t1)
+ if _q.sql != nil {
+ selector = _q.sql
+ selector.Select(selector.Columns(columns...)...)
+ }
+ if _q.ctx.Unique != nil && *_q.ctx.Unique {
+ selector.Distinct()
+ }
+ for _, p := range _q.predicates {
+ p(selector)
+ }
+ for _, p := range _q.order {
+ p(selector)
+ }
+ if offset := _q.ctx.Offset; offset != nil {
+ // limit is mandatory for offset clause. We start
+ // with default value, and override it below if needed.
+ selector.Offset(*offset).Limit(math.MaxInt32)
+ }
+ if limit := _q.ctx.Limit; limit != nil {
+ selector.Limit(*limit)
+ }
+ return selector
+}
+
+// UserVerificationsGroupBy is the group-by builder for UserVerifications entities.
+type UserVerificationsGroupBy struct {
+ selector
+ build *UserVerificationsQuery
+}
+
+// Aggregate adds the given aggregation functions to the group-by query.
+func (_g *UserVerificationsGroupBy) Aggregate(fns ...AggregateFunc) *UserVerificationsGroupBy {
+ _g.fns = append(_g.fns, fns...)
+ return _g
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (_g *UserVerificationsGroupBy) Scan(ctx context.Context, v any) error {
+ ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
+ if err := _g.build.prepareQuery(ctx); err != nil {
+ return err
+ }
+ return scanWithInterceptors[*UserVerificationsQuery, *UserVerificationsGroupBy](ctx, _g.build, _g, _g.build.inters, v)
+}
+
+func (_g *UserVerificationsGroupBy) sqlScan(ctx context.Context, root *UserVerificationsQuery, v any) error {
+ selector := root.sqlQuery(ctx).Select()
+ aggregation := make([]string, 0, len(_g.fns))
+ for _, fn := range _g.fns {
+ aggregation = append(aggregation, fn(selector))
+ }
+ if len(selector.SelectedColumns()) == 0 {
+ columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
+ for _, f := range *_g.flds {
+ columns = append(columns, selector.C(f))
+ }
+ columns = append(columns, aggregation...)
+ selector.Select(columns...)
+ }
+ selector.GroupBy(selector.Columns(*_g.flds...)...)
+ if err := selector.Err(); err != nil {
+ return err
+ }
+ rows := &sql.Rows{}
+ query, args := selector.Query()
+ if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
+ return err
+ }
+ defer rows.Close()
+ return sql.ScanSlice(rows, v)
+}
+
+// UserVerificationsSelect is the builder for selecting fields of UserVerifications entities.
+type UserVerificationsSelect struct {
+ *UserVerificationsQuery
+ selector
+}
+
+// Aggregate adds the given aggregation functions to the selector query.
+func (_s *UserVerificationsSelect) Aggregate(fns ...AggregateFunc) *UserVerificationsSelect {
+ _s.fns = append(_s.fns, fns...)
+ return _s
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (_s *UserVerificationsSelect) Scan(ctx context.Context, v any) error {
+ ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
+ if err := _s.prepareQuery(ctx); err != nil {
+ return err
+ }
+ return scanWithInterceptors[*UserVerificationsQuery, *UserVerificationsSelect](ctx, _s.UserVerificationsQuery, _s, _s.inters, v)
+}
+
+func (_s *UserVerificationsSelect) sqlScan(ctx context.Context, root *UserVerificationsQuery, v any) error {
+ selector := root.sqlQuery(ctx)
+ aggregation := make([]string, 0, len(_s.fns))
+ for _, fn := range _s.fns {
+ aggregation = append(aggregation, fn(selector))
+ }
+ switch n := len(*_s.selector.flds); {
+ case n == 0 && len(aggregation) > 0:
+ selector.Select(aggregation...)
+ case n != 0 && len(aggregation) > 0:
+ selector.AppendSelect(aggregation...)
+ }
+ rows := &sql.Rows{}
+ query, args := selector.Query()
+ if err := _s.driver.Query(ctx, query, args, rows); err != nil {
+ return err
+ }
+ defer rows.Close()
+ return sql.ScanSlice(rows, v)
+}
diff --git a/app/user_verifications/rpc/internal/models/userverifications_update.go b/app/user_verifications/rpc/internal/models/userverifications_update.go
new file mode 100644
index 0000000..ceb0ad9
--- /dev/null
+++ b/app/user_verifications/rpc/internal/models/userverifications_update.go
@@ -0,0 +1,366 @@
+// Code generated by ent, DO NOT EDIT.
+
+package models
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "juwan-backend/app/user_verifications/rpc/internal/models/predicate"
+ "juwan-backend/app/user_verifications/rpc/internal/models/schema"
+ "juwan-backend/app/user_verifications/rpc/internal/models/userverifications"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+)
+
+// UserVerificationsUpdate is the builder for updating UserVerifications entities.
+type UserVerificationsUpdate struct {
+ config
+ hooks []Hook
+ mutation *UserVerificationsMutation
+}
+
+// Where appends a list predicates to the UserVerificationsUpdate builder.
+func (_u *UserVerificationsUpdate) Where(ps ...predicate.UserVerifications) *UserVerificationsUpdate {
+ _u.mutation.Where(ps...)
+ return _u
+}
+
+// SetRole sets the "role" field.
+func (_u *UserVerificationsUpdate) SetRole(v string) *UserVerificationsUpdate {
+ _u.mutation.SetRole(v)
+ return _u
+}
+
+// SetNillableRole sets the "role" field if the given value is not nil.
+func (_u *UserVerificationsUpdate) SetNillableRole(v *string) *UserVerificationsUpdate {
+ if v != nil {
+ _u.SetRole(*v)
+ }
+ return _u
+}
+
+// SetStatus sets the "status" field.
+func (_u *UserVerificationsUpdate) SetStatus(v string) *UserVerificationsUpdate {
+ _u.mutation.SetStatus(v)
+ return _u
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (_u *UserVerificationsUpdate) SetNillableStatus(v *string) *UserVerificationsUpdate {
+ if v != nil {
+ _u.SetStatus(*v)
+ }
+ return _u
+}
+
+// SetMaterials sets the "materials" field.
+func (_u *UserVerificationsUpdate) SetMaterials(v schema.MaterialStruct) *UserVerificationsUpdate {
+ _u.mutation.SetMaterials(v)
+ return _u
+}
+
+// SetNillableMaterials sets the "materials" field if the given value is not nil.
+func (_u *UserVerificationsUpdate) SetNillableMaterials(v *schema.MaterialStruct) *UserVerificationsUpdate {
+ if v != nil {
+ _u.SetMaterials(*v)
+ }
+ return _u
+}
+
+// SetRejectReason sets the "reject_reason" field.
+func (_u *UserVerificationsUpdate) SetRejectReason(v string) *UserVerificationsUpdate {
+ _u.mutation.SetRejectReason(v)
+ return _u
+}
+
+// SetNillableRejectReason sets the "reject_reason" field if the given value is not nil.
+func (_u *UserVerificationsUpdate) SetNillableRejectReason(v *string) *UserVerificationsUpdate {
+ if v != nil {
+ _u.SetRejectReason(*v)
+ }
+ return _u
+}
+
+// SetReviewedBy sets the "reviewed_by" field.
+func (_u *UserVerificationsUpdate) SetReviewedBy(v int64) *UserVerificationsUpdate {
+ _u.mutation.ResetReviewedBy()
+ _u.mutation.SetReviewedBy(v)
+ return _u
+}
+
+// SetNillableReviewedBy sets the "reviewed_by" field if the given value is not nil.
+func (_u *UserVerificationsUpdate) SetNillableReviewedBy(v *int64) *UserVerificationsUpdate {
+ if v != nil {
+ _u.SetReviewedBy(*v)
+ }
+ return _u
+}
+
+// AddReviewedBy adds value to the "reviewed_by" field.
+func (_u *UserVerificationsUpdate) AddReviewedBy(v int64) *UserVerificationsUpdate {
+ _u.mutation.AddReviewedBy(v)
+ return _u
+}
+
+// Mutation returns the UserVerificationsMutation object of the builder.
+func (_u *UserVerificationsUpdate) Mutation() *UserVerificationsMutation {
+ return _u.mutation
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (_u *UserVerificationsUpdate) Save(ctx context.Context) (int, error) {
+ return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_u *UserVerificationsUpdate) SaveX(ctx context.Context) int {
+ affected, err := _u.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return affected
+}
+
+// Exec executes the query.
+func (_u *UserVerificationsUpdate) Exec(ctx context.Context) error {
+ _, err := _u.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_u *UserVerificationsUpdate) ExecX(ctx context.Context) {
+ if err := _u.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+func (_u *UserVerificationsUpdate) sqlSave(ctx context.Context) (_node int, err error) {
+ _spec := sqlgraph.NewUpdateSpec(userverifications.Table, userverifications.Columns, sqlgraph.NewFieldSpec(userverifications.FieldID, field.TypeInt64))
+ if ps := _u.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if value, ok := _u.mutation.Role(); ok {
+ _spec.SetField(userverifications.FieldRole, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Status(); ok {
+ _spec.SetField(userverifications.FieldStatus, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Materials(); ok {
+ _spec.SetField(userverifications.FieldMaterials, field.TypeJSON, value)
+ }
+ if value, ok := _u.mutation.RejectReason(); ok {
+ _spec.SetField(userverifications.FieldRejectReason, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.ReviewedBy(); ok {
+ _spec.SetField(userverifications.FieldReviewedBy, field.TypeInt64, value)
+ }
+ if value, ok := _u.mutation.AddedReviewedBy(); ok {
+ _spec.AddField(userverifications.FieldReviewedBy, field.TypeInt64, value)
+ }
+ if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
+ if _, ok := err.(*sqlgraph.NotFoundError); ok {
+ err = &NotFoundError{userverifications.Label}
+ } else if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return 0, err
+ }
+ _u.mutation.done = true
+ return _node, nil
+}
+
+// UserVerificationsUpdateOne is the builder for updating a single UserVerifications entity.
+type UserVerificationsUpdateOne struct {
+ config
+ fields []string
+ hooks []Hook
+ mutation *UserVerificationsMutation
+}
+
+// SetRole sets the "role" field.
+func (_u *UserVerificationsUpdateOne) SetRole(v string) *UserVerificationsUpdateOne {
+ _u.mutation.SetRole(v)
+ return _u
+}
+
+// SetNillableRole sets the "role" field if the given value is not nil.
+func (_u *UserVerificationsUpdateOne) SetNillableRole(v *string) *UserVerificationsUpdateOne {
+ if v != nil {
+ _u.SetRole(*v)
+ }
+ return _u
+}
+
+// SetStatus sets the "status" field.
+func (_u *UserVerificationsUpdateOne) SetStatus(v string) *UserVerificationsUpdateOne {
+ _u.mutation.SetStatus(v)
+ return _u
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (_u *UserVerificationsUpdateOne) SetNillableStatus(v *string) *UserVerificationsUpdateOne {
+ if v != nil {
+ _u.SetStatus(*v)
+ }
+ return _u
+}
+
+// SetMaterials sets the "materials" field.
+func (_u *UserVerificationsUpdateOne) SetMaterials(v schema.MaterialStruct) *UserVerificationsUpdateOne {
+ _u.mutation.SetMaterials(v)
+ return _u
+}
+
+// SetNillableMaterials sets the "materials" field if the given value is not nil.
+func (_u *UserVerificationsUpdateOne) SetNillableMaterials(v *schema.MaterialStruct) *UserVerificationsUpdateOne {
+ if v != nil {
+ _u.SetMaterials(*v)
+ }
+ return _u
+}
+
+// SetRejectReason sets the "reject_reason" field.
+func (_u *UserVerificationsUpdateOne) SetRejectReason(v string) *UserVerificationsUpdateOne {
+ _u.mutation.SetRejectReason(v)
+ return _u
+}
+
+// SetNillableRejectReason sets the "reject_reason" field if the given value is not nil.
+func (_u *UserVerificationsUpdateOne) SetNillableRejectReason(v *string) *UserVerificationsUpdateOne {
+ if v != nil {
+ _u.SetRejectReason(*v)
+ }
+ return _u
+}
+
+// SetReviewedBy sets the "reviewed_by" field.
+func (_u *UserVerificationsUpdateOne) SetReviewedBy(v int64) *UserVerificationsUpdateOne {
+ _u.mutation.ResetReviewedBy()
+ _u.mutation.SetReviewedBy(v)
+ return _u
+}
+
+// SetNillableReviewedBy sets the "reviewed_by" field if the given value is not nil.
+func (_u *UserVerificationsUpdateOne) SetNillableReviewedBy(v *int64) *UserVerificationsUpdateOne {
+ if v != nil {
+ _u.SetReviewedBy(*v)
+ }
+ return _u
+}
+
+// AddReviewedBy adds value to the "reviewed_by" field.
+func (_u *UserVerificationsUpdateOne) AddReviewedBy(v int64) *UserVerificationsUpdateOne {
+ _u.mutation.AddReviewedBy(v)
+ return _u
+}
+
+// Mutation returns the UserVerificationsMutation object of the builder.
+func (_u *UserVerificationsUpdateOne) Mutation() *UserVerificationsMutation {
+ return _u.mutation
+}
+
+// Where appends a list predicates to the UserVerificationsUpdate builder.
+func (_u *UserVerificationsUpdateOne) Where(ps ...predicate.UserVerifications) *UserVerificationsUpdateOne {
+ _u.mutation.Where(ps...)
+ return _u
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (_u *UserVerificationsUpdateOne) Select(field string, fields ...string) *UserVerificationsUpdateOne {
+ _u.fields = append([]string{field}, fields...)
+ return _u
+}
+
+// Save executes the query and returns the updated UserVerifications entity.
+func (_u *UserVerificationsUpdateOne) Save(ctx context.Context) (*UserVerifications, error) {
+ return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_u *UserVerificationsUpdateOne) SaveX(ctx context.Context) *UserVerifications {
+ node, err := _u.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return node
+}
+
+// Exec executes the query on the entity.
+func (_u *UserVerificationsUpdateOne) Exec(ctx context.Context) error {
+ _, err := _u.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_u *UserVerificationsUpdateOne) ExecX(ctx context.Context) {
+ if err := _u.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+func (_u *UserVerificationsUpdateOne) sqlSave(ctx context.Context) (_node *UserVerifications, err error) {
+ _spec := sqlgraph.NewUpdateSpec(userverifications.Table, userverifications.Columns, sqlgraph.NewFieldSpec(userverifications.FieldID, field.TypeInt64))
+ id, ok := _u.mutation.ID()
+ if !ok {
+ return nil, &ValidationError{Name: "id", err: errors.New(`models: missing "UserVerifications.id" for update`)}
+ }
+ _spec.Node.ID.Value = id
+ if fields := _u.fields; len(fields) > 0 {
+ _spec.Node.Columns = make([]string, 0, len(fields))
+ _spec.Node.Columns = append(_spec.Node.Columns, userverifications.FieldID)
+ for _, f := range fields {
+ if !userverifications.ValidColumn(f) {
+ return nil, &ValidationError{Name: f, err: fmt.Errorf("models: invalid field %q for query", f)}
+ }
+ if f != userverifications.FieldID {
+ _spec.Node.Columns = append(_spec.Node.Columns, f)
+ }
+ }
+ }
+ if ps := _u.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if value, ok := _u.mutation.Role(); ok {
+ _spec.SetField(userverifications.FieldRole, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Status(); ok {
+ _spec.SetField(userverifications.FieldStatus, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Materials(); ok {
+ _spec.SetField(userverifications.FieldMaterials, field.TypeJSON, value)
+ }
+ if value, ok := _u.mutation.RejectReason(); ok {
+ _spec.SetField(userverifications.FieldRejectReason, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.ReviewedBy(); ok {
+ _spec.SetField(userverifications.FieldReviewedBy, field.TypeInt64, value)
+ }
+ if value, ok := _u.mutation.AddedReviewedBy(); ok {
+ _spec.AddField(userverifications.FieldReviewedBy, field.TypeInt64, value)
+ }
+ _node = &UserVerifications{config: _u.config}
+ _spec.Assign = _node.assignValues
+ _spec.ScanValues = _node.scanValues
+ if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
+ if _, ok := err.(*sqlgraph.NotFoundError); ok {
+ err = &NotFoundError{userverifications.Label}
+ } else if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return nil, err
+ }
+ _u.mutation.done = true
+ return _node, nil
+}
diff --git a/app/user_verifications/rpc/internal/svc/serviceContext.go b/app/user_verifications/rpc/internal/svc/serviceContext.go
index c49f41b..abfe8bd 100644
--- a/app/user_verifications/rpc/internal/svc/serviceContext.go
+++ b/app/user_verifications/rpc/internal/svc/serviceContext.go
@@ -1,13 +1,55 @@
package svc
-import "juwan-backend/app/user_verifications/rpc/internal/config"
+import (
+ "juwan-backend/app/snowflake/rpc/snowflake"
+ "juwan-backend/app/user_verifications/rpc/internal/config"
+ "juwan-backend/app/user_verifications/rpc/internal/models"
+ "juwan-backend/app/user_verifications/rpc/userverifications"
+ "juwan-backend/common/redisx"
+ "juwan-backend/common/snowflakex"
+ "juwan-backend/pkg/adapter"
+ "time"
+
+ "ariga.io/entcache"
+ "entgo.io/ent/dialect/sql"
+ "github.com/redis/go-redis/v9"
+ "github.com/zeromicro/go-zero/core/logx"
+ "github.com/zeromicro/go-zero/zrpc"
+)
type ServiceContext struct {
- Config config.Config
+ Config config.Config
+ UserVeriModelRW *models.UserVerificationsClient
+ UserVeriModelRO *models.UserVerificationsClient
+ RedisClient *redis.ClusterClient
+ UserVeriRpc userverifications.UserVerificationsZrpcClient
+ SnowflakeRpc snowflake.SnowflakeServiceClient
}
func NewServiceContext(c config.Config) *ServiceContext {
+ RWConn, err := sql.Open("pgx", c.DB.Master)
+ if err != nil {
+ panic(err)
+ }
+ ROConn, err := sql.Open("pgx", c.DB.Slave)
+ if err != nil {
+ panic(err)
+ }
+
+ redisConn, err := redisx.ConnectMasterSlaveCluster(c.CacheConf, 5*time.Second)
+ if err != nil || redisConn == nil {
+ logx.Errorf("redis connect master error: %s", err)
+ panic(err)
+ }
+
+ RWDrv := entcache.NewDriver(RWConn, entcache.TTL(time.Second*30), entcache.Levels(adapter.NewRedisCache(redisConn.Client)))
+ RODrv := entcache.NewDriver(ROConn, entcache.TTL(time.Second*30), entcache.Levels(adapter.NewRedisCache(redisConn.Client)))
return &ServiceContext{
- Config: c,
+ Config: c,
+ UserVeriModelRW: models.NewClient(models.Driver(RWDrv)).UserVerifications,
+ UserVeriModelRO: models.NewClient(models.Driver(RODrv)).UserVerifications,
+ RedisClient: redisConn.Client,
+ UserVeriRpc: userverifications.NewUserVerificationsZrpcClient(zrpc.MustNewClient(c.UserVeriRpcConf)),
+ SnowflakeRpc: snowflakex.NewClient(c.SnowflakeRpcConf),
}
}
diff --git a/app/user_verifications/rpc/internal/svc/userVerificationsModel.go b/app/user_verifications/rpc/internal/svc/userVerificationsModel.go
deleted file mode 100644
index 59c0c67..0000000
--- a/app/user_verifications/rpc/internal/svc/userVerificationsModel.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package svc
-
-import (
- "github.com/zeromicro/go-zero/core/stores/cache"
- "github.com/zeromicro/go-zero/core/stores/sqlx"
-)
-
-var _ UserVerificationsModel = (*customUserVerificationsModel)(nil)
-
-type (
- // UserVerificationsModel is an interface to be customized, add more methods here,
- // and implement the added methods in customUserVerificationsModel.
- UserVerificationsModel interface {
- userVerificationsModel
- }
-
- customUserVerificationsModel struct {
- *defaultUserVerificationsModel
- }
-)
-
-// NewUserVerificationsModel returns a model for the database table.
-func NewUserVerificationsModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserVerificationsModel {
- return &customUserVerificationsModel{
- defaultUserVerificationsModel: newUserVerificationsModel(conn, c, opts...),
- }
-}
diff --git a/app/user_verifications/rpc/internal/svc/userVerificationsModel_gen.go b/app/user_verifications/rpc/internal/svc/userVerificationsModel_gen.go
deleted file mode 100644
index 9385283..0000000
--- a/app/user_verifications/rpc/internal/svc/userVerificationsModel_gen.go
+++ /dev/null
@@ -1,154 +0,0 @@
-// Code generated by goctl. DO NOT EDIT.
-// versions:
-// goctl version: 1.9.2
-
-package svc
-
-import (
- "context"
- "database/sql"
- "fmt"
- "strings"
- "time"
-
- "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 (
- userVerificationsFieldNames = builder.RawFieldNames(&UserVerifications{}, true)
- userVerificationsRows = strings.Join(userVerificationsFieldNames, ",")
- userVerificationsRowsExpectAutoSet = strings.Join(stringx.Remove(userVerificationsFieldNames, "create_at", "create_time", "created_at", "update_at", "update_time", "updated_at"), ",")
- userVerificationsRowsWithPlaceHolder = builder.PostgreSqlJoin(stringx.Remove(userVerificationsFieldNames, "id", "create_at", "create_time", "created_at", "update_at", "update_time", "updated_at"))
-
- cachePublicUserVerificationsIdPrefix = "cache:public:userVerifications:id:"
- cachePublicUserVerificationsUserIdRolePrefix = "cache:public:userVerifications:userId:role:"
-)
-
-type (
- userVerificationsModel interface {
- Insert(ctx context.Context, data *UserVerifications) (sql.Result, error)
- FindOne(ctx context.Context, id int64) (*UserVerifications, error)
- FindOneByUserIdRole(ctx context.Context, userId int64, role string) (*UserVerifications, error)
- Update(ctx context.Context, data *UserVerifications) error
- Delete(ctx context.Context, id int64) error
- }
-
- defaultUserVerificationsModel struct {
- sqlc.CachedConn
- table string
- }
-
- UserVerifications struct {
- Id int64 `db:"id"`
- UserId int64 `db:"user_id"`
- Role string `db:"role"`
- Status string `db:"status"`
- Materials string `db:"materials"`
- RejectReason sql.NullString `db:"reject_reason"`
- ReviewedBy sql.NullInt64 `db:"reviewed_by"`
- ReviewedAt sql.NullTime `db:"reviewed_at"`
- CreatedAt time.Time `db:"created_at"`
- UpdatedAt time.Time `db:"updated_at"`
- }
-)
-
-func newUserVerificationsModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultUserVerificationsModel {
- return &defaultUserVerificationsModel{
- CachedConn: sqlc.NewConn(conn, c, opts...),
- table: `"public"."user_verifications"`,
- }
-}
-
-func (m *defaultUserVerificationsModel) Delete(ctx context.Context, id int64) error {
- data, err := m.FindOne(ctx, id)
- if err != nil {
- return err
- }
-
- publicUserVerificationsIdKey := fmt.Sprintf("%s%v", cachePublicUserVerificationsIdPrefix, id)
- publicUserVerificationsUserIdRoleKey := fmt.Sprintf("%s%v:%v", cachePublicUserVerificationsUserIdRolePrefix, data.UserId, data.Role)
- _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
- query := fmt.Sprintf("delete from %s where id = $1", m.table)
- return conn.ExecCtx(ctx, query, id)
- }, publicUserVerificationsIdKey, publicUserVerificationsUserIdRoleKey)
- return err
-}
-
-func (m *defaultUserVerificationsModel) FindOne(ctx context.Context, id int64) (*UserVerifications, error) {
- publicUserVerificationsIdKey := fmt.Sprintf("%s%v", cachePublicUserVerificationsIdPrefix, id)
- var resp UserVerifications
- err := m.QueryRowCtx(ctx, &resp, publicUserVerificationsIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error {
- query := fmt.Sprintf("select %s from %s where id = $1 limit 1", userVerificationsRows, 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 *defaultUserVerificationsModel) FindOneByUserIdRole(ctx context.Context, userId int64, role string) (*UserVerifications, error) {
- publicUserVerificationsUserIdRoleKey := fmt.Sprintf("%s%v:%v", cachePublicUserVerificationsUserIdRolePrefix, userId, role)
- var resp UserVerifications
- err := m.QueryRowIndexCtx(ctx, &resp, publicUserVerificationsUserIdRoleKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) {
- query := fmt.Sprintf("select %s from %s where user_id = $1 and role = $2 limit 1", userVerificationsRows, m.table)
- if err := conn.QueryRowCtx(ctx, &resp, query, userId, role); 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 *defaultUserVerificationsModel) Insert(ctx context.Context, data *UserVerifications) (sql.Result, error) {
- publicUserVerificationsIdKey := fmt.Sprintf("%s%v", cachePublicUserVerificationsIdPrefix, data.Id)
- publicUserVerificationsUserIdRoleKey := fmt.Sprintf("%s%v:%v", cachePublicUserVerificationsUserIdRolePrefix, data.UserId, data.Role)
- 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 ($1, $2, $3, $4, $5, $6, $7, $8)", m.table, userVerificationsRowsExpectAutoSet)
- return conn.ExecCtx(ctx, query, data.Id, data.UserId, data.Role, data.Status, data.Materials, data.RejectReason, data.ReviewedBy, data.ReviewedAt)
- }, publicUserVerificationsIdKey, publicUserVerificationsUserIdRoleKey)
- return ret, err
-}
-
-func (m *defaultUserVerificationsModel) Update(ctx context.Context, newData *UserVerifications) error {
- data, err := m.FindOne(ctx, newData.Id)
- if err != nil {
- return err
- }
-
- publicUserVerificationsIdKey := fmt.Sprintf("%s%v", cachePublicUserVerificationsIdPrefix, data.Id)
- publicUserVerificationsUserIdRoleKey := fmt.Sprintf("%s%v:%v", cachePublicUserVerificationsUserIdRolePrefix, data.UserId, data.Role)
- _, 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 = $1", m.table, userVerificationsRowsWithPlaceHolder)
- return conn.ExecCtx(ctx, query, newData.Id, newData.UserId, newData.Role, newData.Status, newData.Materials, newData.RejectReason, newData.ReviewedBy, newData.ReviewedAt)
- }, publicUserVerificationsIdKey, publicUserVerificationsUserIdRoleKey)
- return err
-}
-
-func (m *defaultUserVerificationsModel) formatPrimary(primary any) string {
- return fmt.Sprintf("%s%v", cachePublicUserVerificationsIdPrefix, primary)
-}
-
-func (m *defaultUserVerificationsModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error {
- query := fmt.Sprintf("select %s from %s where id = $1 limit 1", userVerificationsRows, m.table)
- return conn.QueryRowCtx(ctx, v, query, primary)
-}
-
-func (m *defaultUserVerificationsModel) tableName() string {
- return m.table
-}
diff --git a/app/user_verifications/rpc/internal/svc/vars.go b/app/user_verifications/rpc/internal/svc/vars.go
deleted file mode 100644
index e3c6697..0000000
--- a/app/user_verifications/rpc/internal/svc/vars.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package svc
-
-import "github.com/zeromicro/go-zero/core/stores/sqlx"
-
-var ErrNotFound = sqlx.ErrNotFound
diff --git a/app/user_verifications/rpc/pb.go b/app/user_verifications/rpc/pb.go
index bef17af..119e7c2 100644
--- a/app/user_verifications/rpc/pb.go
+++ b/app/user_verifications/rpc/pb.go
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
- conf.MustLoad(*configFile, &c)
+ conf.MustLoad(*configFile, &c, conf.UseEnv())
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
diff --git a/app/user_verifications/rpc/pb/user_verifications.pb.go b/app/user_verifications/rpc/pb/user_verifications.pb.go
index 6ad5107..fcdd59c 100644
--- a/app/user_verifications/rpc/pb/user_verifications.pb.go
+++ b/app/user_verifications/rpc/pb/user_verifications.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
-// protoc v5.29.6
+// protoc v3.19.4
// source: user_verifications.proto
package pb
@@ -284,16 +284,16 @@ func (*AddUserVerificationsResp) Descriptor() ([]byte, []int) {
type UpdateUserVerificationsReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
- Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` //id
- UserId int64 `protobuf:"varint,2,opt,name=userId,proto3" json:"userId,omitempty"` //userId
- Role string `protobuf:"bytes,3,opt,name=role,proto3" json:"role,omitempty"` //role
- Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` //status
- Materials string `protobuf:"bytes,5,opt,name=materials,proto3" json:"materials,omitempty"` //materials
- RejectReason string `protobuf:"bytes,6,opt,name=rejectReason,proto3" json:"rejectReason,omitempty"` //rejectReason
- ReviewedBy int64 `protobuf:"varint,7,opt,name=reviewedBy,proto3" json:"reviewedBy,omitempty"` //reviewedBy
- ReviewedAt int64 `protobuf:"varint,8,opt,name=reviewedAt,proto3" json:"reviewedAt,omitempty"` //reviewedAt
- CreatedAt int64 `protobuf:"varint,9,opt,name=createdAt,proto3" json:"createdAt,omitempty"` //createdAt
- UpdatedAt int64 `protobuf:"varint,10,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` //updatedAt
+ Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` //id
+ UserId *int64 `protobuf:"varint,2,opt,name=userId,proto3,oneof" json:"userId,omitempty"` //userId
+ Role *string `protobuf:"bytes,3,opt,name=role,proto3,oneof" json:"role,omitempty"` //role
+ Status *string `protobuf:"bytes,4,opt,name=status,proto3,oneof" json:"status,omitempty"` //status
+ Materials *string `protobuf:"bytes,5,opt,name=materials,proto3,oneof" json:"materials,omitempty"` //materials
+ RejectReason *string `protobuf:"bytes,6,opt,name=rejectReason,proto3,oneof" json:"rejectReason,omitempty"` //rejectReason
+ ReviewedBy int64 `protobuf:"varint,7,opt,name=reviewedBy,proto3" json:"reviewedBy,omitempty"` //reviewedBy
+ ReviewedAt int64 `protobuf:"varint,8,opt,name=reviewedAt,proto3" json:"reviewedAt,omitempty"` //reviewedAt
+ CreatedAt int64 `protobuf:"varint,9,opt,name=createdAt,proto3" json:"createdAt,omitempty"` //createdAt
+ UpdatedAt int64 `protobuf:"varint,10,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` //updatedAt
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -336,36 +336,36 @@ func (x *UpdateUserVerificationsReq) GetId() int64 {
}
func (x *UpdateUserVerificationsReq) GetUserId() int64 {
- if x != nil {
- return x.UserId
+ if x != nil && x.UserId != nil {
+ return *x.UserId
}
return 0
}
func (x *UpdateUserVerificationsReq) GetRole() string {
- if x != nil {
- return x.Role
+ if x != nil && x.Role != nil {
+ return *x.Role
}
return ""
}
func (x *UpdateUserVerificationsReq) GetStatus() string {
- if x != nil {
- return x.Status
+ if x != nil && x.Status != nil {
+ return *x.Status
}
return ""
}
func (x *UpdateUserVerificationsReq) GetMaterials() string {
- if x != nil {
- return x.Materials
+ if x != nil && x.Materials != nil {
+ return *x.Materials
}
return ""
}
func (x *UpdateUserVerificationsReq) GetRejectReason() string {
- if x != nil {
- return x.RejectReason
+ if x != nil && x.RejectReason != nil {
+ return *x.RejectReason
}
return ""
}
@@ -813,14 +813,14 @@ const file_user_verifications_proto_rawDesc = "" +
"reviewedAt\x12\x1c\n" +
"\tcreatedAt\x18\b \x01(\x03R\tcreatedAt\x12\x1c\n" +
"\tupdatedAt\x18\t \x01(\x03R\tupdatedAt\"\x1a\n" +
- "\x18AddUserVerificationsResp\"\xae\x02\n" +
+ "\x18AddUserVerificationsResp\"\x85\x03\n" +
"\x1aUpdateUserVerificationsReq\x12\x0e\n" +
- "\x02id\x18\x01 \x01(\x03R\x02id\x12\x16\n" +
- "\x06userId\x18\x02 \x01(\x03R\x06userId\x12\x12\n" +
- "\x04role\x18\x03 \x01(\tR\x04role\x12\x16\n" +
- "\x06status\x18\x04 \x01(\tR\x06status\x12\x1c\n" +
- "\tmaterials\x18\x05 \x01(\tR\tmaterials\x12\"\n" +
- "\frejectReason\x18\x06 \x01(\tR\frejectReason\x12\x1e\n" +
+ "\x02id\x18\x01 \x01(\x03R\x02id\x12\x1b\n" +
+ "\x06userId\x18\x02 \x01(\x03H\x00R\x06userId\x88\x01\x01\x12\x17\n" +
+ "\x04role\x18\x03 \x01(\tH\x01R\x04role\x88\x01\x01\x12\x1b\n" +
+ "\x06status\x18\x04 \x01(\tH\x02R\x06status\x88\x01\x01\x12!\n" +
+ "\tmaterials\x18\x05 \x01(\tH\x03R\tmaterials\x88\x01\x01\x12'\n" +
+ "\frejectReason\x18\x06 \x01(\tH\x04R\frejectReason\x88\x01\x01\x12\x1e\n" +
"\n" +
"reviewedBy\x18\a \x01(\x03R\n" +
"reviewedBy\x12\x1e\n" +
@@ -829,7 +829,13 @@ const file_user_verifications_proto_rawDesc = "" +
"reviewedAt\x12\x1c\n" +
"\tcreatedAt\x18\t \x01(\x03R\tcreatedAt\x12\x1c\n" +
"\tupdatedAt\x18\n" +
- " \x01(\x03R\tupdatedAt\"\x1d\n" +
+ " \x01(\x03R\tupdatedAtB\t\n" +
+ "\a_userIdB\a\n" +
+ "\x05_roleB\t\n" +
+ "\a_statusB\f\n" +
+ "\n" +
+ "_materialsB\x0f\n" +
+ "\r_rejectReason\"\x1d\n" +
"\x1bUpdateUserVerificationsResp\")\n" +
"\x17DelUserVerificationsReq\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\"\x1a\n" +
@@ -916,6 +922,7 @@ func file_user_verifications_proto_init() {
if File_user_verifications_proto != nil {
return
}
+ file_user_verifications_proto_msgTypes[3].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
diff --git a/app/user_verifications/rpc/pb/user_verifications_grpc.pb.go b/app/user_verifications/rpc/pb/user_verifications_grpc.pb.go
index 020be6a..6d3b409 100644
--- a/app/user_verifications/rpc/pb/user_verifications_grpc.pb.go
+++ b/app/user_verifications/rpc/pb/user_verifications_grpc.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
-// - protoc v5.29.6
+// - protoc v3.19.4
// source: user_verifications.proto
package pb
diff --git a/app/users/api/etc/user-api.yaml b/app/users/api/etc/user-api.yaml
index 93d74f3..9ec0dfa 100644
--- a/app/users/api/etc/user-api.yaml
+++ b/app/users/api/etc/user-api.yaml
@@ -7,8 +7,9 @@ Prometheus:
Port: 4001
Path: /metrics
-UsercenterRpcConf:
- Target: k8s://juwan/user-rpc-svc:9001
SnowflakeRpcConf:
Target: k8s://juwan/snowflake-svc:8080
+
+UserVerificationRpc:
+ Target: k8s://juwan/user_verifications-svc:8080
diff --git a/app/users/api/internal/config/config.go b/app/users/api/internal/config/config.go
index d864b93..da8c048 100644
--- a/app/users/api/internal/config/config.go
+++ b/app/users/api/internal/config/config.go
@@ -10,6 +10,6 @@ import (
type Config struct {
rest.RestConf
- UsercenterRpcConf zrpc.RpcClientConf
- SnowflakeRpcConf zrpc.RpcClientConf
+ UsercenterRpcConf zrpc.RpcClientConf
+ UserVerificationRpc zrpc.RpcClientConf
}
diff --git a/app/users/api/internal/handler/auth/forgotPasswordHandler.go b/app/users/api/internal/handler/auth/forgotPasswordHandler.go
new file mode 100644
index 0000000..802b7e0
--- /dev/null
+++ b/app/users/api/internal/handler/auth/forgotPasswordHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package auth
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/users/api/internal/logic/auth"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+)
+
+// 忘记密码-发送验证码
+func ForgotPasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.ForgotPasswordReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := auth.NewForgotPasswordLogic(r.Context(), svcCtx)
+ resp, err := l.ForgotPassword(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/user/loginHandler.go b/app/users/api/internal/handler/auth/loginHandler.go
similarity index 76%
rename from app/users/api/internal/handler/user/loginHandler.go
rename to app/users/api/internal/handler/auth/loginHandler.go
index ef7032b..fbb671c 100644
--- a/app/users/api/internal/handler/user/loginHandler.go
+++ b/app/users/api/internal/handler/auth/loginHandler.go
@@ -1,19 +1,19 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
-package user
+package auth
import (
- "juwan-backend/app/users/api/internal/logic/user"
+ "net/http"
+
+ "juwan-backend/app/users/api/internal/logic/auth"
"juwan-backend/app/users/api/internal/svc"
"juwan-backend/app/users/api/internal/types"
- "juwan-backend/common/utils"
- "net/http"
"github.com/zeromicro/go-zero/rest/httpx"
)
-// 用户登录接口
+// 用户登录
func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.LoginReq
@@ -22,14 +22,13 @@ func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return
}
- l := user.NewLoginLogic(r.Context(), svcCtx)
+ l := auth.NewLoginLogic(r.Context(), svcCtx)
resp, err := l.Login(&req)
-
if err != nil {
- httpx.ErrorCtx(r.Context(), w, utils.NewErrorResp(400, err))
+ httpx.ErrorCtx(r.Context(), w, err)
} else {
- token := resp.Token
- resp.Token = ""
+ token := resp.RefreshToken
+ resp.RefreshToken = ""
http.SetCookie(w, &http.Cookie{
Name: "JToken",
Value: token,
diff --git a/app/users/api/internal/handler/user/logoutHandler.go b/app/users/api/internal/handler/auth/logoutHandler.go
similarity index 77%
rename from app/users/api/internal/handler/user/logoutHandler.go
rename to app/users/api/internal/handler/auth/logoutHandler.go
index 1dd45f1..4420138 100644
--- a/app/users/api/internal/handler/user/logoutHandler.go
+++ b/app/users/api/internal/handler/auth/logoutHandler.go
@@ -1,18 +1,19 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
-package user
+package auth
import (
"net/http"
- "github.com/zeromicro/go-zero/rest/httpx"
- "juwan-backend/app/users/api/internal/logic/user"
+ "juwan-backend/app/users/api/internal/logic/auth"
"juwan-backend/app/users/api/internal/svc"
"juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
)
-// 用户登出
+// 退出登录
func LogoutHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.LogoutReq
@@ -21,7 +22,8 @@ func LogoutHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return
}
- l := user.NewLogoutLogic(r.Context(), svcCtx)
+ // TODO: add userId from http header x-auth-user-id
+ l := auth.NewLogoutLogic(r.Context(), svcCtx)
resp, err := l.Logout(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
diff --git a/app/users/api/internal/handler/auth/registerHandler.go b/app/users/api/internal/handler/auth/registerHandler.go
new file mode 100644
index 0000000..13d96e1
--- /dev/null
+++ b/app/users/api/internal/handler/auth/registerHandler.go
@@ -0,0 +1,48 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package auth
+
+import (
+ "net/http"
+
+ "juwan-backend/app/users/api/internal/logic/auth"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// 用户注册
+func RegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.RegisterReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := auth.NewRegisterLogic(r.Context(), svcCtx)
+ resp, err := l.Register(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ token := resp.RefreshToken
+ resp.RefreshToken = ""
+ http.SetCookie(w, &http.Cookie{
+ Name: "JToken",
+ Value: token,
+ Quoted: false,
+ Path: "/",
+ Domain: "",
+ RawExpires: "",
+ MaxAge: 691200,
+ Secure: false,
+ HttpOnly: true,
+ SameSite: http.SameSiteStrictMode,
+ Partitioned: false,
+ })
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/auth/resetPasswordHandler.go b/app/users/api/internal/handler/auth/resetPasswordHandler.go
new file mode 100644
index 0000000..8733e23
--- /dev/null
+++ b/app/users/api/internal/handler/auth/resetPasswordHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package auth
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/users/api/internal/logic/auth"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+)
+
+// 重置密码
+func ResetPasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.ResetPasswordReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := auth.NewResetPasswordLogic(r.Context(), svcCtx)
+ resp, err := l.ResetPassword(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/routes.go b/app/users/api/internal/handler/routes.go
index ea2b386..578bf8d 100644
--- a/app/users/api/internal/handler/routes.go
+++ b/app/users/api/internal/handler/routes.go
@@ -6,7 +6,10 @@ package handler
import (
"net/http"
+ auth "juwan-backend/app/users/api/internal/handler/auth"
user "juwan-backend/app/users/api/internal/handler/user"
+ verification_admin "juwan-backend/app/users/api/internal/handler/verification_admin"
+ verification_user "juwan-backend/app/users/api/internal/handler/verification_user"
"juwan-backend/app/users/api/internal/svc"
"github.com/zeromicro/go-zero/rest"
@@ -14,41 +17,44 @@ import (
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
- rest.WithMiddlewares(
- []rest.Middleware{serverCtx.Logger},
- []rest.Route{
- {
- // 用户登出
- Method: http.MethodPost,
- Path: "/:userId/logout",
- Handler: user.LogoutHandler(serverCtx),
- },
- {
- // 修改用户密码
- Method: http.MethodPut,
- Path: "/:userId/password",
- Handler: user.UpdatePasswordHandler(serverCtx),
- },
- {
- // 修改密码-使用验证码
- Method: http.MethodPut,
- Path: "/forgot-password/reset",
- Handler: user.UpdatePasswordByVcodeHandler(serverCtx),
- },
- {
- // 用户登录接口
- Method: http.MethodPost,
- Path: "/login",
- Handler: user.LoginHandler(serverCtx),
- },
- {
- // 用户注册接口
- Method: http.MethodPost,
- Path: "/register",
- Handler: user.RegisterHandler(serverCtx),
- },
- }...,
- ),
+ []rest.Route{
+ {
+ // 忘记密码-发送验证码
+ Method: http.MethodPost,
+ Path: "/forgot-password",
+ Handler: auth.ForgotPasswordHandler(serverCtx),
+ },
+ {
+ // 用户登录
+ Method: http.MethodPost,
+ Path: "/login",
+ Handler: auth.LoginHandler(serverCtx),
+ },
+ {
+ // 用户注册
+ Method: http.MethodPost,
+ Path: "/register",
+ Handler: auth.RegisterHandler(serverCtx),
+ },
+ {
+ // 重置密码
+ Method: http.MethodPost,
+ Path: "/reset-password",
+ Handler: auth.ResetPasswordHandler(serverCtx),
+ },
+ },
+ rest.WithPrefix("/api/v1/auth"),
+ )
+
+ server.AddRoutes(
+ []rest.Route{
+ {
+ // 退出登录
+ Method: http.MethodPost,
+ Path: "/logout",
+ Handler: auth.LogoutHandler(serverCtx),
+ },
+ },
rest.WithPrefix("/api/v1/auth"),
)
@@ -57,16 +63,16 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
[]rest.Middleware{serverCtx.Logger},
[]rest.Route{
{
- // 获取用户信息
- Method: http.MethodGet,
- Path: "/:userId",
- Handler: user.GetUserInfoHandler(serverCtx),
+ // 关注用户
+ Method: http.MethodPost,
+ Path: "/:id/follow",
+ Handler: user.FollowUserHandler(serverCtx),
},
{
- // 修改用户信息
- Method: http.MethodPut,
- Path: "/:userId",
- Handler: user.UpdateUserInfoHandler(serverCtx),
+ // 取消关注用户
+ Method: http.MethodDelete,
+ Path: "/:id/follow",
+ Handler: user.UnfollowUserHandler(serverCtx),
},
{
// 获取当前登录用户信息
@@ -75,13 +81,88 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Handler: user.GetMeHandler(serverCtx),
},
{
- // 更改当前登录用户信息
+ // 更新个人资料
Method: http.MethodPut,
Path: "/me",
Handler: user.UpdateMeHandler(serverCtx),
},
+ {
+ // 更新通知偏好
+ Method: http.MethodPut,
+ Path: "/me/preferences/notifications",
+ Handler: user.UpdateNotificationSettingsHandler(serverCtx),
+ },
+ {
+ // 更新主题偏好
+ Method: http.MethodPut,
+ Path: "/me/preferences/theme",
+ Handler: user.UpdateThemeSettingsHandler(serverCtx),
+ },
+ {
+ // 切换当前激活角色
+ Method: http.MethodPost,
+ Path: "/me/switch-role",
+ Handler: user.SwitchRoleHandler(serverCtx),
+ },
}...,
),
- rest.WithPrefix("/api/v1/user"),
+ rest.WithPrefix("/api/v1/users"),
+ )
+
+ server.AddRoutes(
+ []rest.Route{
+ {
+ // 获取指定用户信息
+ Method: http.MethodGet,
+ Path: "/:id",
+ Handler: user.GetUserInfoHandler(serverCtx),
+ },
+ },
+ rest.WithPrefix("/api/v1/users"),
+ )
+
+ server.AddRoutes(
+ []rest.Route{
+ {
+ // 管理员获取认证申请列表 (分页)
+ Method: http.MethodGet,
+ Path: "/verifications",
+ Handler: verification_admin.GetVerificationsHandler(serverCtx),
+ },
+ {
+ // 管理员通过申请
+ Method: http.MethodPost,
+ Path: "/verifications/:id/approve",
+ Handler: verification_admin.ApproveVerificationHandler(serverCtx),
+ },
+ {
+ // 管理员驳回申请
+ Method: http.MethodPost,
+ Path: "/verifications/:id/reject",
+ Handler: verification_admin.RejectVerificationHandler(serverCtx),
+ },
+ },
+ rest.WithPrefix("/api/v1/admin"),
+ )
+
+ server.AddRoutes(
+ rest.WithMiddlewares(
+ []rest.Middleware{serverCtx.Logger},
+ []rest.Route{
+ {
+ // 提交或修改角色认证申请 (支持幂等更新)
+ Method: http.MethodPost,
+ Path: "/me/verification",
+ Handler: verification_user.ApplyVerificationHandler(serverCtx),
+ },
+ {
+ // 获取我的所有认证状态
+ Method: http.MethodGet,
+ Path: "/me/verification",
+ Handler: verification_user.GetMyVerificationsHandler(serverCtx),
+ },
+ }...,
+ ),
+ rest.WithPrefix("/api/v1/users"),
)
}
diff --git a/app/users/api/internal/handler/user/updatePasswordHandler.go b/app/users/api/internal/handler/user/followUserHandler.go
similarity index 71%
rename from app/users/api/internal/handler/user/updatePasswordHandler.go
rename to app/users/api/internal/handler/user/followUserHandler.go
index db1b50c..aebb470 100644
--- a/app/users/api/internal/handler/user/updatePasswordHandler.go
+++ b/app/users/api/internal/handler/user/followUserHandler.go
@@ -12,17 +12,17 @@ import (
"juwan-backend/app/users/api/internal/types"
)
-// 修改用户密码
-func UpdatePasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+// 关注用户
+func FollowUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
- var req types.UpdatePasswordReq
+ var req types.FollowUserReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
- l := user.NewUpdatePasswordLogic(r.Context(), svcCtx)
- resp, err := l.UpdatePassword(&req)
+ l := user.NewFollowUserLogic(r.Context(), svcCtx)
+ resp, err := l.FollowUser(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
diff --git a/app/users/api/internal/handler/user/registerHandler.go b/app/users/api/internal/handler/user/registerHandler.go
deleted file mode 100644
index affba01..0000000
--- a/app/users/api/internal/handler/user/registerHandler.go
+++ /dev/null
@@ -1,96 +0,0 @@
-// Code scaffolded by goctl. Safe to edit.
-// goctl 1.9.2
-
-package user
-
-import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "juwan-backend/app/users/api/internal/contextx"
- "juwan-backend/common/utils"
- "net/http"
- "strconv"
-
- "juwan-backend/app/users/api/internal/logic/user"
- "juwan-backend/app/users/api/internal/svc"
- "juwan-backend/app/users/api/internal/types"
-
- "github.com/zeromicro/go-zero/rest/httpx"
-)
-
-// 用户注册接口
-func RegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- if err := normalizeRegisterBody(r); err != nil {
- httpx.ErrorCtx(r.Context(), w, err)
- return
- }
-
- var req types.RegisterReq
- if err := httpx.Parse(r, &req); err != nil {
- httpx.ErrorCtx(r.Context(), w, err)
- return
- }
-
- requestId := r.Header.Get("X-Request-ID")
- //regCtx := context.WithValue(r.Context(), "request_id", requestId)
- regCtx := contextx.WithRequestId(r.Context(), requestId)
- if requestId == "" {
- httpx.ErrorCtx(r.Context(), w, errors.New("bad request"))
- }
-
- l := user.NewRegisterLogic(regCtx, svcCtx)
- resp, err := l.Register(&req)
-
- if err != nil {
- httpx.ErrorCtx(r.Context(), w, utils.NewErrorResp(400, err))
- } else {
- httpx.OkJsonCtx(r.Context(), w, resp)
- }
- }
-}
-
-func normalizeRegisterBody(r *http.Request) error {
- body, err := io.ReadAll(r.Body)
- if err != nil {
- return err
- }
- defer r.Body.Close()
-
- if len(body) == 0 {
- r.Body = io.NopCloser(bytes.NewReader(body))
- return nil
- }
-
- var payload map[string]any
- if err := json.Unmarshal(body, &payload); err != nil {
- r.Body = io.NopCloser(bytes.NewReader(body))
- return nil
- }
-
- vcode, exists := payload["vcode"]
- if exists {
- switch value := vcode.(type) {
- case string:
- parsed, convErr := strconv.Atoi(value)
- if convErr != nil {
- return fmt.Errorf("invalid vcode format")
- }
- payload["vcode"] = parsed
- case float64:
- payload["vcode"] = int(value)
- }
- }
-
- normalized, err := json.Marshal(payload)
- if err != nil {
- return err
- }
-
- r.Body = io.NopCloser(bytes.NewReader(normalized))
- r.ContentLength = int64(len(normalized))
- return nil
-}
diff --git a/app/users/api/internal/handler/user/updatePasswordByVcodeHandler.go b/app/users/api/internal/handler/user/switchRoleHandler.go
similarity index 68%
rename from app/users/api/internal/handler/user/updatePasswordByVcodeHandler.go
rename to app/users/api/internal/handler/user/switchRoleHandler.go
index 189ab43..2e31ee1 100644
--- a/app/users/api/internal/handler/user/updatePasswordByVcodeHandler.go
+++ b/app/users/api/internal/handler/user/switchRoleHandler.go
@@ -12,17 +12,17 @@ import (
"juwan-backend/app/users/api/internal/types"
)
-// 修改密码-使用验证码
-func UpdatePasswordByVcodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+// 切换当前激活角色
+func SwitchRoleHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
- var req types.ResetPasswordByVcode
+ var req types.SwitchRoleReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
- l := user.NewUpdatePasswordByVcodeLogic(r.Context(), svcCtx)
- resp, err := l.UpdatePasswordByVcode(&req)
+ l := user.NewSwitchRoleLogic(r.Context(), svcCtx)
+ resp, err := l.SwitchRole(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
diff --git a/app/users/api/internal/handler/user/unfollowUserHandler.go b/app/users/api/internal/handler/user/unfollowUserHandler.go
new file mode 100644
index 0000000..7d7161b
--- /dev/null
+++ b/app/users/api/internal/handler/user/unfollowUserHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/users/api/internal/logic/user"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+)
+
+// 取消关注用户
+func UnfollowUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.UnfollowUserReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := user.NewUnfollowUserLogic(r.Context(), svcCtx)
+ resp, err := l.UnfollowUser(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/user/updateNotificationSettingsHandler.go b/app/users/api/internal/handler/user/updateNotificationSettingsHandler.go
new file mode 100644
index 0000000..5b8c3dd
--- /dev/null
+++ b/app/users/api/internal/handler/user/updateNotificationSettingsHandler.go
@@ -0,0 +1,33 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "net/http"
+
+ "juwan-backend/app/users/api/internal/logic/user"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// 更新通知偏好
+func UpdateNotificationSettingsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.UpdateNotifySettingsReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := user.NewUpdateNotificationSettingsLogic(r.Context(), svcCtx)
+ resp, err := l.UpdateNotificationSettings(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/user/updateThemeSettingsHandler.go b/app/users/api/internal/handler/user/updateThemeSettingsHandler.go
new file mode 100644
index 0000000..3c4d0fb
--- /dev/null
+++ b/app/users/api/internal/handler/user/updateThemeSettingsHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/users/api/internal/logic/user"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+)
+
+// 更新主题偏好
+func UpdateThemeSettingsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.UpdateThemeSettingsReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := user.NewUpdateThemeSettingsLogic(r.Context(), svcCtx)
+ resp, err := l.UpdateThemeSettings(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/verification_admin/approveVerificationHandler.go b/app/users/api/internal/handler/verification_admin/approveVerificationHandler.go
new file mode 100644
index 0000000..f9e0c1e
--- /dev/null
+++ b/app/users/api/internal/handler/verification_admin/approveVerificationHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_admin
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/users/api/internal/logic/verification_admin"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+)
+
+// 管理员通过申请
+func ApproveVerificationHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.VerificationIdReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := verification_admin.NewApproveVerificationLogic(r.Context(), svcCtx)
+ resp, err := l.ApproveVerification(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/verification_admin/getVerificationsHandler.go b/app/users/api/internal/handler/verification_admin/getVerificationsHandler.go
new file mode 100644
index 0000000..4f1247f
--- /dev/null
+++ b/app/users/api/internal/handler/verification_admin/getVerificationsHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_admin
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/users/api/internal/logic/verification_admin"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+)
+
+// 管理员获取认证申请列表 (分页)
+func GetVerificationsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.GetPendingListReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := verification_admin.NewGetVerificationsLogic(r.Context(), svcCtx)
+ resp, err := l.GetVerifications(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/verification_admin/rejectVerificationHandler.go b/app/users/api/internal/handler/verification_admin/rejectVerificationHandler.go
new file mode 100644
index 0000000..932ef74
--- /dev/null
+++ b/app/users/api/internal/handler/verification_admin/rejectVerificationHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_admin
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/users/api/internal/logic/verification_admin"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+)
+
+// 管理员驳回申请
+func RejectVerificationHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.RejectVerificationReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := verification_admin.NewRejectVerificationLogic(r.Context(), svcCtx)
+ resp, err := l.RejectVerification(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/verification_user/applyVerificationHandler.go b/app/users/api/internal/handler/verification_user/applyVerificationHandler.go
new file mode 100644
index 0000000..fa92226
--- /dev/null
+++ b/app/users/api/internal/handler/verification_user/applyVerificationHandler.go
@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_user
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/users/api/internal/logic/verification_user"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+)
+
+// 提交或修改角色认证申请 (支持幂等更新)
+func ApplyVerificationHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ var req types.ApplyVerificationReq
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := verification_user.NewApplyVerificationLogic(r.Context(), svcCtx)
+ resp, err := l.ApplyVerification(&req)
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/handler/verification_user/getMyVerificationsHandler.go b/app/users/api/internal/handler/verification_user/getMyVerificationsHandler.go
new file mode 100644
index 0000000..86846d8
--- /dev/null
+++ b/app/users/api/internal/handler/verification_user/getMyVerificationsHandler.go
@@ -0,0 +1,25 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_user
+
+import (
+ "net/http"
+
+ "github.com/zeromicro/go-zero/rest/httpx"
+ "juwan-backend/app/users/api/internal/logic/verification_user"
+ "juwan-backend/app/users/api/internal/svc"
+)
+
+// 获取我的所有认证状态
+func GetMyVerificationsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ l := verification_user.NewGetMyVerificationsLogic(r.Context(), svcCtx)
+ resp, err := l.GetMyVerifications()
+ if err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ } else {
+ httpx.OkJsonCtx(r.Context(), w, resp)
+ }
+ }
+}
diff --git a/app/users/api/internal/logic/auth/forgotPasswordLogic.go b/app/users/api/internal/logic/auth/forgotPasswordLogic.go
new file mode 100644
index 0000000..49ead4f
--- /dev/null
+++ b/app/users/api/internal/logic/auth/forgotPasswordLogic.go
@@ -0,0 +1,33 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package auth
+
+import (
+ "context"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type ForgotPasswordLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 忘记密码-发送验证码
+func NewForgotPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ForgotPasswordLogic {
+ return &ForgotPasswordLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *ForgotPasswordLogic) ForgotPassword(req *types.ForgotPasswordReq) (resp *types.EmptyResp, err error) {
+ // todo: add your logic here and delete this line
+
+ return
+}
diff --git a/app/users/api/internal/logic/user/loginLogic.go b/app/users/api/internal/logic/auth/loginLogic.go
similarity index 87%
rename from app/users/api/internal/logic/user/loginLogic.go
rename to app/users/api/internal/logic/auth/loginLogic.go
index 1d12dca..c54d14a 100644
--- a/app/users/api/internal/logic/user/loginLogic.go
+++ b/app/users/api/internal/logic/auth/loginLogic.go
@@ -1,15 +1,15 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
-package user
+package auth
import (
"context"
"errors"
+ "juwan-backend/app/users/rpc/usercenter"
+
"juwan-backend/app/users/api/internal/svc"
"juwan-backend/app/users/api/internal/types"
- "juwan-backend/app/users/rpc/usercenter"
- "time"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -20,7 +20,7 @@ type LoginLogic struct {
svcCtx *svc.ServiceContext
}
-// 用户登录接口
+// 用户登录
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
return &LoginLogic{
Logger: logx.WithContext(ctx),
@@ -30,6 +30,7 @@ func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic
}
func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) {
+ // todo: add your logic here and delete this line
if len(req.Username) < 3 || len(req.Password) < 8 || len(req.Password) > 20 {
return nil, errors.New("the information is illegal")
}
@@ -50,10 +51,8 @@ func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err erro
}
return &types.LoginResp{
- UserId: res.Id,
- Username: res.Username,
- Email: res.Email,
- Token: res.Token,
- Expires: int64((7 * 24 * time.Hour).Seconds()),
+ AccessToken: "",
+ RefreshToken: res.Token,
+ User: types.User{},
}, nil
}
diff --git a/app/users/api/internal/logic/user/logoutLogic.go b/app/users/api/internal/logic/auth/logoutLogic.go
similarity index 64%
rename from app/users/api/internal/logic/user/logoutLogic.go
rename to app/users/api/internal/logic/auth/logoutLogic.go
index b740558..eeab5ea 100644
--- a/app/users/api/internal/logic/user/logoutLogic.go
+++ b/app/users/api/internal/logic/auth/logoutLogic.go
@@ -1,15 +1,16 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
-package user
+package auth
import (
"context"
"errors"
+ "juwan-backend/app/users/rpc/usercenter"
+ "juwan-backend/common/utils/contextx"
"juwan-backend/app/users/api/internal/svc"
"juwan-backend/app/users/api/internal/types"
- "juwan-backend/app/users/rpc/usercenter"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -20,7 +21,7 @@ type LogoutLogic struct {
svcCtx *svc.ServiceContext
}
-// 用户登出
+// 退出登录
func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogic {
return &LogoutLogic{
Logger: logx.WithContext(ctx),
@@ -29,15 +30,19 @@ func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogi
}
}
-func (l *LogoutLogic) Logout(req *types.LogoutReq) (resp *types.LogoutResp, err error) {
- if req.UserId <= 0 {
+func (l *LogoutLogic) Logout(_ *types.LogoutReq) (resp *types.EmptyResp, err error) {
+ // todo: add your logic here and delete this line
+ userId, err := contextx.UserIDFrom(l.ctx)
+ if err != nil {
+ return nil, errors.New("illegal id")
+ }
+ if userId <= 0 {
return nil, errors.New("invalid userId")
}
- _, err = l.svcCtx.UserRpc.Logout(l.ctx, &usercenter.LogoutReq{UserId: req.UserId})
+ _, err = l.svcCtx.UserRpc.Logout(l.ctx, &usercenter.LogoutReq{UserId: userId})
if err != nil {
return nil, err
}
-
- return &types.LogoutResp{Message: "logout success"}, nil
+ return &types.EmptyResp{}, nil
}
diff --git a/app/users/api/internal/logic/user/registerLogic.go b/app/users/api/internal/logic/auth/registerLogic.go
similarity index 84%
rename from app/users/api/internal/logic/user/registerLogic.go
rename to app/users/api/internal/logic/auth/registerLogic.go
index 0ba0a0a..e9e36d0 100644
--- a/app/users/api/internal/logic/user/registerLogic.go
+++ b/app/users/api/internal/logic/auth/registerLogic.go
@@ -1,19 +1,19 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
-package user
+package auth
import (
"context"
"errors"
- "juwan-backend/app/users/api/internal/contextx"
+ "juwan-backend/app/users/rpc/pb"
+ "juwan-backend/app/users/rpc/usercenter"
+ "juwan-backend/common/utils/contextx"
+ "juwan-backend/common/utils/pwdUtils"
"regexp"
"juwan-backend/app/users/api/internal/svc"
"juwan-backend/app/users/api/internal/types"
- "juwan-backend/app/users/rpc/pb"
- "juwan-backend/app/users/rpc/usercenter"
- "juwan-backend/common/utils"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -24,7 +24,7 @@ type RegisterLogic struct {
svcCtx *svc.ServiceContext
}
-// 用户注册接口
+// 用户注册
func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic {
return &RegisterLogic{
Logger: logx.WithContext(ctx),
@@ -52,7 +52,7 @@ func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterRe
return nil, errors.New("user already exists")
}
- hashedPassword, err := utils.HashPassword(req.Password)
+ hashedPassword, err := pwdUtils.HashPassword(req.Password)
if err != nil {
return nil, errors.New("hash password failed")
}
@@ -63,7 +63,7 @@ func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterRe
return nil, errors.New("contextx.RequestIdFrom failed")
}
- _, err = l.svcCtx.UserRpc.Register(l.ctx, &usercenter.RegisterReq{
+ res, err := l.svcCtx.UserRpc.Register(l.ctx, &usercenter.RegisterReq{
Username: req.Username,
Passwd: hashedPassword,
Phone: req.Username,
@@ -78,9 +78,8 @@ func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterRe
// 返回响应
return &types.RegisterResp{
- UserId: 0,
- Username: req.Username,
- Email: req.Email,
- Message: "register success",
+ AccessToken: "",
+ RefreshToken: res.Res,
+ User: types.User{},
}, nil
}
diff --git a/app/users/api/internal/logic/user/updatePasswordByVcodeLogic.go b/app/users/api/internal/logic/auth/resetPasswordLogic.go
similarity index 64%
rename from app/users/api/internal/logic/user/updatePasswordByVcodeLogic.go
rename to app/users/api/internal/logic/auth/resetPasswordLogic.go
index 8f64f51..1b9a767 100644
--- a/app/users/api/internal/logic/user/updatePasswordByVcodeLogic.go
+++ b/app/users/api/internal/logic/auth/resetPasswordLogic.go
@@ -1,14 +1,14 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
-package user
+package auth
import (
"context"
"errors"
- "juwan-backend/app/users/api/internal/contextx"
"juwan-backend/app/users/rpc/usercenter"
- "juwan-backend/common/utils"
+ "juwan-backend/common/utils/contextx"
+ "juwan-backend/common/utils/pwdUtils"
"juwan-backend/app/users/api/internal/svc"
"juwan-backend/app/users/api/internal/types"
@@ -16,29 +16,28 @@ import (
"github.com/zeromicro/go-zero/core/logx"
)
-type UpdatePasswordByVcodeLogic struct {
+type ResetPasswordLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
-// 修改密码-使用验证码
-func NewUpdatePasswordByVcodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdatePasswordByVcodeLogic {
- return &UpdatePasswordByVcodeLogic{
+// 重置密码
+func NewResetPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetPasswordLogic {
+ return &ResetPasswordLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
-func (l *UpdatePasswordByVcodeLogic) UpdatePasswordByVcode(req *types.ResetPasswordByVcode) (resp *types.EmptyResp, err error) {
- // todo: add your logic here and delete this line
+func (l *ResetPasswordLogic) ResetPassword(req *types.ResetPasswordReq) (resp *types.EmptyResp, err error) {
requestId, err := contextx.RequestIdFrom(l.ctx)
if err != nil {
logx.Errorf("get request id from context failed, err:%v.", err)
return nil, errors.New("bad request")
}
- hashedPassword, err := utils.HashPassword(req.Password)
+ hashedPassword, err := pwdUtils.HashPassword(req.NewPassword)
if err != nil {
logx.Errorf("hash password failed, err:%v.", err)
return nil, errors.New("bad password")
diff --git a/app/users/api/internal/logic/user/followUserLogic.go b/app/users/api/internal/logic/user/followUserLogic.go
new file mode 100644
index 0000000..49bd0cf
--- /dev/null
+++ b/app/users/api/internal/logic/user/followUserLogic.go
@@ -0,0 +1,34 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "context"
+
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type FollowUserLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 关注用户
+func NewFollowUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FollowUserLogic {
+ return &FollowUserLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *FollowUserLogic) FollowUser(req *types.FollowUserReq) (resp *types.EmptyResp, err error) {
+ // todo: add your logic here and delete this line
+
+ return
+}
diff --git a/app/users/api/internal/logic/user/getMeLogic.go b/app/users/api/internal/logic/user/getMeLogic.go
index a291f33..9dcfb8a 100644
--- a/app/users/api/internal/logic/user/getMeLogic.go
+++ b/app/users/api/internal/logic/user/getMeLogic.go
@@ -6,9 +6,10 @@ package user
import (
"context"
"errors"
- "juwan-backend/app/users/api/internal/contextx"
"juwan-backend/app/users/rpc/usercenter"
"juwan-backend/common/converter"
+ "juwan-backend/common/utils/contextx"
+ "time"
"juwan-backend/app/users/api/internal/svc"
"juwan-backend/app/users/api/internal/types"
@@ -31,7 +32,7 @@ func NewGetMeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMeLogic
}
}
-func (l *GetMeLogic) GetMe() (resp *types.UserInfo, err error) {
+func (l *GetMeLogic) GetMe() (resp *types.User, err error) {
userId, err := contextx.UserIDFrom(l.ctx)
if err != nil {
return nil, errors.New("illegal id")
@@ -43,6 +44,8 @@ func (l *GetMeLogic) GetMe() (resp *types.UserInfo, err error) {
return nil, errors.New("get user by id error")
}
err = converter.StructToStruct(user, &resp)
+ createAt := time.Unix(user.Users.CreatedAt, 0)
+ resp.CreatedAt = createAt.Format(time.DateTime)
if err != nil {
return nil, errors.New("to struct error")
}
diff --git a/app/users/api/internal/logic/user/getUserInfoLogic.go b/app/users/api/internal/logic/user/getUserInfoLogic.go
index e427495..d5b244d 100644
--- a/app/users/api/internal/logic/user/getUserInfoLogic.go
+++ b/app/users/api/internal/logic/user/getUserInfoLogic.go
@@ -6,12 +6,11 @@ package user
import (
"context"
"errors"
- "juwan-backend/app/users/rpc/usercenter"
- "juwan-backend/common/converter"
-
"juwan-backend/app/users/api/internal/svc"
"juwan-backend/app/users/api/internal/types"
+ "juwan-backend/app/users/rpc/usercenter"
+ "github.com/jinzhu/copier"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -30,21 +29,21 @@ func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUs
}
}
-func (l *GetUserInfoLogic) GetUserInfo(req *types.GetUserInfoReq) (resp types.UserInfo, err error) {
-
+func (l *GetUserInfoLogic) GetUserInfo(req *types.GetUserReq) (resp types.User, err error) {
+
pbUser, err := l.svcCtx.UserRpc.GetUsersById(l.ctx, &usercenter.GetUsersByIdReq{
- Id: req.UserId,
+ Id: req.Id,
})
if err != nil {
- return types.UserInfo{}, errors.New("failed to get user info by userid")
- }
- user := types.UserInfo{}
- err = converter.StructToStruct(&pbUser.Users, &user)
- if err != nil {
- logx.Errorf("struct to user info failed, err:%v.", err)
- return types.UserInfo{}, errors.New("failed to get user info by userid")
+ return types.User{}, errors.New("failed to get user info by userid")
+ }
+
+ user := types.User{}
+ err = copier.Copy(&user, &pbUser.Users)
+ if err != nil {
+ logx.Errorf("struct to user info failed, err:%v.", err)
+ return types.User{}, errors.New("failed to get user info by userid")
}
- //req.UserId
return user, nil
}
diff --git a/app/users/api/internal/logic/user/switchRoleLogic.go b/app/users/api/internal/logic/user/switchRoleLogic.go
new file mode 100644
index 0000000..66bf3c1
--- /dev/null
+++ b/app/users/api/internal/logic/user/switchRoleLogic.go
@@ -0,0 +1,51 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "context"
+ "errors"
+ "juwan-backend/app/users/rpc/usercenter"
+ "juwan-backend/common/utils/contextx"
+
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+ "google.golang.org/protobuf/proto"
+)
+
+type SwitchRoleLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 切换当前激活角色
+func NewSwitchRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SwitchRoleLogic {
+ return &SwitchRoleLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *SwitchRoleLogic) SwitchRole(req *types.SwitchRoleReq) (resp *types.EmptyResp, err error) {
+ // todo: add your logic here and delete this line
+ id, err := contextx.UserIDFrom(l.ctx)
+ if err != nil {
+ logx.Errorf("get user id from context: %v", err)
+ return nil, errors.New("illegal id")
+ }
+ _, err = l.svcCtx.UserRpc.UpdateUsers(l.ctx, &usercenter.UpdateUsersReq{
+ Id: id,
+ CurrentRole: proto.String(req.Role),
+ })
+
+ if err != nil {
+ logx.Errorf("update user info by id: %v", err)
+ return nil, errors.New("update user info by userid")
+ }
+ return
+}
diff --git a/app/users/api/internal/logic/user/unfollowUserLogic.go b/app/users/api/internal/logic/user/unfollowUserLogic.go
new file mode 100644
index 0000000..3fb1cdd
--- /dev/null
+++ b/app/users/api/internal/logic/user/unfollowUserLogic.go
@@ -0,0 +1,34 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "context"
+
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type UnfollowUserLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 取消关注用户
+func NewUnfollowUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UnfollowUserLogic {
+ return &UnfollowUserLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *UnfollowUserLogic) UnfollowUser(req *types.UnfollowUserReq) (resp *types.EmptyResp, err error) {
+ // todo: add your logic here and delete this line
+
+ return
+}
diff --git a/app/users/api/internal/logic/user/updateMeLogic.go b/app/users/api/internal/logic/user/updateMeLogic.go
index 4690fb9..1ceda46 100644
--- a/app/users/api/internal/logic/user/updateMeLogic.go
+++ b/app/users/api/internal/logic/user/updateMeLogic.go
@@ -6,9 +6,10 @@ package user
import (
"context"
"errors"
- "juwan-backend/app/users/api/internal/contextx"
"juwan-backend/app/users/rpc/usercenter"
"juwan-backend/common/converter"
+ "juwan-backend/common/utils/contextx"
+ "strings"
"juwan-backend/app/users/api/internal/svc"
"juwan-backend/app/users/api/internal/types"
@@ -31,16 +32,16 @@ func NewUpdateMeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateMe
}
}
-func (l *UpdateMeLogic) UpdateMe(req *types.UpdateUserInfoReq) (resp *types.UserInfo, err error) {
+func (l *UpdateMeLogic) UpdateMe(req *types.UpdateUserProfileReq) (resp *types.UpdateUserProfileReq, err error) {
userId, err := contextx.UserIDFrom(l.ctx)
if err != nil {
return nil, err
}
res, err := l.svcCtx.UserRpc.UpdateUsers(l.ctx, &usercenter.UpdateUsersReq{
Id: userId,
- Nickname: req.Nickname,
- Avatar: req.Avatar,
- Bio: req.Bio,
+ Nickname: proto_string(req.Nickname),
+ Avatar: proto_string(req.Avatar),
+ Bio: proto_string(req.Bio),
VerifiedRoles: nil,
})
if err != nil {
@@ -53,3 +54,10 @@ func (l *UpdateMeLogic) UpdateMe(req *types.UpdateUserInfoReq) (resp *types.User
}
return
}
+
+func proto_string(s string) *string {
+ if len(s) == 0 || strings.Contains(s, " ") {
+ return nil
+ }
+ return &s
+}
diff --git a/app/users/api/internal/logic/user/updateNotificationSettingsLogic.go b/app/users/api/internal/logic/user/updateNotificationSettingsLogic.go
new file mode 100644
index 0000000..c1c3161
--- /dev/null
+++ b/app/users/api/internal/logic/user/updateNotificationSettingsLogic.go
@@ -0,0 +1,34 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "context"
+
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateNotificationSettingsLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 更新通知偏好
+func NewUpdateNotificationSettingsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateNotificationSettingsLogic {
+ return &UpdateNotificationSettingsLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *UpdateNotificationSettingsLogic) UpdateNotificationSettings(req *types.UpdateNotifySettingsReq) (resp *types.EmptyResp, err error) {
+ // todo: add your logic here and delete this line
+
+ return
+}
diff --git a/app/users/api/internal/logic/user/updatePasswordLogic.go b/app/users/api/internal/logic/user/updatePasswordLogic.go
deleted file mode 100644
index 790c55a..0000000
--- a/app/users/api/internal/logic/user/updatePasswordLogic.go
+++ /dev/null
@@ -1,72 +0,0 @@
-// Code scaffolded by goctl. Safe to edit.
-// goctl 1.9.2
-
-package user
-
-import (
- "context"
- "errors"
- "juwan-backend/app/users/api/internal/contextx"
- "juwan-backend/app/users/rpc/usercenter"
- "juwan-backend/common/utils"
-
- "juwan-backend/app/users/api/internal/svc"
- "juwan-backend/app/users/api/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
-)
-
-var ChangeUserPassFailed = errors.New("change user pass failed")
-
-type UpdatePasswordLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
-}
-
-// 修改用户密码
-func NewUpdatePasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdatePasswordLogic {
- return &UpdatePasswordLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
-}
-
-func (l *UpdatePasswordLogic) UpdatePassword(req *types.UpdatePasswordReq) (resp *types.UpdatePasswordResp, err error) {
- // todo: add your logic here and delete this line
- userId, err := contextx.UserIDFrom(l.ctx)
- if err != nil {
- logx.Errorf("get user id from context failed, err:%v.", err)
- return nil, ChangeUserPassFailed
- }
-
- user, err := l.svcCtx.UserRpc.GetUsersById(l.ctx, &usercenter.GetUsersByIdReq{
- Id: userId,
- })
- if err != nil {
- logx.Errorf("get user info failed, err:%v.", err)
- return nil, ChangeUserPassFailed
- }
-
- oldPasswd, err := utils.HashPassword(req.OldPassword)
- if err != nil {
- logx.Errorf("hash old password failed, err:%v.", err)
- return nil, ChangeUserPassFailed
- }
-
- if oldPasswd != user.Users.PasswordHash {
- return nil, ChangeUserPassFailed
- }
-
- _, err = l.svcCtx.UserRpc.UpdateUsers(l.ctx, &usercenter.UpdateUsersReq{
- Id: userId,
- Username: &user.Users.Username,
- PasswordHash: &req.NewPassword,
- })
- if err != nil {
- logx.Errorf("update user password failed, err:%v.", err)
- return nil, ChangeUserPassFailed
- }
- return
-}
diff --git a/app/users/api/internal/logic/user/updateThemeSettingsLogic.go b/app/users/api/internal/logic/user/updateThemeSettingsLogic.go
new file mode 100644
index 0000000..8bc13d5
--- /dev/null
+++ b/app/users/api/internal/logic/user/updateThemeSettingsLogic.go
@@ -0,0 +1,34 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package user
+
+import (
+ "context"
+
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateThemeSettingsLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 更新主题偏好
+func NewUpdateThemeSettingsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateThemeSettingsLogic {
+ return &UpdateThemeSettingsLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *UpdateThemeSettingsLogic) UpdateThemeSettings(req *types.UpdateThemeSettingsReq) (resp *types.EmptyResp, err error) {
+ // todo: add your logic here and delete this line
+
+ return
+}
diff --git a/app/users/api/internal/logic/user/updateUserInfoLogic.go b/app/users/api/internal/logic/user/updateUserInfoLogic.go
deleted file mode 100644
index d517698..0000000
--- a/app/users/api/internal/logic/user/updateUserInfoLogic.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Code scaffolded by goctl. Safe to edit.
-// goctl 1.9.2
-
-package user
-
-import (
- "context"
- "errors"
- "juwan-backend/app/users/api/internal/contextx"
- "juwan-backend/app/users/rpc/usercenter"
-
- "juwan-backend/app/users/api/internal/svc"
- "juwan-backend/app/users/api/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
-)
-
-type UpdateUserInfoLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
-}
-
-// 修改用户信息
-func NewUpdateUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserInfoLogic {
- return &UpdateUserInfoLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
-}
-
-func (l *UpdateUserInfoLogic) UpdateUserInfo(req *types.UpdateUserInfoReq) (resp *types.UpdateUserInfoResp, err error) {
- userId, err := contextx.UserIDFrom(l.ctx)
- if err != nil {
- return nil, errors.New("user not found")
- }
- _, err = l.svcCtx.UserRpc.UpdateUsers(l.ctx, &usercenter.UpdateUsersReq{
- Id: userId,
- Nickname: req.Nickname,
- Avatar: req.Avatar,
- Bio: req.Bio,
- })
- if err != nil {
- return nil, errors.New("update user info failed")
- }
- return
-}
diff --git a/app/users/api/internal/logic/verification_admin/approveVerificationLogic.go b/app/users/api/internal/logic/verification_admin/approveVerificationLogic.go
new file mode 100644
index 0000000..6d9834d
--- /dev/null
+++ b/app/users/api/internal/logic/verification_admin/approveVerificationLogic.go
@@ -0,0 +1,52 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_admin
+
+import (
+ "context"
+ "juwan-backend/app/user_verifications/rpc/pb"
+ "juwan-backend/common/utils/contextx"
+ "time"
+
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type ApproveVerificationLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 管理员通过申请
+func NewApproveVerificationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApproveVerificationLogic {
+ return &ApproveVerificationLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+var (
+ APPROVE = "approved"
+)
+
+func (l *ApproveVerificationLogic) ApproveVerification(req *types.VerificationIdReq) (resp *types.VerificationEmptyResp, err error) {
+ adminId, err := contextx.AdminIdFrom(l.ctx)
+ if err != nil {
+ return nil, err
+ }
+ _, err = l.svcCtx.UserVerificationsRpc.UpdateUserVerifications(l.ctx, &pb.UpdateUserVerificationsReq{
+ Id: req.Id,
+ Status: &APPROVE,
+ ReviewedBy: adminId,
+ ReviewedAt: time.Now().Unix(),
+ })
+ if err != nil {
+ return nil, err
+ }
+ return
+}
diff --git a/app/users/api/internal/logic/verification_admin/getVerificationsLogic.go b/app/users/api/internal/logic/verification_admin/getVerificationsLogic.go
new file mode 100644
index 0000000..797eb34
--- /dev/null
+++ b/app/users/api/internal/logic/verification_admin/getVerificationsLogic.go
@@ -0,0 +1,66 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_admin
+
+import (
+ "context"
+ "juwan-backend/app/user_verifications/rpc/pb"
+ "juwan-backend/common/utils/contextx"
+ "time"
+
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/jinzhu/copier"
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetVerificationsLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 管理员获取认证申请列表 (分页)
+func NewGetVerificationsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetVerificationsLogic {
+ return &GetVerificationsLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *GetVerificationsLogic) GetVerifications(req *types.GetPendingListReq) (resp *types.GetPendingListResp, err error) {
+ _, err = contextx.AdminIdFrom(l.ctx)
+ if err != nil {
+ return nil, err
+ }
+ verifications, err := l.svcCtx.UserVerificationsRpc.SearchUserVerifications(l.ctx, &pb.SearchUserVerificationsReq{
+ Page: req.Page,
+ Limit: req.Size,
+ Role: req.Role,
+ Status: req.Status,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ var searchResults []types.VerificationItem
+ for _, v := range verifications.UserVerifications {
+ temp := types.VerificationItem{}
+ err = copier.Copy(&temp, v)
+ if err != nil {
+ logx.Errorf("copy verification item err: %s", err.Error())
+ continue
+ }
+ temp.CreatedAt = time.Unix(v.CreatedAt, 0).Format(time.DateTime)
+ temp.ReviewedAt = time.Unix(v.ReviewedAt, 0).Format(time.DateTime)
+ searchResults = append(searchResults, temp)
+ }
+ resp = &types.GetPendingListResp{
+ List: searchResults,
+ Total: 0,
+ }
+ return
+}
diff --git a/app/users/api/internal/logic/verification_admin/rejectVerificationLogic.go b/app/users/api/internal/logic/verification_admin/rejectVerificationLogic.go
new file mode 100644
index 0000000..6da01f9
--- /dev/null
+++ b/app/users/api/internal/logic/verification_admin/rejectVerificationLogic.go
@@ -0,0 +1,52 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_admin
+
+import (
+ "context"
+ "juwan-backend/app/user_verifications/rpc/pb"
+ "juwan-backend/common/utils/contextx"
+ "time"
+
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type RejectVerificationLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 管理员驳回申请
+func NewRejectVerificationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RejectVerificationLogic {
+ return &RejectVerificationLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+var REJECTED = "rejected"
+
+func (l *RejectVerificationLogic) RejectVerification(req *types.RejectVerificationReq) (resp *types.VerificationEmptyResp, err error) {
+ // todo: add your logic here and delete this line
+ adminId, err := contextx.AdminIdFrom(l.ctx)
+ if err != nil {
+ return nil, err
+ }
+ _, err = l.svcCtx.UserVerificationsRpc.UpdateUserVerifications(l.ctx, &pb.UpdateUserVerificationsReq{
+ Id: req.Id,
+ Status: &REJECTED,
+ RejectReason: &req.Reason,
+ ReviewedBy: adminId,
+ ReviewedAt: time.Now().Unix(),
+ })
+ if err != nil {
+ return nil, err
+ }
+ return
+}
diff --git a/app/users/api/internal/logic/verification_user/applyVerificationLogic.go b/app/users/api/internal/logic/verification_user/applyVerificationLogic.go
new file mode 100644
index 0000000..b7c40f3
--- /dev/null
+++ b/app/users/api/internal/logic/verification_user/applyVerificationLogic.go
@@ -0,0 +1,68 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_user
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "juwan-backend/app/user_verifications/rpc/pb"
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+ "juwan-backend/common/utils/contextx"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type ApplyVerificationLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 提交或修改角色认证申请 (支持幂等更新)
+func NewApplyVerificationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApplyVerificationLogic {
+ return &ApplyVerificationLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *ApplyVerificationLogic) ApplyVerification(req *types.ApplyVerificationReq) (resp *types.VerificationEmptyResp, err error) {
+ userId, err := contextx.UserIDFrom(l.ctx)
+ if err != nil {
+ logx.Errorf("get user id from context: %v", err)
+ return nil, contextx.ERRILLEGALUSER
+ }
+ verifications, err := l.svcCtx.UserVerificationsRpc.SearchUserVerifications(l.ctx, &pb.SearchUserVerificationsReq{
+ UserId: userId,
+ })
+ if err != nil {
+ logx.Errorf("search user verifications: %v", err)
+ return nil, errors.New("search user verifications failed")
+ }
+
+ materials, err := json.Marshal(req.Materials)
+ if err != nil {
+ logx.Errorf("marshal materials: %v", err)
+ return nil, err
+ }
+
+ if verifications == nil || len(verifications.UserVerifications) == 0 {
+ // 如果没有则增加
+ _, err = l.svcCtx.UserVerificationsRpc.AddUserVerifications(l.ctx, &pb.AddUserVerificationsReq{
+ Role: req.Role,
+ Materials: string(materials),
+ })
+ if err != nil {
+ logx.Errorf("add user verifications: %v", err)
+ return nil, errors.New("add user verifications failed")
+ }
+ } else {
+
+ }
+
+ return &types.VerificationEmptyResp{}, nil
+}
diff --git a/app/users/api/internal/logic/verification_user/getMyVerificationsLogic.go b/app/users/api/internal/logic/verification_user/getMyVerificationsLogic.go
new file mode 100644
index 0000000..b03bfd2
--- /dev/null
+++ b/app/users/api/internal/logic/verification_user/getMyVerificationsLogic.go
@@ -0,0 +1,68 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package verification_user
+
+import (
+ "context"
+ "juwan-backend/app/user_verifications/rpc/pb"
+ "juwan-backend/common/utils/contextx"
+ "time"
+
+ "juwan-backend/app/users/api/internal/svc"
+ "juwan-backend/app/users/api/internal/types"
+
+ "github.com/jinzhu/copier"
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetMyVerificationsLogic struct {
+ logx.Logger
+ ctx context.Context
+ svcCtx *svc.ServiceContext
+}
+
+// 获取我的所有认证状态
+func NewGetMyVerificationsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMyVerificationsLogic {
+ return &GetMyVerificationsLogic{
+ Logger: logx.WithContext(ctx),
+ ctx: ctx,
+ svcCtx: svcCtx,
+ }
+}
+
+func (l *GetMyVerificationsLogic) GetMyVerifications() (resp *types.GetMyVerificationsResp, err error) {
+ // todo: add your logic here and delete this line
+ userId, err := contextx.UserIDFrom(l.ctx)
+ if err != nil {
+ logx.Errorf("get user id from context: %v", err)
+ return nil, contextx.ERRILLEGALUSER
+ }
+
+ verifications, err := l.svcCtx.UserVerificationsRpc.SearchUserVerifications(l.ctx, &pb.SearchUserVerificationsReq{
+ UserId: userId,
+ Page: 1,
+ Limit: 100, // assuming a user won't have more than 100 verification records, adjust as needed
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ var searchResults []types.VerificationItem
+ for _, v := range verifications.UserVerifications {
+ temp := types.VerificationItem{}
+ err = copier.Copy(&temp, v)
+ if err != nil {
+ logx.Errorf("copy verification item err: %s", err.Error())
+ continue
+ }
+ temp.CreatedAt = time.Unix(v.CreatedAt, 0).Format(time.DateTime)
+ temp.ReviewedAt = time.Unix(v.ReviewedAt, 0).Format(time.DateTime)
+ searchResults = append(searchResults, temp)
+ }
+
+ resp = &types.GetMyVerificationsResp{
+ List: searchResults,
+ }
+ return
+}
diff --git a/app/users/api/internal/middleware/jwtauthMiddleware.go b/app/users/api/internal/middleware/jwtauthMiddleware.go
new file mode 100644
index 0000000..c51e5ba
--- /dev/null
+++ b/app/users/api/internal/middleware/jwtauthMiddleware.go
@@ -0,0 +1,22 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.9.2
+
+package middleware
+
+import "net/http"
+
+type JwtAuthMiddleware struct {
+}
+
+func NewJwtAuthMiddleware() *JwtAuthMiddleware {
+ return &JwtAuthMiddleware{}
+}
+
+func (m *JwtAuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ // TODO generate middleware implement function, delete after code implementation
+
+ // Passthrough to next handler if need
+ next(w, r)
+ }
+}
diff --git a/app/users/api/internal/svc/serviceContext.go b/app/users/api/internal/svc/serviceContext.go
index b83e976..5b6c751 100644
--- a/app/users/api/internal/svc/serviceContext.go
+++ b/app/users/api/internal/svc/serviceContext.go
@@ -4,28 +4,27 @@
package svc
import (
- "juwan-backend/app/snowflake/rpc/snowflake"
+ "juwan-backend/app/user_verifications/rpc/userverifications"
"juwan-backend/app/users/api/internal/config"
"juwan-backend/app/users/api/internal/middleware"
"juwan-backend/app/users/rpc/usercenter"
- "juwan-backend/common/snowflakex"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/zrpc"
)
type ServiceContext struct {
- Config config.Config
- Logger rest.Middleware
- UserRpc usercenter.Usercenter
- SnowflakeRpc snowflake.SnowflakeServiceClient
+ Config config.Config
+ Logger rest.Middleware
+ UserRpc usercenter.Usercenter
+ UserVerificationsRpc userverifications.UserVerificationsZrpcClient
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
- Config: c,
- Logger: middleware.NewLoggerMiddleware().Handle,
- UserRpc: usercenter.NewUsercenter(zrpc.MustNewClient(c.UsercenterRpcConf)),
- SnowflakeRpc: snowflakex.NewClient(c.SnowflakeRpcConf),
+ Config: c,
+ Logger: middleware.NewLoggerMiddleware().Handle,
+ UserRpc: usercenter.NewUsercenter(zrpc.MustNewClient(c.UsercenterRpcConf)),
+ UserVerificationsRpc: userverifications.NewUserVerificationsZrpcClient(zrpc.MustNewClient(c.UserVerificationRpc)),
}
}
diff --git a/app/users/api/internal/types/types.go b/app/users/api/internal/types/types.go
index f6944b4..00eee78 100644
--- a/app/users/api/internal/types/types.go
+++ b/app/users/api/internal/types/types.go
@@ -3,89 +3,137 @@
package types
+type ApplyVerificationReq struct {
+ Role string `json:"role"` // 申请什么角色
+ Materials map[string]string `json:"materials"` // 证明材料键值对 {"idCardFront": "http...", "license": "http..."}
+}
+
type EmptyResp struct {
}
-type ErrorResp struct {
- Code int `json:"code"`
- Message string `json:"message"`
+type FollowUserReq struct {
+ Id int64 `path:"id"`
}
-type GetUserInfoReq struct {
- UserId int64 `path:"userId" binding:"required,gt=0"`
+type ForgotPasswordReq struct {
+ Phone string `json:"phone,omitempty"`
+ Email string `json:"email,omitempty"`
+}
+
+type GetMyVerificationsResp struct {
+ List []VerificationItem `json:"list"`
+}
+
+type GetPendingListReq struct {
+ Page int64 `form:"page,default=1"`
+ Size int64 `form:"size,default=20"`
+ Role string `form:"role,optional"` // 筛选角色
+ Status string `form:"status,optional"` // 筛选状态,默认 pending
+}
+
+type GetPendingListResp struct {
+ List []VerificationItem `json:"list"`
+ Total int64 `json:"total"`
+}
+
+type GetUserReq struct {
+ Id int64 `path:"id"`
}
type LoginReq struct {
- Username string `json:"username" binding:"required"`
- Password string `json:"password" binding:"required"`
+ Phone string `json:"phone,omitempty"` // 手机号登录
+ Username string `json:"username,omitempty"` // 或用户名登录
+ Password string `json:"password"`
+ Remember bool `json:"remember,optional"`
}
type LoginResp struct {
- UserId int64 `json:"userId"`
- Username string `json:"username"`
- Email string `json:"email"`
- Token string `json:"token"`
- Expires int64 `json:"expires"`
+ AccessToken string `json:"accessToken"`
+ RefreshToken string `json:"refreshToken"`
+ User User `json:"user"`
}
type LogoutReq struct {
- UserId int64 `path:"userId" binding:"required,gt=0"`
- Token string `header:"Authorization" binding:"required"`
-}
-
-type LogoutResp struct {
- Message string `json:"message"`
}
type RegisterReq struct {
- Username string `json:"username" binding:"required,min=3,max=50"`
- Password string `json:"password" binding:"required,min=6,max=128"`
- Email string `json:"email,omitempty" binding:"omitempty,email"`
- Phone string `json:"phone,omitempty" binding:"omitempty,len=11"`
- Vcode int32 `json:"vcode"`
+ Phone string `json:"phone,omitempty"`
+ Email string `json:"email,omitempty"`
+ Username string `json:"username"`
+ Password string `json:"password"`
+ Vcode string `json:"vcode,omitempty"` // 验证码
}
type RegisterResp struct {
- UserId int64 `json:"userId"`
- Username string `json:"username"`
- Email string `json:"email"`
- Message string `json:"message"`
+ AccessToken string `json:"accessToken"`
+ RefreshToken string `json:"refreshToken"`
+ User User `json:"user"`
}
-type ResetPasswordByVcode struct {
- Email string `json:"email"`
- Password string `json:"password"`
- Vcode string `json:"vcode"`
+type RejectVerificationReq struct {
+ Id int64 `path:"id"`
+ Reason string `json:"reason"` // 必填:驳回原因
}
-type UpdatePasswordReq struct {
- UserId int64 `path:"userId" binding:"required,gt=0"`
- OldPassword string `json:"oldPassword" binding:"required"`
- NewPassword string `json:"newPassword" binding:"required,min=6,max=128"`
+type ResetPasswordReq struct {
+ Phone string `json:"phone,omitempty"`
+ Email string `json:"email,omitempty"`
+ Vcode string `json:"vcode"`
+ NewPassword string `json:"newPassword"`
}
-type UpdatePasswordResp struct {
- Message string `json:"message"`
+type SwitchRoleReq struct {
+ Role string `json:"role"` // 目标角色
}
-type UpdateUserInfoReq struct {
- Nickname *string `json:"nickname,omitempty"`
- Avatar *string `json:"avatar,omitempty"`
- Bio *string `json:"bio,omitempty"`
+type UnfollowUserReq struct {
+ Id int64 `path:"id"`
}
-type UpdateUserInfoResp struct {
- UserId int64 `json:"userId"`
- Message string `json:"message"`
+type UpdateNotifySettingsReq struct {
+ Order bool `json:"order,optional"`
+ Community bool `json:"community,optional"`
+ System bool `json:"system,optional"`
}
-type UserInfo struct {
- UserId int64 `json:"userId"`
- Username string `json:"username"`
- Email string `json:"email"`
- Phone string `json:"phone"`
- Avatar string `json:"avatar"`
- Status int `json:"status"`
- CreateAt int64 `json:"createAt"`
- UpdateAt int64 `json:"updateAt"`
+type UpdateThemeSettingsReq struct {
+ Theme string `json:"theme"` // dark, light
+}
+
+type UpdateUserProfileReq struct {
+ Nickname string `json:"nickname,optional"`
+ Avatar string `json:"avatar,optional"`
+ Bio string `json:"bio,optional"`
+}
+
+type User struct {
+ Id int64 `json:"id"`
+ Username string `json:"username"`
+ Nickname string `json:"nickname"`
+ Avatar string `json:"avatar"`
+ Role string `json:"role"` // consumer, player, owner, admin
+ VerifiedRoles []string `json:"verifiedRoles"` // e.g. ["consumer", "player"]
+ VerificationStatus map[string]string `json:"verificationStatus"` // e.g. {"player": "approved"}
+ Phone string `json:"phone,omitempty"`
+ Bio string `json:"bio,omitempty"`
+ CreatedAt string `json:"createdAt"` // ISO 8601
+}
+
+type VerificationEmptyResp struct {
+}
+
+type VerificationIdReq struct {
+ Id int64 `path:"id"` // 注意:这是 user_verifications.id
+}
+
+type VerificationItem struct {
+ Id int64 `json:"id"` // 认证记录ID (主键,用于管理员操作)
+ UserId int64 `json:"userId"` // 申请人ID (外键)
+ UserNickname string `json:"userNickname"` // 冗余显示,方便前端展示
+ Role string `json:"role"` // 申请角色: player, owner
+ Status string `json:"status"` // pending, approved, rejected
+ Materials map[string]string `json:"materials"` // 核心字段:对应 DB 的 JSONB
+ RejectReason string `json:"rejectReason"` // 驳回原因
+ CreatedAt string `json:"createdAt"` // 申请时间
+ ReviewedAt string `json:"reviewedAt"` // 审核时间
}
diff --git a/app/users/rpc/internal/logic/loginLogic.go b/app/users/rpc/internal/logic/loginLogic.go
index 93f7caf..5f21188 100644
--- a/app/users/rpc/internal/logic/loginLogic.go
+++ b/app/users/rpc/internal/logic/loginLogic.go
@@ -7,7 +7,7 @@ import (
"juwan-backend/app/users/rpc/internal/svc"
utils2 "juwan-backend/app/users/rpc/internal/utils"
"juwan-backend/app/users/rpc/pb"
- "juwan-backend/common/utils"
+ "juwan-backend/common/utils/pwdUtils"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -36,7 +36,7 @@ func (l *LoginLogic) Login(in *pb.LoginReq) (*pb.LoginResp, error) {
return nil, err
}
logx.Infof("user:%v", user)
- if !utils.VerifyPassword(user.PasswordHash, in.Passwd) {
+ if !pwdUtils.VerifyPassword(user.PasswordHash, in.Passwd) {
logx.WithContext(l.ctx).Errorf("User %s Login failed", user.Username)
return nil, errors.New("incorrect password")
}
diff --git a/app/users/rpc/internal/logic/registerLogic.go b/app/users/rpc/internal/logic/registerLogic.go
index 0c33d2f..34b5416 100644
--- a/app/users/rpc/internal/logic/registerLogic.go
+++ b/app/users/rpc/internal/logic/registerLogic.go
@@ -8,9 +8,9 @@ import (
"fmt"
"juwan-backend/app/snowflake/rpc/snowflake"
"juwan-backend/app/users/rpc/internal/svc"
+ "juwan-backend/app/users/rpc/internal/utils"
"juwan-backend/app/users/rpc/pb"
"juwan-backend/common/redisx"
- "strconv"
"strings"
"github.com/zeromicro/go-zero/core/logx"
@@ -56,8 +56,7 @@ func (l *RegisterLogic) Register(in *pb.RegisterReq) (*pb.RegisterResp, error) {
return nil, errors.New("invalid verification code")
}
- code, err := strconv.ParseInt(vcode, 10, 32)
- if err != nil || int32(code) != in.Vcode {
+ if vcode != in.Vcode {
logx.Error("invalid verification code")
return nil, errors.New("invalid verification code")
}
@@ -80,7 +79,16 @@ func (l *RegisterLogic) Register(in *pb.RegisterReq) (*pb.RegisterResp, error) {
return nil, err
}
+ token, err := l.svcCtx.JwtManager.New(l.ctx, &utils.TokenPayload{
+ UserId: resp.Id,
+ IsAdmin: false,
+ })
+ if err != nil {
+ logx.Errorf("generate token failed, err:%v", err)
+ return nil, errors.New("generate token failed, but user registered successfully")
+ }
+
return &pb.RegisterResp{
- Res: "user registered successfully",
+ Res: token,
}, nil
}
diff --git a/app/users/rpc/internal/logic/searchUsersLogic.go b/app/users/rpc/internal/logic/searchUsersLogic.go
index 4e518e0..2dd4a1c 100644
--- a/app/users/rpc/internal/logic/searchUsersLogic.go
+++ b/app/users/rpc/internal/logic/searchUsersLogic.go
@@ -30,6 +30,14 @@ func NewSearchUsersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Searc
var SearUsersErr = errors.New("search users failed")
func (l *SearchUsersLogic) SearchUsers(in *pb.SearchUsersReq) (out *pb.SearchUsersResp, err error) {
+ if in.Page == nil || *in.Page < 0 {
+ logx.Errorf("Invalid page number: %v", in.Page)
+ return nil, errors.New("invalid page number")
+ }
+ if *in.Limit > 1000 {
+ logx.Errorf("Limit exceeds max limit: %d", in.Limit)
+ return nil, errors.New("limit exceeds max limit")
+ }
user, err := l.svcCtx.UsersModelRO.Query().
Where(users.Or(
users.UsernameContainsFold(*in.Username),
@@ -37,6 +45,8 @@ func (l *SearchUsersLogic) SearchUsers(in *pb.SearchUsersReq) (out *pb.SearchUse
users.EmailContainsFold(*in.Username),
users.CurrentRole(*in.CurrentRole),
)).
+ Offset(int(*in.Page * *in.Limit)).
+ Limit(int(*in.Limit)).
All(l.ctx)
if err != nil {
logx.Errorf("search users failed, err:%v.", err)
diff --git a/app/users/rpc/internal/models/users.go b/app/users/rpc/internal/models/users.go
index 3c01873..5b7a5ab 100644
--- a/app/users/rpc/internal/models/users.go
+++ b/app/users/rpc/internal/models/users.go
@@ -14,7 +14,7 @@ import (
"entgo.io/ent/dialect/sql"
)
-// Users is the model entity for the Users schema.
+// Users is the models entity for the Users schema.
type Users struct {
config `json:"-"`
// ID of the ent.
diff --git a/app/users/rpc/pb/users.pb.go b/app/users/rpc/pb/users.pb.go
index ff0d0d2..eb133db 100644
--- a/app/users/rpc/pb/users.pb.go
+++ b/app/users/rpc/pb/users.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
-// protoc v3.19.4
+// protoc v5.29.6
// source: users.proto
package pb
@@ -1201,10 +1201,10 @@ func (x *ValidateTokenReq) GetUserId() int64 {
type ValidateTokenResp struct {
state protoimpl.MessageState `protogen:"open.v1"`
- Valid bool `protobuf:"varint,1,opt,name=valid,proto3" json:"valid,omitempty"` // token 是否有效(不在黑名单中)
- Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // 验证失败原因
- UserId int64 `protobuf:"varint,3,opt,name=userId,proto3" json:"userId,omitempty"` // 用户ID
- RoleType string `protobuf:"varint,4,opt,name=roleType,proto3" json:"roleType,omitempty"` // 用户角色
+ Valid bool `protobuf:"varint,1,opt,name=valid,proto3" json:"valid,omitempty"` // token 是否有效(不在黑名单中)
+ Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // 验证失败原因
+ UserId int64 `protobuf:"varint,3,opt,name=userId,proto3" json:"userId,omitempty"` // 用户ID
+ RoleType string `protobuf:"bytes,4,opt,name=roleType,proto3" json:"roleType,omitempty"` // 用户角色
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -1384,7 +1384,7 @@ type RegisterReq struct {
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
Passwd string `protobuf:"bytes,2,opt,name=passwd,proto3" json:"passwd,omitempty"`
Phone string `protobuf:"bytes,3,opt,name=phone,proto3" json:"phone,omitempty"`
- Vcode int32 `protobuf:"varint,4,opt,name=vcode,proto3" json:"vcode,omitempty"`
+ Vcode string `protobuf:"bytes,4,opt,name=vcode,proto3" json:"vcode,omitempty"`
Email string `protobuf:"bytes,5,opt,name=email,proto3" json:"email,omitempty"`
RequestId string `protobuf:"bytes,6,opt,name=requestId,proto3" json:"requestId,omitempty"`
unknownFields protoimpl.UnknownFields
@@ -1442,11 +1442,11 @@ func (x *RegisterReq) GetPhone() string {
return ""
}
-func (x *RegisterReq) GetVcode() int32 {
+func (x *RegisterReq) GetVcode() string {
if x != nil {
return x.Vcode
}
- return 0
+ return ""
}
func (x *RegisterReq) GetEmail() string {
@@ -1834,7 +1834,7 @@ const file_users_proto_rawDesc = "" +
"\x05valid\x18\x01 \x01(\bR\x05valid\x12\x18\n" +
"\amessage\x18\x02 \x01(\tR\amessage\x12\x16\n" +
"\x06userId\x18\x03 \x01(\x03R\x06userId\x12\x1a\n" +
- "\broleType\x18\x04 \x01(\x03R\broleType\"`\n" +
+ "\broleType\x18\x04 \x01(\tR\broleType\"`\n" +
"\x12CheckPermissionReq\x12\x16\n" +
"\x06userId\x18\x01 \x01(\x03R\x06userId\x12\x1a\n" +
"\bresource\x18\x02 \x01(\tR\bresource\x12\x16\n" +
@@ -1846,7 +1846,7 @@ const file_users_proto_rawDesc = "" +
"\busername\x18\x01 \x01(\tR\busername\x12\x16\n" +
"\x06passwd\x18\x02 \x01(\tR\x06passwd\x12\x14\n" +
"\x05phone\x18\x03 \x01(\tR\x05phone\x12\x14\n" +
- "\x05vcode\x18\x04 \x01(\x05R\x05vcode\x12\x14\n" +
+ "\x05vcode\x18\x04 \x01(\tR\x05vcode\x12\x14\n" +
"\x05email\x18\x05 \x01(\tR\x05email\x12\x1c\n" +
"\trequestId\x18\x06 \x01(\tR\trequestId\" \n" +
"\fRegisterResp\x12\x10\n" +
diff --git a/app/users/rpc/pb/users_grpc.pb.go b/app/users/rpc/pb/users_grpc.pb.go
index 2918bff..4f7a28e 100644
--- a/app/users/rpc/pb/users_grpc.pb.go
+++ b/app/users/rpc/pb/users_grpc.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
-// - protoc v3.19.4
+// - protoc v5.29.6
// source: users.proto
package pb
diff --git a/cnpg_for_specific_namespace.yaml b/backup/cnpg_for_specific_namespace.yaml
similarity index 100%
rename from cnpg_for_specific_namespace.yaml
rename to backup/cnpg_for_specific_namespace.yaml
diff --git a/common/converter/README.md b/common/converter/README.md
index df38334..38fbe67 100644
--- a/common/converter/README.md
+++ b/common/converter/README.md
@@ -40,7 +40,7 @@ func StructToStruct(src, dst interface{}) error
```go
import "app/common/converter"
-// 单个 model 转 pb
+// 单个 models 转 pb
user, _ := m.FindOne(ctx, userId)
pbUser := &pb.Users{}
converter.StructToStruct(user, pbUser)
@@ -63,7 +63,7 @@ func SliceToSlice(src interface{}, dstSliceType interface{}) (interface{}, error
**示例:**
```go
-// 多个 model 转 pb
+// 多个 models 转 pb
users := []*models.Users{user1, user2, user3}
pbUsersIface, _ := converter.SliceToSlice(users, []*pb.Users{})
pbUsers := pbUsersIface.([]*pb.Users)
diff --git a/common/converter/generic.go b/common/converter/generic.go
index ca2f31b..5fdc89d 100644
--- a/common/converter/generic.go
+++ b/common/converter/generic.go
@@ -1,207 +1,207 @@
-package converter
-
-import (
- "database/sql"
- "reflect"
- "time"
-)
-
-// StructToStruct 通用结构体转换函数,利用反射将源结构体的字段值复制到目标结构体
-// src: 源结构体(通常是 model)
-// dst: 目标结构体(通常是 pb),必须是指针
-// 支持的自动转换:
-// - time.Time -> int64 (Unix 时间戳)
-// - sql.NullTime -> int64 (如果有效)
-// - sql.NullInt64 -> int64
-// - sql.NullString -> string
-// - 相同名称和兼容类型的字段
-func StructToStruct(src, dst interface{}) error {
- if src == nil {
- return nil
- }
-
- srcVal := reflect.ValueOf(src)
- dstVal := reflect.ValueOf(dst)
-
- // 确保 dst 是指针
- if dstVal.Kind() != reflect.Ptr {
- return newError("destination must be a pointer")
- }
-
- dstVal = dstVal.Elem()
-
- // 如果 src 是指针,解引用
- if srcVal.Kind() == reflect.Ptr {
- srcVal = srcVal.Elem()
- }
-
- // 都必须是结构体
- if srcVal.Kind() != reflect.Struct || dstVal.Kind() != reflect.Struct {
- return newError("both source and destination must be structs")
- }
-
- srcType := srcVal.Type()
-
- // 遍历源结构体的所有字段
- for i := 0; i < srcVal.NumField(); i++ {
- srcField := srcVal.Field(i)
- srcFieldName := srcType.Field(i).Name
-
- // 在目标结构体中查找同名字段
- dstField := dstVal.FieldByName(srcFieldName)
- if !dstField.IsValid() || !dstField.CanSet() {
- continue
- }
-
- // 进行类型转换和赋值
- if err := assignValue(srcField, dstField); err != nil {
- continue // 如果单个字段转换失败,继续处理其他字段
- }
- }
-
- return nil
-}
-
-// assignValue 尝试将源字段值赋给目标字段
-func assignValue(srcField, dstField reflect.Value) error {
- // 如果是可直接赋值的类型
- if srcField.Type() == dstField.Type() {
- dstField.Set(srcField)
- return nil
- }
-
- srcType := srcField.Type()
- dstType := dstField.Type()
-
- // 处理 time.Time -> int64 的转换
- if srcType == reflect.TypeOf(time.Time{}) && dstType.Kind() == reflect.Int64 {
- t := srcField.Interface().(time.Time)
- dstField.SetInt(t.Unix())
- return nil
- }
-
- // 处理 sql.NullTime -> int64 的转换
- if srcType == reflect.TypeOf(sql.NullTime{}) && dstType.Kind() == reflect.Int64 {
- nt := srcField.Interface().(sql.NullTime)
- if nt.Valid {
- dstField.SetInt(nt.Time.Unix())
- }
- return nil
- }
-
- // 处理 sql.NullTime -> time.Time 的转换
- if srcType == reflect.TypeOf(sql.NullTime{}) && dstType == reflect.TypeOf(time.Time{}) {
- nt := srcField.Interface().(sql.NullTime)
- if nt.Valid {
- dstField.Set(reflect.ValueOf(nt.Time))
- }
- return nil
- }
-
- // 处理 sql.NullInt64 -> int64 的转换
- if srcType == reflect.TypeOf(sql.NullInt64{}) && dstType.Kind() == reflect.Int64 {
- ni := srcField.Interface().(sql.NullInt64)
- if ni.Valid {
- dstField.SetInt(ni.Int64)
- }
- return nil
- }
-
- // 处理 sql.NullString -> string 的转换
- if srcType == reflect.TypeOf(sql.NullString{}) && dstType.Kind() == reflect.String {
- ns := srcField.Interface().(sql.NullString)
- if ns.Valid {
- dstField.SetString(ns.String)
- }
- return nil
- }
-
- // 处理 sql.NullBool -> bool 的转换
- if srcType == reflect.TypeOf(sql.NullBool{}) && dstType.Kind() == reflect.Bool {
- nb := srcField.Interface().(sql.NullBool)
- if nb.Valid {
- dstField.SetBool(nb.Bool)
- }
- return nil
- }
-
- // 处理 int -> int64 的转换
- if srcType.Kind() == reflect.Int && dstType.Kind() == reflect.Int64 {
- dstField.SetInt(int64(srcField.Int()))
- return nil
- }
-
- // 处理 int64 -> int 的转换
- if srcType.Kind() == reflect.Int64 && dstType.Kind() == reflect.Int {
- dstField.SetInt(srcField.Int())
- return nil
- }
-
- // 处理 string -> string(某些情况下可能存在复制)
- if srcType.Kind() == reflect.String && dstType.Kind() == reflect.String {
- dstField.SetString(srcField.String())
- return nil
- }
-
- // 处理 bool -> bool
- if srcType.Kind() == reflect.Bool && dstType.Kind() == reflect.Bool {
- dstField.SetBool(srcField.Bool())
- return nil
- }
-
- return newError("unsupported type conversion from " + srcType.String() + " to " + dstType.String())
-}
-
-// SliceToSlice 通用切片转换函数,使用 StructToStruct 转换每个元素
-func SliceToSlice(src interface{}, dstSliceType interface{}) (interface{}, error) {
- srcVal := reflect.ValueOf(src)
-
- // src 必须是切片
- if srcVal.Kind() != reflect.Slice {
- return nil, newError("source must be a slice")
- }
-
- // 获取原始 dst slice type
- dstSliceVal := reflect.ValueOf(dstSliceType)
- if dstSliceVal.Kind() != reflect.Slice {
- return nil, newError("dstSliceType must be a slice type")
- }
-
- dstSliceElemType := dstSliceVal.Type().Elem()
-
- // 创建新的目标切片
- dstSlice := reflect.MakeSlice(dstSliceVal.Type(), srcVal.Len(), srcVal.Len())
-
- // 逐个转换元素
- for i := 0; i < srcVal.Len(); i++ {
- srcElem := srcVal.Index(i)
- dstElem := reflect.New(dstSliceElemType)
-
- // 如果 src 元素是指针,需要解引用
- if srcElem.Kind() == reflect.Ptr {
- srcElem = srcElem.Elem()
- }
-
- // 转换单个元素
- dstElemIface := dstElem.Interface()
- if err := StructToStruct(srcElem.Interface(), dstElemIface); err != nil {
- return nil, err
- }
-
- dstSlice.Index(i).Set(dstElem.Elem())
- }
-
- return dstSlice.Interface(), nil
-}
-
-type Error struct {
- msg string
-}
-
-func (e *Error) Error() string {
- return e.msg
-}
-
-func newError(msg string) error {
- return &Error{msg: msg}
-}
+package converter
+
+import (
+ "database/sql"
+ "reflect"
+ "time"
+)
+
+// StructToStruct 通用结构体转换函数,利用反射将源结构体的字段值复制到目标结构体
+// src: 源结构体(通常是 models)
+// dst: 目标结构体(通常是 pb),必须是指针
+// 支持的自动转换:
+// - time.Time -> int64 (Unix 时间戳)
+// - sql.NullTime -> int64 (如果有效)
+// - sql.NullInt64 -> int64
+// - sql.NullString -> string
+// - 相同名称和兼容类型的字段
+func StructToStruct(src, dst interface{}) error {
+ if src == nil {
+ return nil
+ }
+
+ srcVal := reflect.ValueOf(src)
+ dstVal := reflect.ValueOf(dst)
+
+ // 确保 dst 是指针
+ if dstVal.Kind() != reflect.Ptr {
+ return newError("destination must be a pointer")
+ }
+
+ dstVal = dstVal.Elem()
+
+ // 如果 src 是指针,解引用
+ if srcVal.Kind() == reflect.Ptr {
+ srcVal = srcVal.Elem()
+ }
+
+ // 都必须是结构体
+ if srcVal.Kind() != reflect.Struct || dstVal.Kind() != reflect.Struct {
+ return newError("both source and destination must be structs")
+ }
+
+ srcType := srcVal.Type()
+
+ // 遍历源结构体的所有字段
+ for i := 0; i < srcVal.NumField(); i++ {
+ srcField := srcVal.Field(i)
+ srcFieldName := srcType.Field(i).Name
+
+ // 在目标结构体中查找同名字段
+ dstField := dstVal.FieldByName(srcFieldName)
+ if !dstField.IsValid() || !dstField.CanSet() {
+ continue
+ }
+
+ // 进行类型转换和赋值
+ if err := assignValue(srcField, dstField); err != nil {
+ continue // 如果单个字段转换失败,继续处理其他字段
+ }
+ }
+
+ return nil
+}
+
+// assignValue 尝试将源字段值赋给目标字段
+func assignValue(srcField, dstField reflect.Value) error {
+ // 如果是可直接赋值的类型
+ if srcField.Type() == dstField.Type() {
+ dstField.Set(srcField)
+ return nil
+ }
+
+ srcType := srcField.Type()
+ dstType := dstField.Type()
+
+ // 处理 time.Time -> int64 的转换
+ if srcType == reflect.TypeOf(time.Time{}) && dstType.Kind() == reflect.Int64 {
+ t := srcField.Interface().(time.Time)
+ dstField.SetInt(t.Unix())
+ return nil
+ }
+
+ // 处理 sql.NullTime -> int64 的转换
+ if srcType == reflect.TypeOf(sql.NullTime{}) && dstType.Kind() == reflect.Int64 {
+ nt := srcField.Interface().(sql.NullTime)
+ if nt.Valid {
+ dstField.SetInt(nt.Time.Unix())
+ }
+ return nil
+ }
+
+ // 处理 sql.NullTime -> time.Time 的转换
+ if srcType == reflect.TypeOf(sql.NullTime{}) && dstType == reflect.TypeOf(time.Time{}) {
+ nt := srcField.Interface().(sql.NullTime)
+ if nt.Valid {
+ dstField.Set(reflect.ValueOf(nt.Time))
+ }
+ return nil
+ }
+
+ // 处理 sql.NullInt64 -> int64 的转换
+ if srcType == reflect.TypeOf(sql.NullInt64{}) && dstType.Kind() == reflect.Int64 {
+ ni := srcField.Interface().(sql.NullInt64)
+ if ni.Valid {
+ dstField.SetInt(ni.Int64)
+ }
+ return nil
+ }
+
+ // 处理 sql.NullString -> string 的转换
+ if srcType == reflect.TypeOf(sql.NullString{}) && dstType.Kind() == reflect.String {
+ ns := srcField.Interface().(sql.NullString)
+ if ns.Valid {
+ dstField.SetString(ns.String)
+ }
+ return nil
+ }
+
+ // 处理 sql.NullBool -> bool 的转换
+ if srcType == reflect.TypeOf(sql.NullBool{}) && dstType.Kind() == reflect.Bool {
+ nb := srcField.Interface().(sql.NullBool)
+ if nb.Valid {
+ dstField.SetBool(nb.Bool)
+ }
+ return nil
+ }
+
+ // 处理 int -> int64 的转换
+ if srcType.Kind() == reflect.Int && dstType.Kind() == reflect.Int64 {
+ dstField.SetInt(int64(srcField.Int()))
+ return nil
+ }
+
+ // 处理 int64 -> int 的转换
+ if srcType.Kind() == reflect.Int64 && dstType.Kind() == reflect.Int {
+ dstField.SetInt(srcField.Int())
+ return nil
+ }
+
+ // 处理 string -> string(某些情况下可能存在复制)
+ if srcType.Kind() == reflect.String && dstType.Kind() == reflect.String {
+ dstField.SetString(srcField.String())
+ return nil
+ }
+
+ // 处理 bool -> bool
+ if srcType.Kind() == reflect.Bool && dstType.Kind() == reflect.Bool {
+ dstField.SetBool(srcField.Bool())
+ return nil
+ }
+
+ return newError("unsupported type conversion from " + srcType.String() + " to " + dstType.String())
+}
+
+// SliceToSlice 通用切片转换函数,使用 StructToStruct 转换每个元素
+func SliceToSlice(src interface{}, dstSliceType interface{}) (interface{}, error) {
+ srcVal := reflect.ValueOf(src)
+
+ // src 必须是切片
+ if srcVal.Kind() != reflect.Slice {
+ return nil, newError("source must be a slice")
+ }
+
+ // 获取原始 dst slice type
+ dstSliceVal := reflect.ValueOf(dstSliceType)
+ if dstSliceVal.Kind() != reflect.Slice {
+ return nil, newError("dstSliceType must be a slice type")
+ }
+
+ dstSliceElemType := dstSliceVal.Type().Elem()
+
+ // 创建新的目标切片
+ dstSlice := reflect.MakeSlice(dstSliceVal.Type(), srcVal.Len(), srcVal.Len())
+
+ // 逐个转换元素
+ for i := 0; i < srcVal.Len(); i++ {
+ srcElem := srcVal.Index(i)
+ dstElem := reflect.New(dstSliceElemType)
+
+ // 如果 src 元素是指针,需要解引用
+ if srcElem.Kind() == reflect.Ptr {
+ srcElem = srcElem.Elem()
+ }
+
+ // 转换单个元素
+ dstElemIface := dstElem.Interface()
+ if err := StructToStruct(srcElem.Interface(), dstElemIface); err != nil {
+ return nil, err
+ }
+
+ dstSlice.Index(i).Set(dstElem.Elem())
+ }
+
+ return dstSlice.Interface(), nil
+}
+
+type Error struct {
+ msg string
+}
+
+func (e *Error) Error() string {
+ return e.msg
+}
+
+func newError(msg string) error {
+ return &Error{msg: msg}
+}
diff --git a/app/users/api/internal/contextx/contextx.go b/common/utils/contextx/contextx.go
similarity index 53%
rename from app/users/api/internal/contextx/contextx.go
rename to common/utils/contextx/contextx.go
index cfed81d..9b9d29c 100644
--- a/app/users/api/internal/contextx/contextx.go
+++ b/common/utils/contextx/contextx.go
@@ -3,6 +3,15 @@ package contextx
import (
"context"
"errors"
+
+ "github.com/zeromicro/go-zero/core/logx"
+)
+
+var (
+ ERRILLEGALUSER = errors.New("illegal user")
+ ERRILLEGALTOKEN = errors.New("illegal token")
+ ERRILLEGALREQUESTID = errors.New("illegal request id")
+ ERRILLEGALISADMIN = errors.New("illegal is_admin")
)
func WithRequestId(c context.Context, requestId string) context.Context {
@@ -41,6 +50,8 @@ func UserIDFrom(c context.Context) (int64, error) {
}
}
+// request_id is used for tracing and logging, not for authentication or authorization,
+// so it can be set by clients or generated by the server.
func WithRequestID(c context.Context, requestID string) context.Context {
return context.WithValue(c, "request_id", requestID)
}
@@ -52,3 +63,34 @@ func RequestIDFrom(c context.Context) (string, error) {
return requestID, nil
}
}
+
+func WithIsAdmin(c context.Context, isAdmin bool) context.Context {
+ return context.WithValue(c, "is_admin", isAdmin)
+}
+
+func IsAdminFrom(c context.Context) (bool, error) {
+ if isAdmin, ok := c.Value("is_admin").(bool); !ok {
+ return false, errors.New("is_admin not found in context")
+ } else {
+ return isAdmin, nil
+ }
+}
+
+func AdminIdFrom(c context.Context) (adminId int64, err error) {
+
+ adminId, err = UserIDFrom(c)
+ if err != nil {
+ logx.Errorf("get user id from context: %v", err)
+ return 0, ERRILLEGALUSER
+ }
+ isAdmin, err := IsAdminFrom(c)
+ if err != nil {
+ logx.Errorf("get isAdmin from context: %v", err)
+ return 0, ERRILLEGALUSER
+ }
+ if !isAdmin {
+ logx.Errorf("user %d is not admin", adminId)
+ return 0, ERRILLEGALUSER
+ }
+ return
+}
diff --git a/common/utils/httpx/httpx.go b/common/utils/httpx/httpx.go
new file mode 100644
index 0000000..8d480f6
--- /dev/null
+++ b/common/utils/httpx/httpx.go
@@ -0,0 +1,30 @@
+package httpx
+
+import (
+ "net/http"
+ "strconv"
+)
+
+func GetUserIdFromHeader(header http.Header) (int64, error) {
+ id := header.Get("x-auth-user-id")
+ if id == "" {
+ return 0, http.ErrNoCookie
+ }
+ intId, err := strconv.ParseInt(id, 10, 64)
+ if err != nil {
+ return 0, err
+ }
+ return intId, nil
+}
+
+func GetUserIsAdminFromHeader(header http.Header) (bool, error) {
+ isAdmin := header.Get("x-auth-is-admin")
+ if isAdmin == "" {
+ return false, nil
+ }
+ boolIsAdmin, err := strconv.ParseBool(isAdmin)
+ if err != nil {
+ return false, err
+ }
+ return boolIsAdmin, nil
+}
diff --git a/common/utils/password.go b/common/utils/pwdUtils/password.go
similarity index 93%
rename from common/utils/password.go
rename to common/utils/pwdUtils/password.go
index 96851d3..fcb0113 100644
--- a/common/utils/password.go
+++ b/common/utils/pwdUtils/password.go
@@ -1,25 +1,25 @@
-package utils
-
-import (
- "golang.org/x/crypto/bcrypt"
-)
-
-const (
- // bcrypt 密钥成本
- bcryptCost = bcrypt.DefaultCost
-)
-
-// HashPassword 对密码进行哈希加密
-func HashPassword(password string) (string, error) {
- hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
- if err != nil {
- return "", err
- }
- return string(hash), nil
-}
-
-// VerifyPassword 验证密码是否正确
-func VerifyPassword(hashedPassword, password string) bool {
- err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
- return err == nil
-}
+package pwdUtils
+
+import (
+ "golang.org/x/crypto/bcrypt"
+)
+
+const (
+ // bcrypt 密钥成本
+ bcryptCost = bcrypt.DefaultCost
+)
+
+// HashPassword 对密码进行哈希加密
+func HashPassword(password string) (string, error) {
+ hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
+ if err != nil {
+ return "", err
+ }
+ return string(hash), nil
+}
+
+// VerifyPassword 验证密码是否正确
+func VerifyPassword(hashedPassword, password string) bool {
+ err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
+ return err == nil
+}
diff --git a/common/utils/responses.go b/common/utils/responses/responses.go
similarity index 95%
rename from common/utils/responses.go
rename to common/utils/responses/responses.go
index 23c0693..5000c4e 100644
--- a/common/utils/responses.go
+++ b/common/utils/responses/responses.go
@@ -1,4 +1,4 @@
-package utils
+package responses
import "encoding/json"
diff --git a/deploy/k8s/envoy/envoy.yaml b/deploy/k8s/envoy/envoy.yaml
index 2923373..86aa780 100644
--- a/deploy/k8s/envoy/envoy.yaml
+++ b/deploy/k8s/envoy/envoy.yaml
@@ -49,7 +49,7 @@ data:
disabled: true
- match:
- prefix: /api/v1/auth/login
+ path: /api/v1/auth/login
route:
cluster: user_api_cluster
timeout: 30s
@@ -59,7 +59,7 @@ data:
disabled: true
- match:
- prefix: /api/v1/auth/register
+ path: /api/v1/auth/register
route:
cluster: user_api_cluster
timeout: 30s
@@ -79,7 +79,7 @@ data:
disabled: true
- match:
- path: /api/email/verification-code/send
+ path: /api/v1/email/verification-code/send
route:
cluster: email_api_cluster
timeout: 30s
diff --git a/desc/api/chat.api b/desc/api/chat.api
new file mode 100644
index 0000000..1989878
--- /dev/null
+++ b/desc/api/chat.api
@@ -0,0 +1,68 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ ChatSession {
+ Id string `json:"id"`
+ Type string `json:"type"` // order, consultation
+ OrderId string `json:"orderId,optional"`
+ Participants []SimpleUser `json:"participants"`
+ LastMessage string `json:"lastMessage"`
+ UnreadCount int `json:"unreadCount"`
+ }
+
+ ChatSessionListResp {
+ Items []ChatSession `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ ChatMessage {
+ Id string `json:"id"`
+ SessionId string `json:"sessionId"`
+ SenderId string `json:"senderId"`
+ Type string `json:"type"` // text, image, system
+ Content string `json:"content"`
+ CreatedAt string `json:"createdAt"`
+ }
+
+ ChatMessageListResp {
+ Items []ChatMessage `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ SendMessageReq {
+ Type string `json:"type"`
+ Content string `json:"content"`
+ }
+)
+
+@server(
+ prefix: api/v1/chat
+ group: chat
+ jwt: Auth
+)
+service juwan-api {
+ @doc "获取会话列表"
+ @handler ListSessions
+ get /sessions (PageReq) returns (ChatSessionListResp)
+
+ @doc "获取会话详情"
+ @handler GetSession
+ get /sessions/:id (EmptyResp) returns (ChatSession)
+
+ @doc "获取消息历史"
+ @handler ListMessages
+ get /sessions/:id/messages (PageReq) returns (ChatMessageListResp)
+
+ @doc "发送消息"
+ @handler SendMessage
+ post /sessions/:id/messages (SendMessageReq) returns (ChatMessage)
+
+ @doc "创建/获取订单会话"
+ @handler EnsureOrderSession
+ post /sessions/order (EmptyResp) returns (ChatSession)
+
+ @doc "创建咨询会话"
+ @handler CreateConsultation
+ post /sessions/consultation (EmptyResp) returns (ChatSession)
+}
\ No newline at end of file
diff --git a/desc/api/common.api b/desc/api/common.api
new file mode 100644
index 0000000..4ffa5c8
--- /dev/null
+++ b/desc/api/common.api
@@ -0,0 +1,47 @@
+syntax = "v1"
+
+info(
+ title: "公共定义"
+ desc: "Common structures"
+ author: "Juwan Team"
+ version: "1.0"
+)
+
+type (
+ // 分页请求基础
+ PageReq {
+ Offset int64 `form:"offset,default=0"`
+ Limit int64 `form:"limit,default=20"`
+ }
+
+ // 分页响应元数据
+ PageMeta {
+ Total int64 `json:"total"`
+ Offset int64 `json:"offset"`
+ Limit int64 `json:"limit"`
+ }
+
+ // 空响应
+ EmptyResp {}
+
+ // 核心用户画像 (被 Auth, Community, Shop 等多个服务引用)
+ UserProfile {
+ Id string `json:"id"`
+ Username string `json:"username"`
+ Nickname string `json:"nickname"`
+ Avatar string `json:"avatar"`
+ Role string `json:"role"` // consumer, player, owner, admin
+ VerifiedRoles []string `json:"verifiedRoles"`
+ VerificationStatus map[string]string `json:"verificationStatus"`
+ Phone string `json:"phone,optional"`
+ Bio string `json:"bio,optional"`
+ CreatedAt string `json:"createdAt"`
+ }
+
+ // 简略用户信息 (用于列表、聊天头像等)
+ SimpleUser {
+ Id string `json:"id"`
+ Nickname string `json:"nickname"`
+ Avatar string `json:"avatar"`
+ }
+)
\ No newline at end of file
diff --git a/desc/api/community.api b/desc/api/community.api
new file mode 100644
index 0000000..97a93cf
--- /dev/null
+++ b/desc/api/community.api
@@ -0,0 +1,115 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ Post {
+ Id string `json:"id"`
+ Title string `json:"title"`
+ Content string `json:"content"`
+ Images []string `json:"images"`
+ Tags []string `json:"tags"`
+ LikeCount int64 `json:"likeCount"`
+ CommentCount int64 `json:"commentCount"`
+ Liked bool `json:"liked"`
+ Author UserProfile `json:"author"`
+ CreatedAt string `json:"createdAt"`
+ }
+
+ CreatePostReq {
+ Title string `json:"title"`
+ Content string `json:"content"`
+ Images []string `json:"images"`
+ Tags []string `json:"tags"`
+ LinkedOrderId string `json:"linkedOrderId,optional"`
+ }
+
+ PostListReq {
+ PageReq
+ Tags string `form:"tags,optional"`
+ SortBy string `form:"sortBy,optional"`
+ }
+
+ PostListResp {
+ Items []Post `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ Comment {
+ Id string `json:"id"`
+ Content string `json:"content"`
+ Author UserProfile `json:"author"`
+ LikeCount int64 `json:"likeCount"`
+ Liked bool `json:"liked"`
+ CreatedAt string `json:"createdAt"`
+ }
+
+ CommentListResp {
+ Items []Comment `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ CreateCommentReq {
+ Content string `json:"content"`
+ }
+)
+
+@server(
+ prefix: api/v1
+ group: community
+)
+service juwan-api {
+ @doc "获取帖子列表"
+ @handler ListPosts
+ get /posts (PostListReq) returns (PostListResp)
+
+ @doc "获取帖子详情"
+ @handler GetPost
+ get /posts/:id (EmptyResp) returns (Post)
+
+ @doc "获取帖子评论"
+ @handler ListComments
+ get /posts/:id/comments (PageReq) returns (CommentListResp)
+
+ @doc "获取用户帖子"
+ @handler ListUserPosts
+ get /users/:id/posts (PageReq) returns (PostListResp)
+}
+
+@server(
+ prefix: api/v1
+ group: community
+ jwt: Auth
+)
+service juwan-api {
+ @doc "发布帖子"
+ @handler CreatePost
+ post /posts (CreatePostReq) returns (Post)
+
+ @doc "点赞帖子"
+ @handler LikePost
+ post /posts/:id/like (EmptyResp) returns (EmptyResp)
+
+ @doc "取消点赞帖子"
+ @handler UnlikePost
+ delete /posts/:id/like (EmptyResp) returns (EmptyResp)
+
+ @doc "置顶帖子"
+ @handler PinPost
+ post /posts/:id/pin (EmptyResp) returns (EmptyResp)
+
+ @doc "取消置顶"
+ @handler UnpinPost
+ delete /posts/:id/pin (EmptyResp) returns (EmptyResp)
+
+ @doc "发表评论"
+ @handler CreateComment
+ post /posts/:id/comments (CreateCommentReq) returns (Comment)
+
+ @doc "点赞评论"
+ @handler LikeComment
+ post /comments/:id/like (EmptyResp) returns (EmptyResp)
+
+ @doc "取消点赞评论"
+ @handler UnlikeComment
+ delete /comments/:id/like (EmptyResp) returns (EmptyResp)
+}
\ No newline at end of file
diff --git a/desc/api/dispute.api b/desc/api/dispute.api
new file mode 100644
index 0000000..7798e32
--- /dev/null
+++ b/desc/api/dispute.api
@@ -0,0 +1,60 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ Dispute {
+ Id string `json:"id"`
+ OrderId string `json:"orderId"`
+ Reason string `json:"reason"`
+ Status string `json:"status"`
+ Evidence []string `json:"evidence"`
+ Result string `json:"result,optional"`
+ CreatedAt string `json:"createdAt"`
+ }
+
+ DisputeListResp {
+ Items []Dispute `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ CreateDisputeReq {
+ Reason string `json:"reason"`
+ Evidence []string `json:"evidence"`
+ }
+
+ DisputeResponseReq {
+ Reason string `json:"reason"`
+ Evidence []string `json:"evidence"`
+ }
+
+ AppealReq {
+ Reason string `json:"reason"`
+ }
+)
+
+@server(
+ prefix: api/v1
+ group: dispute
+ jwt: Auth
+)
+service juwan-api {
+ @doc "获取争议列表"
+ @handler ListDisputes
+ get /disputes (PageReq) returns (DisputeListResp)
+
+ @doc "获取订单争议"
+ @handler GetOrderDispute
+ get /orders/:id/dispute (EmptyResp) returns (Dispute)
+
+ @doc "发起争议"
+ @handler CreateDispute
+ post /orders/:id/dispute (CreateDisputeReq) returns (EmptyResp)
+
+ @doc "回应争议"
+ @handler RespondDispute
+ post /disputes/:id/response (DisputeResponseReq) returns (EmptyResp)
+
+ @doc "申诉"
+ @handler AppealDispute
+ post /disputes/:id/appeal (AppealReq) returns (EmptyResp)
+}
\ No newline at end of file
diff --git a/desc/api/email.api b/desc/api/email.api
index 72d22f0..e4f171d 100644
--- a/desc/api/email.api
+++ b/desc/api/email.api
@@ -7,7 +7,7 @@ info (
)
type (
- EmptyResp {}
+ EmptyResp {}
ForgotPasswordReq {
Email string `json:"email"`
}
@@ -24,7 +24,7 @@ type (
@server (
group: email
- prefix: /api/email
+ prefix: /api/v1/email
middleware: Logger
)
service email-api {
@@ -34,14 +34,11 @@ service email-api {
)
@handler SendVerificationCode
post /verification-code/send (SendVerificationCodeReq) returns (SendVerificationCodeResp)
-
-
}
@server (
- group: user
+ group: email
prefix: /api/v1/auth
-
)
service email-api {
@doc "忘记密码-发送验证码"
diff --git a/desc/api/game.api b/desc/api/game.api
new file mode 100644
index 0000000..5254893
--- /dev/null
+++ b/desc/api/game.api
@@ -0,0 +1,30 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ Game {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Icon string `json:"icon"`
+ Category string `json:"category"`
+ }
+
+ GameListResp {
+ Items []Game `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+)
+
+@server(
+ prefix: api/v1/games
+ group: game
+)
+service juwan-api {
+ @doc "获取游戏列表"
+ @handler ListGames
+ get / (PageReq) returns (GameListResp)
+
+ @doc "获取游戏详情"
+ @handler GetGame
+ get /:id (EmptyResp) returns (Game)
+}
\ No newline at end of file
diff --git a/desc/api/notification.api b/desc/api/notification.api
new file mode 100644
index 0000000..253af69
--- /dev/null
+++ b/desc/api/notification.api
@@ -0,0 +1,38 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ Notification {
+ Id string `json:"id"`
+ Type string `json:"type"`
+ Title string `json:"title"`
+ Content string `json:"content"`
+ Read bool `json:"read"`
+ Link string `json:"link,optional"`
+ CreatedAt string `json:"createdAt"`
+ }
+
+ NotificationListResp {
+ Items []Notification `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+)
+
+@server(
+ prefix: api/v1
+ group: notification
+ jwt: Auth
+)
+service juwan-api {
+ @doc "获取通知列表"
+ @handler ListNotifications
+ get /notifications (PageReq) returns (NotificationListResp)
+
+ @doc "标记已读"
+ @handler ReadNotification
+ put /notifications/:id/read (EmptyResp) returns (EmptyResp)
+
+ @doc "全部已读"
+ @handler ReadAllNotifications
+ put /notifications/read-all (EmptyResp) returns (EmptyResp)
+}
\ No newline at end of file
diff --git a/desc/api/objectstory.api b/desc/api/objectstory.api
new file mode 100644
index 0000000..5bcd95d
--- /dev/null
+++ b/desc/api/objectstory.api
@@ -0,0 +1,40 @@
+syntax = "v1"
+
+info (
+ title: "文件服务"
+ desc: "处理文件上传与获取"
+ author: "Asadz"
+ version: "v1"
+)
+
+type (
+ // 上传请求参数(File文件流在Handler中通过 r.FormFile 获取)
+ UploadReq {
+ Type string `form:"type,options=avatar|chat|post|verification|dispute"` // 文件类型限制
+ }
+ // 上传响应
+ UploadResp {
+ Url string `json:"url"` // 返回 CDN 地址或访问地址
+ }
+ // 获取文件请求(用于私有文件或代理访问)
+ GetFileReq {
+ FileId string `path:"fileId"`
+ }
+)
+
+@server (
+ prefix: /api/v1
+ group: file
+ jwt: Logger
+ middleware: FileSizeLimit // 建议添加中间件限制文件大小
+)
+service file-api {
+ @doc "文件上传接口"
+ @handler Upload
+ post /upload (UploadReq) returns (UploadResp)
+
+ @doc "文件获取接口 (如果是私有文件,通过此接口获取或重定向)"
+ @handler GetFile
+ get /files/:fileId (GetFileReq)
+}
+
diff --git a/desc/api/order.api b/desc/api/order.api
index e69de29..cf03c16 100644
--- a/desc/api/order.api
+++ b/desc/api/order.api
@@ -0,0 +1,93 @@
+syntax = "v1"
+import "common.api"
+import "player.api" // 为了使用 PlayerService 定义
+
+type (
+ Order {
+ Id string `json:"id"`
+ ConsumerId string `json:"consumerId"`
+ ConsumerName string `json:"consumerName"`
+ PlayerId string `json:"playerId"`
+ PlayerName string `json:"playerName"`
+ ShopId string `json:"shopId,optional"`
+ ShopName string `json:"shopName,optional"`
+ Service PlayerService `json:"service"`
+ Status string `json:"status"`
+ TotalPrice float64 `json:"totalPrice"`
+ Note string `json:"note,optional"`
+ CreatedAt string `json:"createdAt"`
+ AcceptedAt string `json:"acceptedAt,optional"`
+ CompletedAt string `json:"completedAt,optional"`
+ }
+
+ OrderListReq {
+ PageReq
+ Role string `form:"role"` // consumer, player, owner
+ Status string `form:"status,optional"`
+ }
+
+ OrderListResp {
+ Items []Order `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ CreateOrderReq {
+ PlayerId string `json:"playerId"`
+ ShopId string `json:"shopId,optional"`
+ ServiceId string `json:"serviceId"`
+ Quantity int `json:"quantity"`
+ Note string `json:"note,optional"`
+ }
+
+ CreateOrderResp {
+ Ok bool `json:"ok"`
+ Order Order `json:"order"`
+ }
+)
+
+@server(
+ prefix: api/v1/orders
+ group: order
+ jwt: Auth
+)
+service juwan-api {
+ @doc "获取订单列表"
+ @handler ListOrders
+ get / (OrderListReq) returns (OrderListResp)
+
+ @doc "获取订单详情"
+ @handler GetOrder
+ get /:id (EmptyResp) returns (Order)
+
+ @doc "创建订单"
+ @handler CreateOrder
+ post / (CreateOrderReq) returns (CreateOrderResp)
+
+ @doc "创建并支付订单"
+ @handler CreateAndPayOrder
+ post /paid (CreateOrderReq) returns (CreateOrderResp)
+
+ @doc "支付订单"
+ @handler PayOrder
+ post /:id/pay (EmptyResp) returns (EmptyResp)
+
+ @doc "接单"
+ @handler AcceptOrder
+ post /:id/accept (EmptyResp) returns (EmptyResp)
+
+ @doc "申请结算"
+ @handler RequestCloseOrder
+ post /:id/request-close (EmptyResp) returns (EmptyResp)
+
+ @doc "确认结算"
+ @handler ConfirmCloseOrder
+ post /:id/confirm-close (EmptyResp) returns (EmptyResp)
+
+ @doc "取消订单"
+ @handler CancelOrder
+ post /:id/cancel (EmptyResp) returns (EmptyResp)
+
+ @doc "再来一单"
+ @handler Reorder
+ post /:id/reorder (EmptyResp) returns (CreateOrderResp)
+}
\ No newline at end of file
diff --git a/desc/api/player.api b/desc/api/player.api
new file mode 100644
index 0000000..a03f1b0
--- /dev/null
+++ b/desc/api/player.api
@@ -0,0 +1,110 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ PlayerService {
+ Id string `json:"id"`
+ PlayerId string `json:"playerId"`
+ GameId string `json:"gameId"`
+ GameName string `json:"gameName"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ Price float64 `json:"price"`
+ Unit string `json:"unit"`
+ RankRange string `json:"rankRange,optional"`
+ Availability []string `json:"availability"`
+ }
+
+ PlayerServiceListResp {
+ Items []PlayerService `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ CreateServiceReq {
+ GameId string `json:"gameId"`
+ Title string `json:"title"`
+ Description string `json:"description,optional"`
+ Price float64 `json:"price"`
+ Unit string `json:"unit"`
+ RankRange string `json:"rankRange,optional"`
+ Availability []string `json:"availability,optional"`
+ }
+
+ PlayerProfile {
+ Id string `json:"id"`
+ User UserProfile `json:"user"`
+ Rating float64 `json:"rating"`
+ TotalOrders int64 `json:"totalOrders"`
+ CompletionRate float64 `json:"completionRate"`
+ Status string `json:"status"`
+ Games []string `json:"games"`
+ Services []PlayerService `json:"services"`
+ ShopId string `json:"shopId,optional"`
+ ShopName string `json:"shopName,optional"`
+ Tags []string `json:"tags"`
+ }
+
+ PlayerListReq {
+ PageReq
+ GameId string `form:"gameId,optional"`
+ Gender int `form:"gender,optional"`
+ }
+
+ PlayerListResp {
+ Items []PlayerProfile `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ UpdatePlayerStatusReq {
+ Status string `json:"status"`
+ }
+)
+
+@server(
+ prefix: api/v1
+ group: player
+)
+service juwan-api {
+ @doc "获取打手列表"
+ @handler ListPlayers
+ get /players (PlayerListReq) returns (PlayerListResp)
+
+ @doc "获取打手详情"
+ @handler GetPlayer
+ get /players/:id (EmptyResp) returns (PlayerProfile)
+
+ @doc "获取所有服务列表"
+ @handler ListServices
+ get /services (PageReq) returns (PlayerServiceListResp)
+
+ @doc "获取服务详情"
+ @handler GetService
+ get /services/:id (EmptyResp) returns (PlayerService)
+
+ @doc "获取指定打手的服务列表"
+ @handler ListPlayerServices
+ get /players/:id/services (PageReq) returns (PlayerServiceListResp)
+}
+
+@server(
+ prefix: api/v1
+ group: player
+ jwt: Auth
+)
+service juwan-api {
+ @doc "更新接单状态"
+ @handler UpdatePlayerStatus
+ put /players/me/status (UpdatePlayerStatusReq) returns (EmptyResp)
+
+ @doc "创建服务"
+ @handler CreateService
+ post /services (CreateServiceReq) returns (PlayerService)
+
+ @doc "更新服务"
+ @handler UpdateService
+ put /services/:id (CreateServiceReq) returns (PlayerService)
+
+ @doc "删除服务"
+ @handler DeleteService
+ delete /services/:id (EmptyResp) returns (EmptyResp)
+}
\ No newline at end of file
diff --git a/desc/api/review.api b/desc/api/review.api
new file mode 100644
index 0000000..6f0d8e1
--- /dev/null
+++ b/desc/api/review.api
@@ -0,0 +1,54 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ Review {
+ Id string `json:"id"`
+ OrderId string `json:"orderId"`
+ FromUserId string `json:"fromUserId"`
+ FromUserName string `json:"fromUserName"`
+ Rating int `json:"rating"`
+ Content string `json:"content"`
+ Sealed bool `json:"sealed"`
+ CreatedAt string `json:"createdAt"`
+ }
+
+ ReviewListResp {
+ Items []Review `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ SubmitReviewReq {
+ Rating int `json:"rating"`
+ Content string `json:"content,optional"`
+ }
+)
+
+@server(
+ prefix: api/v1
+ group: review
+ jwt: Auth
+)
+service juwan-api {
+ @doc "提交评价"
+ @handler SubmitReview
+ post /orders/:id/review (SubmitReviewReq) returns (EmptyResp)
+
+ @doc "获取订单评价"
+ @handler GetOrderReviews
+ get /orders/:id/reviews (EmptyResp) returns ([]Review)
+}
+
+@server(
+ prefix: api/v1
+ group: review
+)
+service juwan-api {
+ @doc "获取公开评价列表"
+ @handler ListReviews
+ get /reviews (PageReq) returns (ReviewListResp)
+
+ @doc "获取用户收到的评价"
+ @handler ListUserReviews
+ get /users/:id/reviews (PageReq) returns (ReviewListResp)
+}
\ No newline at end of file
diff --git a/desc/api/search.api b/desc/api/search.api
new file mode 100644
index 0000000..41c9142
--- /dev/null
+++ b/desc/api/search.api
@@ -0,0 +1,69 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ SearchReq {
+ PageReq
+ Q string `form:"q"`
+ MinPrice float64 `form:"min,optional"`
+ MaxPrice float64 `form:"max,optional"`
+ OnlyOnline bool `form:"onlyOnline,optional"`
+ Sort string `form:"sort,optional"`
+ }
+
+ SearchResp {
+ Items []interface{} `json:"items"` // Mixed items
+ Meta PageMeta `json:"meta"`
+ }
+
+ FavoriteReq {
+ TargetType string `json:"targetType"` // player, shop
+ TargetId string `json:"targetId"`
+ }
+
+ FavoriteCheckReq {
+ TargetType string `form:"targetType"`
+ TargetId string `form:"targetId"`
+ }
+
+ FavoriteCheckResp {
+ Favorited bool `json:"favorited"`
+ }
+)
+
+@server(
+ prefix: api/v1
+ group: search
+)
+service juwan-api {
+ @doc "统一搜索"
+ @handler Search
+ get /search (SearchReq) returns (SearchResp)
+
+ @doc "首页推荐"
+ @handler Recommendations
+ get /recommendations/home (PageReq) returns (SearchResp)
+}
+
+@server(
+ prefix: api/v1
+ group: favorites
+ jwt: Auth
+)
+service juwan-api {
+ @doc "获取收藏列表"
+ @handler ListFavorites
+ get /favorites (PageReq) returns (SearchResp)
+
+ @doc "添加收藏"
+ @handler AddFavorite
+ post /favorites (FavoriteReq) returns (EmptyResp)
+
+ @doc "取消收藏"
+ @handler RemoveFavorite
+ delete /favorites/:id (EmptyResp) returns (EmptyResp)
+
+ @doc "检查收藏状态"
+ @handler CheckFavorite
+ get /users/:id/favorites/check (FavoriteCheckReq) returns (FavoriteCheckResp)
+}
\ No newline at end of file
diff --git a/desc/api/shop.api b/desc/api/shop.api
new file mode 100644
index 0000000..c86767d
--- /dev/null
+++ b/desc/api/shop.api
@@ -0,0 +1,130 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ ShopProfile {
+ Id string `json:"id"`
+ Owner UserProfile `json:"owner"`
+ Name string `json:"name"`
+ Banner string `json:"banner,optional"`
+ Description string `json:"description"`
+ Rating float64 `json:"rating"`
+ TotalOrders int64 `json:"totalOrders"`
+ PlayerCount int64 `json:"playerCount"`
+ CommissionType string `json:"commissionType"`
+ CommissionValue float64 `json:"commissionValue"`
+ Announcements []string `json:"announcements"`
+ TemplateConfig interface{} `json:"templateConfig"`
+ }
+
+ ShopListResp {
+ Items []ShopProfile `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ CreateShopReq {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ CommissionType string `json:"commissionType"`
+ CommissionValue float64 `json:"commissionValue"`
+ }
+
+ UpdateShopReq {
+ Name string `json:"name,optional"`
+ Description string `json:"description,optional"`
+ CommissionType string `json:"commissionType,optional"`
+ CommissionValue float64 `json:"commissionValue,optional"`
+ AllowMultiShop bool `json:"allowMultiShop,optional"`
+ AllowIndependentOrders bool `json:"allowIndependentOrders,optional"`
+ DispatchMode string `json:"dispatchMode,optional"`
+ }
+
+ UpdateTemplateReq {
+ Sections interface{} `json:"sections"`
+ }
+
+ AnnouncementReq {
+ Content string `json:"content"`
+ }
+
+ IncomeStatsResp {
+ MonthlyIncome float64 `json:"monthlyIncome"`
+ PendingSettlement float64 `json:"pendingSettlement"`
+ TotalWithdrawn float64 `json:"totalWithdrawn"`
+ TotalOrders int64 `json:"totalOrders"`
+ CompletedOrders int64 `json:"completedOrders"`
+ }
+
+ InvitationReq {
+ PlayerId string `json:"playerId"`
+ }
+)
+
+@server(
+ prefix: api/v1
+ group: shop
+)
+service juwan-api {
+ @doc "获取店铺列表"
+ @handler ListShops
+ get /shops (PageReq) returns (ShopListResp)
+
+ @doc "获取店铺详情"
+ @handler GetShop
+ get /shops/:id (EmptyResp) returns (ShopProfile)
+
+ @doc "获取店长的店铺"
+ @handler GetUserShop
+ get /users/:id/shop (EmptyResp) returns (ShopProfile)
+}
+
+@server(
+ prefix: api/v1
+ group: shop
+ jwt: Auth
+)
+service juwan-api {
+ @doc "创建店铺"
+ @handler CreateShop
+ post /shops (CreateShopReq) returns (ShopProfile)
+
+ @doc "获取当前用户的店铺"
+ @handler GetMyShop
+ get /shops/mine (EmptyResp) returns (ShopProfile)
+
+ @doc "更新店铺信息"
+ @handler UpdateShop
+ put /shops/:id (UpdateShopReq) returns (ShopProfile)
+
+ @doc "更新店铺模板"
+ @handler UpdateShopTemplate
+ put /shops/:id/template (UpdateTemplateReq) returns (EmptyResp)
+
+ @doc "新增店铺公告"
+ @handler AddAnnouncement
+ post /shops/:id/announcements (AnnouncementReq) returns (EmptyResp)
+
+ @doc "删除店铺公告"
+ @handler DeleteAnnouncement
+ delete /shops/:id/announcements/:index (EmptyResp) returns (EmptyResp)
+
+ @doc "邀请打手"
+ @handler InvitePlayer
+ post /shops/:id/invitations (InvitationReq) returns (EmptyResp)
+
+ @doc "接受邀请"
+ @handler AcceptInvitation
+ post /shops/invitations/:id/accept (EmptyResp) returns (EmptyResp)
+
+ @doc "拒绝邀请"
+ @handler RejectInvitation
+ delete /shops/invitations/:id (EmptyResp) returns (EmptyResp)
+
+ @doc "移除打手"
+ @handler RemovePlayer
+ delete /shops/:id/players/:playerId (EmptyResp) returns (EmptyResp)
+
+ @doc "获取收入统计"
+ @handler GetShopIncomeStats
+ get /shops/:id/income-stats (EmptyResp) returns (IncomeStatsResp)
+}
\ No newline at end of file
diff --git a/desc/api/user_verifications.api b/desc/api/user_verifications.api
new file mode 100644
index 0000000..685a5a6
--- /dev/null
+++ b/desc/api/user_verifications.api
@@ -0,0 +1,55 @@
+syntax = "v1"
+
+info (
+ title: "聚玩认证审核服务"
+ desc: "处理用户角色认证申请(打手/店长)及管理员审核流程"
+ author: "Asadz"
+ version: "1.0"
+)
+
+// =================================================================================
+// 数据结构定义 (Data Structures)
+// =================================================================================
+
+// =================================================================================
+// 用户端接口 (User Side)
+// 路径前缀: /api/v1/users
+// =================================================================================
+@server (
+ group: verification_user
+ prefix: /api/v1/users
+ jwt: Auth // 必须登录
+)
+service verification-api {
+ @doc "提交或修改角色认证申请 (支持幂等更新)"
+ @handler ApplyVerification
+ post /me/verification (ApplyVerificationReq) returns (VerificationEmptyResp)
+
+ @doc "获取我的所有认证状态"
+ @handler GetMyVerifications
+ get /me/verification returns (GetMyVerificationsResp)
+}
+
+// =================================================================================
+// 管理端接口 (Admin Side)
+// 路径前缀: /api/v1/admin
+// =================================================================================
+@server (
+ group: verification_admin
+ prefix: /api/v1/admin
+ jwt: Auth // 需要登录,且 Logic 层需校验 IsAdmin
+)
+service verification-api {
+ @doc "管理员获取认证申请列表 (分页)"
+ @handler GetVerifications
+ get /verifications (GetPendingListReq) returns (GetPendingListResp)
+
+ @doc "管理员通过申请"
+ @handler ApproveVerification
+ post /verifications/:id/approve (VerificationIdReq) returns (VerificationEmptyResp)
+
+ @doc "管理员驳回申请"
+ @handler RejectVerification
+ post /verifications/:id/reject (RejectVerificationReq) returns (VerificationEmptyResp)
+}
+
diff --git a/desc/api/users.api b/desc/api/users.api
index 181c728..0dee23e 100644
--- a/desc/api/users.api
+++ b/desc/api/users.api
@@ -1,164 +1,269 @@
syntax = "v1"
-info(
- author: "Asadz"
- date: "2024-06-19"
- version: "1.0"
+info (
+ title: "聚玩用户服务"
+ desc: "处理用户注册、登录、个人信息管理及关注系统"
+ author: "Asadz"
+ version: "1.0"
)
type (
- EmptyResp {
- }
- SearchUserResp {
- Username string `json:"username"`
- UserId int64 `json:"userId"`
- Nickname string `json:"nickname"`
- Avatar string `json:"avatar"`
- Bio string `json:"bio"`
- Page int64 `json:"page"`
- Limit int64 `json:"limit"`
- }
- ResetPasswordByVcode {
- Email string `json:"email"`
- Password string `json:"password"`
- Vcode string `json:"vcode"`
- }
- RegisterReq {
- Username string `json:"username" binding:"required,min=3,max=50"`
- Password string `json:"password" binding:"required,min=6,max=128"`
- Email string `json:"email,omitempty" binding:"omitempty,email"`
- Phone string `json:"phone,omitempty" binding:"omitempty,len=11"`
- Vcode int32 `json:"vcode"`
- }
- RegisterResp {
- UserId int64 `json:"userId"`
- Username string `json:"username"`
- Email string `json:"email"`
- Message string `json:"message"`
- }
- LoginReq {
- Username string `json:"username" binding:"required"`
- Password string `json:"password" binding:"required"`
- }
- LoginResp {
- UserId int64 `json:"userId"`
- Username string `json:"username"`
- Email string `json:"email"`
- Token string `json:"token"`
- Expires int64 `json:"expires"`
- }
- GetUserInfoReq {
- UserId int64 `path:"userId" binding:"required,gt=0"`
- }
- UserInfo {
- UserId int64 `json:"userId"`
- Username string `json:"username"`
- Email string `json:"email"`
- Phone string `json:"phone"`
- Avatar string `json:"avatar"`
- Status int `json:"status"`
- CreateAt int64 `json:"createAt"`
- UpdateAt int64 `json:"updateAt"`
- }
- UpdateUserInfoReq {
- Nickname *string `json:"nickname,omitempty"`
- Avatar *string `json:"avatar,omitempty"`
- Bio *string `json:"bio,omitempty"`
- }
- UpdateUserInfoResp {
- UserId int64 `json:"userId"`
- Message string `json:"message"`
- }
- UpdatePasswordReq {
- UserId int64 `path:"userId" binding:"required,gt=0"`
- OldPassword string `json:"oldPassword" binding:"required"`
- NewPassword string `json:"newPassword" binding:"required,min=6,max=128"`
- }
- UpdatePasswordResp {
- Message string `json:"message"`
- }
- LogoutReq {
- UserId int64 `path:"userId" binding:"required,gt=0"`
- Token string `header:"Authorization" binding:"required"`
- }
- LogoutResp {
- Message string `json:"message"`
- }
- ErrorResp {
- Code int `json:"code"`
- Message string `json:"message"`
- }
+ // 认证记录展示对象
+ // 对应数据库 user_verifications 表
+ VerificationItem {
+ Id int64 `json:"id"` // 认证记录ID (主键,用于管理员操作)
+ UserId int64 `json:"userId"` // 申请人ID (外键)
+ UserNickname string `json:"userNickname"` // 冗余显示,方便前端展示
+ Role string `json:"role"` // 申请角色: player, owner
+ Status string `json:"status"` // pending, approved, rejected
+ Materials map[string]string `json:"materials"` // 核心字段:对应 DB 的 JSONB
+ RejectReason string `json:"rejectReason"` // 驳回原因
+ CreatedAt string `json:"createdAt"` // 申请时间
+ ReviewedAt string `json:"reviewedAt"` // 审核时间
+ }
+ // 提交申请请求
+ ApplyVerificationReq {
+ Role string `json:"role"` // 申请什么角色
+ Materials map[string]string `json:"materials"` // 证明材料键值对 {"idCardFront": "http...", "license": "http..."}
+ }
+ // 获取我的申请记录响应
+ GetMyVerificationsResp {
+ List []VerificationItem `json:"list"`
+ }
+ // 管理员:获取待审核列表请求
+ GetPendingListReq {
+ Page int64 `form:"page,default=1"`
+ Size int64 `form:"size,default=20"`
+ Role string `form:"role,optional"` // 筛选角色
+ Status string `form:"status,optional"` // 筛选状态,默认 pending
+ }
+ // 管理员:列表响应
+ GetPendingListResp {
+ List []VerificationItem `json:"list"`
+ Total int64 `json:"total"`
+ }
+ // 管理员:审核操作请求 (只包含 ID 路径参数)
+ VerificationIdReq {
+ Id int64 `path:"id"` // 注意:这是 user_verifications.id
+ }
+ // 管理员:驳回请求
+ RejectVerificationReq {
+ Id int64 `path:"id"`
+ Reason string `json:"reason"` // 必填:驳回原因
+ }
+ // 通用空响应
+ VerificationEmptyResp {}
)
-@server(
- group: user
- prefix: /api/v1/auth
- middleware: Logger
+// =================================================================================
+// 基础数据模型 (Data Models)
+// =================================================================================
+type (
+ // 通用空响应
+ EmptyResp {}
+ // 用户信息核心模型 (User)
+ User {
+ Id int64 `json:"id"`
+ Username string `json:"username"`
+ Nickname string `json:"nickname"`
+ Avatar string `json:"avatar"`
+ Role string `json:"role"` // consumer, player, owner, admin
+ VerifiedRoles []string `json:"verifiedRoles"` // e.g. ["consumer", "player"]
+ VerificationStatus map[string]string `json:"verificationStatus"` // e.g. {"player": "approved"}
+ Phone string `json:"phone,omitempty"`
+ Bio string `json:"bio,omitempty"`
+ CreatedAt string `json:"createdAt"` // ISO 8601
+ }
+)
+
+// =================================================================================
+// 认证相关 (Auth)
+// =================================================================================
+type (
+ RegisterReq {
+ Phone string `json:"phone,omitempty"`
+ Email string `json:"email,omitempty"`
+ Username string `json:"username"`
+ Password string `json:"password"`
+ Vcode string `json:"vcode,omitempty"` // 验证码
+ }
+ RegisterResp {
+ AccessToken string `json:"accessToken"`
+ RefreshToken string `json:"refreshToken"`
+ User User `json:"user"`
+ }
+ LoginReq {
+ Phone string `json:"phone,omitempty"` // 手机号登录
+ Username string `json:"username,omitempty"` // 或用户名登录
+ Password string `json:"password"`
+ Remember bool `json:"remember,optional"`
+ }
+ LoginResp {
+ AccessToken string `json:"accessToken"`
+ RefreshToken string `json:"refreshToken"`
+ User User `json:"user"`
+ }
+ LogoutReq {}
+ ForgotPasswordReq {
+ Phone string `json:"phone,omitempty"`
+ Email string `json:"email,omitempty"`
+ }
+ ResetPasswordReq {
+ Phone string `json:"phone,omitempty"`
+ Email string `json:"email,omitempty"`
+ Vcode string `json:"vcode"`
+ NewPassword string `json:"newPassword"`
+ }
+)
+
+// =================================================================================
+// 用户个人中心 (User Me)
+// =================================================================================
+type (
+ UpdateUserProfileReq {
+ Nickname string `json:"nickname,optional"`
+ Avatar string `json:"avatar,optional"`
+ Bio string `json:"bio,optional"`
+ }
+ SwitchRoleReq {
+ Role string `json:"role"` // 目标角色
+ }
+ UpdateNotifySettingsReq {
+ Order bool `json:"order,optional"`
+ Community bool `json:"community,optional"`
+ System bool `json:"system,optional"`
+ }
+ UpdateThemeSettingsReq {
+ Theme string `json:"theme"` // dark, light
+ }
+)
+
+// =================================================================================
+// 公开用户信息与社交 (Public User & Social)
+// =================================================================================
+type (
+ GetUserReq {
+ Id int64 `path:"id"`
+ }
+ FollowUserReq {
+ Id int64 `path:"id"`
+ }
+ UnfollowUserReq {
+ Id int64 `path:"id"`
+ }
+)
+
+// =================================================================================
+// 服务定义
+// =================================================================================
+@server (
+ group: auth
+ prefix: /api/v1/auth
)
service user-api {
- @doc(
- summary: "用户注册接口"
- description: "通过用户名、密码、邮箱、电话号码注册新用户账户"
- )
- @handler Register
- post /register (RegisterReq) returns (RegisterResp)
+ @doc "用户注册"
+ @handler Register
+ post /register (RegisterReq) returns (RegisterResp)
- @doc(
- summary: "用户登录接口"
- description: "使用用户名和密码进行登录,返回访问令牌和用户信息"
- )
- @handler Login
- post /login (LoginReq) returns (LoginResp)
+ @doc "用户登录"
+ @handler Login
+ post /login (LoginReq) returns (LoginResp)
- @doc(
- summary: "修改用户密码"
- description: "验证旧密码后修改为新密码,需要提供原密码"
- )
- @handler UpdatePassword
- put /:userId/password (UpdatePasswordReq) returns (UpdatePasswordResp)
+ @doc "忘记密码-发送验证码"
+ @handler ForgotPassword
+ post /forgot-password (ForgotPasswordReq) returns (EmptyResp)
- @doc(
- summary: "用户登出"
- description: "需要携带 Authorization 令牌,清除用户会话并使令牌失效"
- )
- @handler Logout
- post /:userId/logout (LogoutReq) returns (LogoutResp)
-
- @doc "修改密码-使用验证码"
- @handler UpdatePasswordByVcode
- put /forgot-password/reset (ResetPasswordByVcode) returns (EmptyResp)
+ @doc "重置密码"
+ @handler ResetPassword
+ post /reset-password (ResetPasswordReq) returns (EmptyResp)
}
-@server(
- group: user
- prefix: /api/v1/user
- middleware: Logger
+@server (
+ group: auth
+ prefix: /api/v1/auth
)
service user-api {
- @doc "获取当前登录用户信息"
- @handler GetMe
- get /me returns (UserInfo)
-
- @doc "通过用户名搜索用户"
- @handler SearchUsersByUsername
- get /search () returns ([]UserInfo)
-
- @doc "更改当前登录用户信息"
- @handler UpdateMe
- put /me (UpdateUserInfoReq) returns (UserInfo)
-
- @doc(
- summary: "获取用户信息"
- description: "根据用户ID获取用户的详细信息,包含个人资料和账户状态"
- )
- @handler GetUserInfo
- get /:userId (GetUserInfoReq) returns (UserInfo)
-
- @doc(
- summary: "修改用户信息"
- description: "修改用户的邮箱、电话号码、头像等信息"
- )
- @handler UpdateUserInfo
- put /:userId (UpdateUserInfoReq) returns (UpdateUserInfoResp)
+ @doc "退出登录"
+ @handler Logout
+ post /logout (LogoutReq) returns (EmptyResp)
+}
+
+@server (
+ group: user
+ prefix: /api/v1/users
+ middleware: Logger
+)
+service user-api {
+ @doc "获取当前登录用户信息"
+ @handler GetMe
+ get /me returns (User)
+
+ @doc "更新个人资料"
+ @handler UpdateMe
+ put /me (UpdateUserProfileReq) returns (User)
+
+ @doc "切换当前激活角色"
+ @handler SwitchRole
+ post /me/switch-role (SwitchRoleReq) returns (EmptyResp)
+
+ @doc "更新通知偏好"
+ @handler UpdateNotificationSettings
+ put /me/preferences/notifications (UpdateNotifySettingsReq) returns (EmptyResp)
+
+ @doc "更新主题偏好"
+ @handler UpdateThemeSettings
+ put /me/preferences/theme (UpdateThemeSettingsReq) returns (EmptyResp)
+
+ @doc "关注用户"
+ @handler FollowUser
+ post /:id/follow (FollowUserReq) returns (EmptyResp)
+
+ @doc "取消关注用户"
+ @handler UnfollowUser
+ delete /:id/follow (UnfollowUserReq) returns (EmptyResp)
+}
+
+// 公开接口,不需要 JWT
+@server (
+ group: user
+ prefix: /api/v1/users
+)
+service user-api {
+ @doc "获取指定用户信息"
+ @handler GetUserInfo
+ get /:id (GetUserReq) returns (User)
+}
+
+@server (
+ group: verification_user
+ prefix: /api/v1/users
+ middleware: Logger // 必须登录
+)
+service user-api {
+ @doc "提交或修改角色认证申请 (支持幂等更新)"
+ @handler ApplyVerification
+ post /me/verification (ApplyVerificationReq) returns (VerificationEmptyResp)
+
+ @doc "获取我的所有认证状态"
+ @handler GetMyVerifications
+ get /me/verification returns (GetMyVerificationsResp)
+}
+
+@server (
+ group: verification_admin
+ prefix: /api/v1/admin
+)
+service user-api {
+ @doc "管理员获取认证申请列表 (分页)"
+ @handler GetVerifications
+ get /verifications (GetPendingListReq) returns (GetPendingListResp)
+
+ @doc "管理员通过申请"
+ @handler ApproveVerification
+ post /verifications/:id/approve (VerificationIdReq) returns (VerificationEmptyResp)
+
+ @doc "管理员驳回申请"
+ @handler RejectVerification
+ post /verifications/:id/reject (RejectVerificationReq) returns (VerificationEmptyResp)
}
diff --git a/desc/api/wallet.api b/desc/api/wallet.api
new file mode 100644
index 0000000..61aae1b
--- /dev/null
+++ b/desc/api/wallet.api
@@ -0,0 +1,51 @@
+syntax = "v1"
+import "common.api"
+
+type (
+ WalletBalance {
+ Balance float64 `json:"balance"`
+ FrozenBalance float64 `json:"frozenBalance"`
+ }
+
+ Transaction {
+ Id string `json:"id"`
+ Type string `json:"type"`
+ Amount float64 `json:"amount"`
+ Description string `json:"description"`
+ OrderId string `json:"orderId,optional"`
+ CreatedAt string `json:"createdAt"`
+ }
+
+ TransactionListResp {
+ Items []Transaction `json:"items"`
+ Meta PageMeta `json:"meta"`
+ }
+
+ TopupReq {
+ Amount float64 `json:"amount"`
+ Method string `json:"method"`
+ }
+)
+
+@server(
+ prefix: api/v1/wallet
+ group: wallet
+ jwt: Auth
+)
+service juwan-api {
+ @doc "获取余额"
+ @handler GetBalance
+ get /balance (EmptyResp) returns (WalletBalance)
+
+ @doc "获取流水"
+ @handler ListTransactions
+ get /transactions (PageReq) returns (TransactionListResp)
+
+ @doc "充值"
+ @handler Topup
+ post /topup (TopupReq) returns (EmptyResp)
+
+ @doc "提现"
+ @handler Withdraw
+ post /withdraw (TopupReq) returns (EmptyResp)
+}
\ No newline at end of file
diff --git a/desc/rpc/objectstory.proto b/desc/rpc/objectstory.proto
new file mode 100644
index 0000000..c8b5fb9
--- /dev/null
+++ b/desc/rpc/objectstory.proto
@@ -0,0 +1,34 @@
+syntax = "proto3";
+
+package file;
+option go_package = "./pb";
+
+// 文件上传的元数据信息
+message UploadFileMetadataReq {
+ string fileName = 1;
+ int64 fileSize = 2;
+ string fileType = 3; // avatar, chat, etc.
+ string userId = 4;
+ bytes fileData = 5; // 如果文件很小可以直接传,大文件建议API层直接传S3,RPC只传元数据
+}
+
+message UploadFileResp {
+ string url = 1;
+ string fileId = 2;
+}
+
+message GetFileUrlReq {
+ string fileId = 1;
+ string userId = 2; // 用于鉴权
+}
+
+message GetFileUrlResp {
+ string url = 1; // 可能是带签名的临时 URL
+}
+
+service FileService {
+ // 简单上传(适合小文件,或保存元数据)
+ rpc Upload(UploadFileMetadataReq) returns (UploadFileResp);
+ // 获取文件访问链接(处理私有文件的鉴权)
+ rpc GetFileUrl(GetFileUrlReq) returns (GetFileUrlResp);
+}
diff --git a/desc/rpc/user_verifications.proto b/desc/rpc/user_verifications.proto
index bb92da9..bfcfa65 100644
--- a/desc/rpc/user_verifications.proto
+++ b/desc/rpc/user_verifications.proto
@@ -39,11 +39,11 @@ message AddUserVerificationsResp {
message UpdateUserVerificationsReq {
int64 id = 1; //id
- int64 userId = 2; //userId
- string role = 3; //role
- string status = 4; //status
- string materials = 5; //materials
- string rejectReason = 6; //rejectReason
+ optional int64 userId = 2; //userId
+ optional string role = 3; //role
+ optional string status = 4; //status
+ optional string materials = 5; //materials
+ optional string rejectReason = 6; //rejectReason
int64 reviewedBy = 7; //reviewedBy
int64 reviewedAt = 8; //reviewedAt
int64 createdAt = 9; //createdAt
diff --git a/desc/rpc/users.proto b/desc/rpc/users.proto
index 7ef7119..8c6385e 100644
--- a/desc/rpc/users.proto
+++ b/desc/rpc/users.proto
@@ -155,7 +155,7 @@ message RegisterReq {
string username = 1;
string passwd = 2;
string phone = 3;
- int32 vcode = 4;
+ string vcode = 4;
string email = 5;
string requestId = 6;
}
diff --git a/desc/sql/community/comment_likes.sql b/desc/sql/community/comment_likes.sql
new file mode 100644
index 0000000..9318be7
--- /dev/null
+++ b/desc/sql/community/comment_likes.sql
@@ -0,0 +1,9 @@
+CREATE TABLE comment_likes (
+ comment_id BIGINT NOT NULL,
+ user_id BIGINT NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ PRIMARY KEY (comment_id, user_id)
+);
+
+CREATE INDEX idx_comment_likes_lookup ON comment_likes(user_id, comment_id);
\ No newline at end of file
diff --git a/desc/sql/community/comments.sql b/desc/sql/community/comments.sql
new file mode 100644
index 0000000..3e7d90e
--- /dev/null
+++ b/desc/sql/community/comments.sql
@@ -0,0 +1,21 @@
+CREATE TABLE comments (
+ id BIGINT PRIMARY KEY,
+ post_id BIGINT NOT NULL REFERENCES posts(id),
+ author_id BIGINT NOT NULL,
+ content TEXT NOT NULL,
+ like_count INT DEFAULT 0,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ deleted_at TIMESTAMPTZ
+);
+
+-- 基础索引
+CREATE INDEX idx_comments_post ON comments(post_id, created_at) WHERE deleted_at IS NULL;
+CREATE INDEX idx_comments_author ON comments(author_id, created_at DESC) WHERE deleted_at IS NULL;
+
+-- 三元组索引用于评论内容搜索
+CREATE INDEX idx_comments_content_trgm ON comments USING gin(content gin_trgm_ops)
+ WHERE deleted_at IS NULL;
+
+-- 热门评论索引
+CREATE INDEX idx_comments_post_likes ON comments(post_id, like_count DESC, created_at)
+ WHERE deleted_at IS NULL;
\ No newline at end of file
diff --git a/desc/sql/community/post_likes.sql b/desc/sql/community/post_likes.sql
new file mode 100644
index 0000000..10e5110
--- /dev/null
+++ b/desc/sql/community/post_likes.sql
@@ -0,0 +1,14 @@
+CREATE TABLE post_likes (
+ post_id BIGINT NOT NULL,
+ user_id BIGINT NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ -- 复合主键,防止重复点赞
+ PRIMARY KEY (post_id, user_id)
+);
+
+-- [核心索引] 优化 "feed流中判断我是否已赞" (user_id = ? AND post_id IN (...))
+CREATE INDEX idx_post_likes_lookup ON post_likes(user_id, post_id);
+
+-- [核心索引] 优化 "我赞过的帖子" 列表
+CREATE INDEX idx_post_likes_user_timeline ON post_likes(user_id, created_at DESC);
\ No newline at end of file
diff --git a/desc/sql/community/posts.sql b/desc/sql/community/posts.sql
new file mode 100644
index 0000000..68a059f
--- /dev/null
+++ b/desc/sql/community/posts.sql
@@ -0,0 +1,58 @@
+CREATE TABLE posts (
+ id BIGINT PRIMARY KEY,
+ author_id BIGINT NOT NULL,
+ author_role VARCHAR(20) NOT NULL,
+ title VARCHAR(500) NOT NULL,
+ content TEXT NOT NULL,
+ images TEXT[] DEFAULT ARRAY[]::TEXT[],
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[],
+ linked_order_id BIGINT,
+ quoted_post_id BIGINT REFERENCES posts(id),
+ like_count INT DEFAULT 0,
+ comment_count INT DEFAULT 0,
+ pinned BOOLEAN DEFAULT FALSE,
+ search_text TEXT GENERATED ALWAYS AS (
+ title || ' ' || content
+ ) STORED,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ deleted_at TIMESTAMPTZ
+);
+
+-- 基础索引
+CREATE INDEX idx_posts_author ON posts(author_id, created_at DESC) WHERE deleted_at IS NULL;
+CREATE INDEX idx_posts_created ON posts(created_at DESC) WHERE deleted_at IS NULL;
+
+-- 三元组索引用于帖子内容搜索
+CREATE INDEX idx_posts_search_trgm ON posts USING gin(search_text gin_trgm_ops)
+ WHERE deleted_at IS NULL;
+
+-- 全文搜索索引
+CREATE INDEX idx_posts_fulltext ON posts USING gin(
+ to_tsvector('simple', title || ' ' || content)
+ ) WHERE deleted_at IS NULL;
+
+-- 标签 GIN 索引
+CREATE INDEX idx_posts_tags_gin ON posts USING gin(tags) WHERE deleted_at IS NULL;
+
+-- 复合索引优化热门排序
+CREATE INDEX idx_posts_hot_score ON posts(
+ (like_count * 2 + comment_count) DESC,
+ created_at DESC
+) WHERE deleted_at IS NULL;
+
+-- 置顶+时间索引
+CREATE INDEX idx_posts_pinned_created ON posts(author_id, pinned DESC, created_at DESC)
+ WHERE deleted_at IS NULL;
+
+-- 关联订单索引
+CREATE INDEX idx_posts_linked_order ON posts(linked_order_id)
+ WHERE linked_order_id IS NOT NULL AND deleted_at IS NULL;
+
+-- 图片数组索引
+CREATE INDEX idx_posts_images ON posts USING gin(images) WHERE deleted_at IS NULL;
+
+CREATE TRIGGER trigger_posts_updated_at
+ BEFORE UPDATE ON posts
+ FOR EACH ROW
+ EXECUTE FUNCTION update_updated_at_column();
\ No newline at end of file
diff --git a/desc/sql/dispute/dispute_timeline.sql b/desc/sql/dispute/dispute_timeline.sql
new file mode 100644
index 0000000..b3a8f37
--- /dev/null
+++ b/desc/sql/dispute/dispute_timeline.sql
@@ -0,0 +1,17 @@
+CREATE TABLE dispute_timeline (
+ id BIGINT PRIMARY KEY,
+ dispute_id BIGINT NOT NULL REFERENCES disputes(id),
+ event_type VARCHAR(30) NOT NULL,
+ actor_id BIGINT,
+ actor_name VARCHAR(100),
+ details JSONB,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ CONSTRAINT chk_event_type CHECK (event_type IN (
+ 'created', 'response', 'reviewing', 'resolved', 'appealed'
+ ))
+);
+
+CREATE INDEX idx_timeline_dispute ON dispute_timeline(dispute_id, created_at);
+CREATE INDEX idx_timeline_dispute_created ON dispute_timeline(dispute_id, created_at);
+CREATE INDEX idx_timeline_details ON dispute_timeline USING gin(details);
\ No newline at end of file
diff --git a/desc/sql/dispute/disputes.sql b/desc/sql/dispute/disputes.sql
new file mode 100644
index 0000000..7d511f8
--- /dev/null
+++ b/desc/sql/dispute/disputes.sql
@@ -0,0 +1,49 @@
+CREATE TABLE disputes (
+ id BIGINT PRIMARY KEY,
+ order_id BIGINT NOT NULL UNIQUE,
+ initiator_id BIGINT NOT NULL,
+ initiator_name VARCHAR(100) NOT NULL,
+ respondent_id BIGINT NOT NULL,
+ reason TEXT NOT NULL,
+ evidence TEXT[] DEFAULT ARRAY[]::TEXT[],
+ status VARCHAR(20) NOT NULL DEFAULT 'open',
+ result VARCHAR(30),
+ respondent_reason TEXT,
+ respondent_evidence TEXT[] DEFAULT ARRAY[]::TEXT[],
+ appeal_reason TEXT,
+ appealed_at TIMESTAMPTZ,
+ resolved_by BIGINT,
+ resolved_at TIMESTAMPTZ,
+ search_text TEXT GENERATED ALWAYS AS (
+ initiator_name || ' ' || reason || ' ' || coalesce(respondent_reason, '') || ' ' || coalesce(appeal_reason, '')
+ ) STORED,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ CONSTRAINT chk_dispute_status CHECK (status IN ('open', 'reviewing', 'resolved', 'appealed')),
+ CONSTRAINT chk_dispute_result CHECK (result IS NULL OR result IN ('full_refund', 'full_payment', 'partial_refund'))
+);
+
+-- 基础索引
+CREATE INDEX idx_disputes_order ON disputes(order_id);
+CREATE INDEX idx_disputes_status ON disputes(status, created_at DESC);
+CREATE INDEX idx_disputes_initiator ON disputes(initiator_id);
+
+-- 三元组索引用于争议内容搜索
+CREATE INDEX idx_disputes_search_trgm ON disputes USING gin(search_text gin_trgm_ops);
+
+-- 复合索引优化状态查询
+CREATE INDEX idx_disputes_status_created ON disputes(status, created_at DESC);
+
+-- 参与者索引
+CREATE INDEX idx_disputes_initiator_status ON disputes(initiator_id, status, created_at DESC);
+CREATE INDEX idx_disputes_respondent_status ON disputes(respondent_id, status, created_at DESC);
+
+-- 数组索引优化证据查询
+CREATE INDEX idx_disputes_evidence ON disputes USING gin(evidence);
+CREATE INDEX idx_disputes_respondent_evidence ON disputes USING gin(respondent_evidence);
+
+CREATE TRIGGER trigger_disputes_updated_at
+ BEFORE UPDATE ON disputes
+ FOR EACH ROW
+ EXECUTE FUNCTION update_updated_at_column();
\ No newline at end of file
diff --git a/desc/sql/game/games.sql b/desc/sql/game/games.sql
new file mode 100644
index 0000000..bbb0065
--- /dev/null
+++ b/desc/sql/game/games.sql
@@ -0,0 +1,30 @@
+CREATE TABLE games (
+ id BIGINT PRIMARY KEY,
+ name VARCHAR(100) NOT NULL UNIQUE,
+ icon TEXT NOT NULL,
+ category VARCHAR(50) NOT NULL,
+ sort_order INT DEFAULT 0,
+ is_active BOOLEAN DEFAULT TRUE,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+-- 基础索引
+CREATE INDEX idx_games_category ON games(category) WHERE is_active = TRUE;
+CREATE INDEX idx_games_sort ON games(sort_order, id) WHERE is_active = TRUE;
+
+-- 三元组索引用于游戏名称模糊搜索
+CREATE INDEX idx_games_name_trgm ON games USING gin(name gin_trgm_ops) WHERE is_active = TRUE;
+
+-- 复合索引优化分类+排序查询
+CREATE INDEX idx_games_category_sort ON games(category, sort_order, id) WHERE is_active = TRUE;
+
+-- 全文搜索索引
+CREATE INDEX idx_games_fulltext ON games USING gin(
+ to_tsvector('simple', name || ' ' || category)
+ ) WHERE is_active = TRUE;
+
+CREATE TRIGGER trigger_games_updated_at
+ BEFORE UPDATE ON games
+ FOR EACH ROW
+ EXECUTE FUNCTION update_updated_at_column();
\ No newline at end of file
diff --git a/desc/sql/game/player_services.sql b/desc/sql/game/player_services.sql
new file mode 100644
index 0000000..a12012a
--- /dev/null
+++ b/desc/sql/game/player_services.sql
@@ -0,0 +1,49 @@
+CREATE TABLE player_services (
+ id BIGINT PRIMARY KEY,
+ player_id BIGINT NOT NULL REFERENCES players(id),
+ game_id BIGINT NOT NULL,
+ title VARCHAR(200) NOT NULL,
+ description TEXT,
+ price DECIMAL(10,2) NOT NULL,
+ unit VARCHAR(20) NOT NULL,
+ rank_range VARCHAR(100),
+ availability TEXT[] DEFAULT ARRAY[]::TEXT[],
+ rating DECIMAL(3,2) DEFAULT 5.00,
+ is_active BOOLEAN DEFAULT TRUE,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ CONSTRAINT chk_price_positive CHECK (price > 0),
+ CONSTRAINT chk_service_rating CHECK (rating >= 0 AND rating <= 5)
+);
+
+-- 基础索引
+CREATE INDEX idx_services_player ON player_services(player_id) WHERE is_active = TRUE;
+CREATE INDEX idx_services_game ON player_services(game_id) WHERE is_active = TRUE;
+CREATE INDEX idx_services_price ON player_services(price);
+
+-- 三元组索引用于服务标题模糊搜索
+CREATE INDEX idx_services_title_trgm ON player_services USING gin(title gin_trgm_ops)
+ WHERE is_active = TRUE;
+
+-- 全文搜索索引
+CREATE INDEX idx_services_fulltext ON player_services USING gin(
+ to_tsvector('simple', title || ' ' || coalesce(description, ''))
+ ) WHERE is_active = TRUE;
+
+-- 复合索引优化价格区间查询
+CREATE INDEX idx_services_game_price ON player_services(game_id, price, rating DESC)
+ WHERE is_active = TRUE;
+
+-- 打手+游戏复合索引
+CREATE INDEX idx_services_player_game ON player_services(player_id, game_id)
+ WHERE is_active = TRUE;
+
+-- GIN 索引优化时间段查询
+CREATE INDEX idx_services_availability ON player_services USING gin(availability)
+ WHERE is_active = TRUE;
+
+CREATE TRIGGER trigger_services_updated_at
+ BEFORE UPDATE ON player_services
+ FOR EACH ROW
+ EXECUTE FUNCTION update_updated_at_column();
\ No newline at end of file
diff --git a/desc/sql/game/players.sql b/desc/sql/game/players.sql
new file mode 100644
index 0000000..b9e0603
--- /dev/null
+++ b/desc/sql/game/players.sql
@@ -0,0 +1,40 @@
+CREATE TABLE players
+(
+ id BIGINT PRIMARY KEY,
+ CREATE TABLE players (
+ id BIGINT PRIMARY KEY,
+ user_id BIGINT NOT NULL UNIQUE,
+ status VARCHAR(20) NOT NULL DEFAULT 'offline',
+ rating DECIMAL(3,2) DEFAULT 5.00,
+ total_orders INT DEFAULT 0,
+ completed_orders INT DEFAULT 0,
+
+ -- [注意] 此字段为冗余缓存,通过消息队列与 shop_players 表保持一致
+ shop_id BIGINT,
+
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[],
+ games BIGINT[] DEFAULT ARRAY[]::BIGINT[],
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+-- 基础索引
+CREATE INDEX idx_players_user ON players (user_id);
+CREATE INDEX idx_players_shop ON players (shop_id) WHERE shop_id IS NOT NULL;
+CREATE INDEX idx_players_status ON players (status);
+CREATE INDEX idx_players_rating ON players (rating DESC);
+
+-- 复合索引优化多条件筛选
+CREATE INDEX idx_players_status_rating ON players (status, rating DESC) WHERE status IN ('available', 'busy');
+
+-- GIN 索引优化数组查询
+CREATE INDEX idx_players_games_gin ON players USING gin(games);
+CREATE INDEX idx_players_tags_gin ON players USING gin(tags);
+
+-- 店铺+状态复合索引
+CREATE INDEX idx_players_shop_status ON players (shop_id, status, rating DESC) WHERE shop_id IS NOT NULL;
+
+-- CREATE TRIGGER trigger_players_updated_at
+-- BEFORE UPDATE
+-- ON players
+-- FOR EACH ROW
+-- EXECUTE FUNCTION update_updated_at_column();
diff --git a/desc/sql/game/shop.sql b/desc/sql/game/shop.sql
new file mode 100644
index 0000000..93a4bd3
--- /dev/null
+++ b/desc/sql/game/shop.sql
@@ -0,0 +1,49 @@
+CREATE TABLE shops (
+ id BIGINT PRIMARY KEY,
+ owner_id BIGINT NOT NULL UNIQUE,
+ name VARCHAR(200) NOT NULL,
+ banner TEXT,
+ description TEXT,
+ rating DECIMAL(3,2) DEFAULT 5.00,
+ total_orders INT DEFAULT 0,
+ player_count INT DEFAULT 0,
+ commission_type VARCHAR(20) NOT NULL DEFAULT 'percentage',
+ commission_value DECIMAL(10,2) NOT NULL,
+ allow_multi_shop BOOLEAN DEFAULT FALSE,
+ allow_independent_orders BOOLEAN DEFAULT TRUE,
+ dispatch_mode VARCHAR(20) NOT NULL DEFAULT 'manual',
+ announcements TEXT[] DEFAULT ARRAY[]::TEXT[],
+ template_config JSONB,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ CONSTRAINT chk_commission_type CHECK (commission_type IN ('fixed', 'percentage')),
+ CONSTRAINT chk_dispatch_mode CHECK (dispatch_mode IN ('manual', 'auto')),
+ CONSTRAINT chk_rating_range CHECK (rating >= 0 AND rating <= 5)
+);
+
+-- 基础索引
+CREATE INDEX idx_shops_owner ON shops(owner_id);
+CREATE INDEX idx_shops_rating ON shops(rating DESC);
+
+-- 三元组索引用于店铺名称模糊搜索
+CREATE INDEX idx_shops_name_trgm ON shops USING gin(name gin_trgm_ops);
+
+-- 全文搜索索引
+CREATE INDEX idx_shops_fulltext ON shops USING gin(
+ to_tsvector('simple', name || ' ' || coalesce(description, ''))
+ );
+
+-- 复合索引优化排序查询
+CREATE INDEX idx_shops_rating_orders ON shops(rating DESC, total_orders DESC);
+
+-- 公告数组索引
+CREATE INDEX idx_shops_announcements ON shops USING gin(announcements);
+
+-- JSONB 索引优化模板配置查询
+CREATE INDEX idx_shops_template ON shops USING gin(template_config);
+
+CREATE TRIGGER trigger_shops_updated_at
+ BEFORE UPDATE ON shops
+ FOR EACH ROW
+ EXECUTE FUNCTION update_updated_at_column();
\ No newline at end of file
diff --git a/desc/sql/game/shop_invitations.sql b/desc/sql/game/shop_invitations.sql
new file mode 100644
index 0000000..56d5463
--- /dev/null
+++ b/desc/sql/game/shop_invitations.sql
@@ -0,0 +1,17 @@
+CREATE TABLE shop_invitations (
+ id BIGINT PRIMARY KEY,
+ shop_id BIGINT NOT NULL REFERENCES shops(id),
+ player_id BIGINT NOT NULL,
+ status VARCHAR(20) NOT NULL DEFAULT 'pending',
+ invited_by BIGINT NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ responded_at TIMESTAMPTZ,
+
+ CONSTRAINT chk_invitation_status CHECK (status IN ('pending', 'accepted', 'rejected', 'cancelled')),
+ UNIQUE(shop_id, player_id, status) WHERE status = 'pending'
+);
+
+CREATE INDEX idx_invitations_shop ON shop_invitations(shop_id);
+CREATE INDEX idx_invitations_player ON shop_invitations(player_id) WHERE status = 'pending';
+CREATE INDEX idx_invitations_player_status ON shop_invitations(player_id, status, created_at DESC);
+CREATE INDEX idx_invitations_shop_status ON shop_invitations(shop_id, status, created_at DESC);
\ No newline at end of file
diff --git a/desc/sql/game/shop_players.sql b/desc/sql/game/shop_players.sql
new file mode 100644
index 0000000..78ccc3a
--- /dev/null
+++ b/desc/sql/game/shop_players.sql
@@ -0,0 +1,21 @@
+CREATE TABLE shop_players
+(
+ shop_id BIGINT NOT NULL REFERENCES shops (id),
+ player_id BIGINT NOT NULL,
+
+ -- [新增] 标记是否为主店铺。用于个人主页展示和 players.shop_id 缓存源
+ is_primary BOOLEAN DEFAULT FALSE,
+
+ joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ left_at TIMESTAMPTZ, -- 软删除,表示已离职
+
+ PRIMARY KEY (shop_id, player_id)
+);
+
+-- 索引
+CREATE INDEX idx_shop_players_player ON shop_players (player_id) WHERE left_at IS NULL;
+CREATE INDEX idx_shop_players_shop_active ON shop_players (shop_id, joined_at DESC) WHERE left_at IS NULL;
+
+-- [新增] 唯一索引:确保一个打手同一时间只能有一个主店铺
+CREATE UNIQUE INDEX idx_shop_players_one_primary
+ ON shop_players (player_id) WHERE is_primary = TRUE AND left_at IS NULL;
diff --git a/desc/sql/order/order_state_log.sql b/desc/sql/order/order_state_log.sql
new file mode 100644
index 0000000..f99791e
--- /dev/null
+++ b/desc/sql/order/order_state_log.sql
@@ -0,0 +1,15 @@
+CREATE TABLE order_state_logs (
+ id BIGINT PRIMARY KEY,
+ order_id BIGINT NOT NULL REFERENCES orders(id),
+ from_status VARCHAR(30),
+ to_status VARCHAR(30) NOT NULL,
+ action VARCHAR(50) NOT NULL,
+ actor_id BIGINT NOT NULL,
+ actor_role VARCHAR(20) NOT NULL,
+ metadata JSONB,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE INDEX idx_state_logs_order ON order_state_logs(order_id, created_at);
+CREATE INDEX idx_state_logs_order_created ON order_state_logs(order_id, created_at DESC);
+CREATE INDEX idx_state_logs_actor ON order_state_logs(actor_id, created_at DESC);
\ No newline at end of file
diff --git a/desc/sql/order/orders.sql b/desc/sql/order/orders.sql
new file mode 100644
index 0000000..206e192
--- /dev/null
+++ b/desc/sql/order/orders.sql
@@ -0,0 +1,65 @@
+CREATE TABLE orders (
+ id BIGINT PRIMARY KEY,
+ consumer_id BIGINT NOT NULL,
+ consumer_name VARCHAR(100) NOT NULL,
+ player_id BIGINT NOT NULL,
+ player_name VARCHAR(100) NOT NULL,
+ shop_id BIGINT,
+ shop_name VARCHAR(200),
+ service_snapshot JSONB NOT NULL,
+ status VARCHAR(30) NOT NULL DEFAULT 'pending_payment',
+ total_price DECIMAL(10,2) NOT NULL,
+ note TEXT,
+ version INT NOT NULL DEFAULT 1,
+ timeout_job_id VARCHAR(100),
+ search_text TEXT GENERATED ALWAYS AS (
+ consumer_name || ' ' || player_name || ' ' || coalesce(shop_name, '') || ' ' || coalesce(note, '')
+ ) STORED,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ accepted_at TIMESTAMPTZ,
+ closed_at TIMESTAMPTZ,
+ completed_at TIMESTAMPTZ,
+ cancelled_at TIMESTAMPTZ,
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ CONSTRAINT chk_order_status CHECK (status IN (
+ 'pending_payment', 'pending_accept', 'in_progress',
+ 'pending_close', 'pending_review', 'disputed',
+ 'completed', 'cancelled'
+ )),
+ CONSTRAINT chk_price_positive CHECK (total_price > 0)
+);
+
+-- 基础索引
+CREATE INDEX idx_orders_consumer ON orders(consumer_id, created_at DESC);
+CREATE INDEX idx_orders_player ON orders(player_id, created_at DESC);
+CREATE INDEX idx_orders_shop ON orders(shop_id, created_at DESC) WHERE shop_id IS NOT NULL;
+CREATE INDEX idx_orders_status ON orders(status, created_at DESC);
+CREATE INDEX idx_orders_timeout ON orders(timeout_job_id) WHERE timeout_job_id IS NOT NULL;
+
+-- 三元组索引用于订单搜索
+CREATE INDEX idx_orders_search_trgm ON orders USING gin(search_text gin_trgm_ops);
+
+-- 复合索引优化多条件查询
+CREATE INDEX idx_orders_consumer_status_created ON orders(consumer_id, status, created_at DESC);
+CREATE INDEX idx_orders_player_status_created ON orders(player_id, status, created_at DESC);
+CREATE INDEX idx_orders_shop_status_created ON orders(shop_id, status, created_at DESC)
+ WHERE shop_id IS NOT NULL;
+
+-- 状态+时间复合索引 (用于超时任务扫描)
+CREATE INDEX idx_orders_status_timeout ON orders(status, created_at)
+ WHERE status IN ('pending_accept', 'pending_close', 'pending_review');
+
+-- JSONB 索引优化服务快照查询
+CREATE INDEX idx_orders_service_snapshot ON orders USING gin(service_snapshot);
+
+-- 价格区间索引
+CREATE INDEX idx_orders_price ON orders(total_price) WHERE status = 'completed';
+
+-- 时间范围索引 (用于统计)
+CREATE INDEX idx_orders_completed_at ON orders(completed_at DESC) WHERE completed_at IS NOT NULL;
+
+CREATE TRIGGER trigger_orders_updated_at
+ BEFORE UPDATE ON orders
+ FOR EACH ROW
+ EXECUTE FUNCTION update_updated_at_column();
\ No newline at end of file
diff --git a/desc/sql/review/reviews.sql b/desc/sql/review/reviews.sql
new file mode 100644
index 0000000..616ddd3
--- /dev/null
+++ b/desc/sql/review/reviews.sql
@@ -0,0 +1,37 @@
+CREATE TABLE reviews (
+ id BIGINT PRIMARY KEY,
+ order_id BIGINT NOT NULL,
+ from_user_id BIGINT NOT NULL,
+ from_user_name VARCHAR(100) NOT NULL,
+ from_user_avatar TEXT,
+ to_user_id BIGINT NOT NULL,
+ rating SMALLINT NOT NULL,
+ content TEXT,
+ sealed BOOLEAN DEFAULT TRUE,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ unsealed_at TIMESTAMPTZ,
+
+ CONSTRAINT chk_rating_range CHECK (rating >= 1 AND rating <= 5),
+ UNIQUE(order_id, from_user_id)
+);
+
+-- 基础索引
+CREATE INDEX idx_reviews_order ON reviews(order_id);
+CREATE INDEX idx_reviews_to_user ON reviews(to_user_id, created_at
+
+ CREATE INDEX idx_reviews_to_user ON reviews(to_user_id, created_at DESC) WHERE sealed = FALSE;
+CREATE INDEX idx_reviews_from_user ON reviews(from_user_id);
+
+-- 三元组索引用于评价内容搜索
+CREATE INDEX idx_reviews_content_trgm ON reviews USING gin(content gin_trgm_ops)
+ WHERE sealed = FALSE AND content IS NOT NULL;
+
+-- 复合索引优化用户评价列表
+CREATE INDEX idx_reviews_to_user_rating ON reviews(to_user_id, rating DESC, created_at DESC)
+ WHERE sealed = FALSE;
+
+-- 复合索引优化订单评价查询
+CREATE INDEX idx_reviews_order_sealed ON reviews(order_id, sealed);
+
+-- 评分统计索引
+CREATE INDEX idx_reviews_rating_created ON reviews(rating, created_at DESC) WHERE sealed = FALSE;
\ No newline at end of file
diff --git a/desc/sql/search/favorites.sql b/desc/sql/search/favorites.sql
new file mode 100644
index 0000000..8207300
--- /dev/null
+++ b/desc/sql/search/favorites.sql
@@ -0,0 +1,20 @@
+CREATE TABLE favorites (
+ id BIGINT PRIMARY KEY,
+ user_id BIGINT NOT NULL,
+ target_type VARCHAR(20) NOT NULL,
+ target_id BIGINT NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ CONSTRAINT chk_target_type CHECK (target_type IN ('player', 'shop')),
+ UNIQUE(user_id, target_type, target_id)
+);
+
+-- 基础索引
+CREATE INDEX idx_favorites_user ON favorites(user_id, created_at DESC);
+CREATE INDEX idx_favorites_target ON favorites(target_type, target_id);
+
+-- 复合索引优化收藏列表查询
+CREATE INDEX idx_favorites_user_type_created ON favorites(user_id, target_type, created_at DESC);
+
+-- 目标反查索引(统计收藏数)
+CREATE INDEX idx_favorites_target_count ON favorites(target_type, target_id);
\ No newline at end of file
diff --git a/desc/sql/users/user_follows.sql b/desc/sql/users/user_follows.sql
new file mode 100644
index 0000000..d92ec67
--- /dev/null
+++ b/desc/sql/users/user_follows.sql
@@ -0,0 +1,12 @@
+CREATE TABLE user_follows (
+ id BIGINT PRIMARY KEY,
+ follower_id BIGINT NOT NULL REFERENCES users(id),
+ followee_id BIGINT NOT NULL REFERENCES users(id),
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ UNIQUE(follower_id, followee_id),
+ CONSTRAINT chk_no_self_follow CHECK (follower_id != followee_id)
+ );
+
+CREATE INDEX idx_follows_follower ON user_follows(follower_id);
+CREATE INDEX idx_follows_followee ON user_follows(followee_id);
\ No newline at end of file
diff --git a/desc/sql/users/user_preferences.sql b/desc/sql/users/user_preferences.sql
new file mode 100644
index 0000000..0233742
--- /dev/null
+++ b/desc/sql/users/user_preferences.sql
@@ -0,0 +1,16 @@
+CREATE TABLE user_preferences (
+ user_id BIGINT PRIMARY KEY REFERENCES users(id),
+ notification_order BOOLEAN DEFAULT TRUE,
+ notification_community BOOLEAN DEFAULT TRUE,
+ notification_system BOOLEAN DEFAULT TRUE,
+ theme VARCHAR(20) DEFAULT 'light',
+ language VARCHAR(10) DEFAULT 'zh-CN',
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ CONSTRAINT chk_theme CHECK (theme IN ('light', 'dark', 'auto'))
+);
+
+CREATE TRIGGER trigger_preferences_updated_at
+ BEFORE UPDATE ON user_preferences
+ FOR EACH ROW
+ EXECUTE FUNCTION update_updated_at_column();
\ No newline at end of file
diff --git a/desc/sql/users/user_verifications.sql b/desc/sql/users/user_verifications.sql
index 1ebcd0e..794fb1b 100644
--- a/desc/sql/users/user_verifications.sql
+++ b/desc/sql/users/user_verifications.sql
@@ -1,35 +1,52 @@
-CREATE TABLE user_verifications (
- id BIGINT PRIMARY KEY,
- user_id BIGINT NOT NULL REFERENCES users(id),
- role VARCHAR(20) NOT NULL,
- status VARCHAR(20) NOT NULL DEFAULT 'pending',
- materials JSONB NOT NULL,
- reject_reason TEXT,
- reviewed_by BIGINT,
- reviewed_at TIMESTAMPTZ,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+CREATE TABLE user_verifications
+(
+ id BIGINT PRIMARY KEY,
+ user_id BIGINT NOT NULL REFERENCES users (id),
+ role VARCHAR(20) NOT NULL,
+ status VARCHAR(20) NOT NULL DEFAULT 'pending',
+ materials JSONB NOT NULL,
+ reject_reason TEXT,
+ reviewed_by BIGINT,
+ reviewed_at TIMESTAMPTZ,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT chk_verification_status CHECK (status IN ('pending', 'approved', 'rejected')),
- UNIQUE(user_id, role) -- 每个角色只有一条最新的认证记录
+ CONSTRAINT chk_verification_status CHECK (status IN ('pending', 'approved', 'rejected')),
+ UNIQUE (user_id, role) -- 每个角色只有一条最新的认证记录
);
-- Note:Auto update verification_status of users table
CREATE OR REPLACE FUNCTION sync_user_verification_status()
-RETURNS TRIGGER AS $$
+ RETURNS TRIGGER AS
+$$
DECLARE
-status_json JSONB;
+ status_json JSONB;
BEGIN
-SELECT jsonb_object_agg(role, status)
-INTO status_json
-FROM user_verifications
-WHERE user_id = NEW.user_id;
+ SELECT jsonb_object_agg(role, status)
+ INTO status_json
+ FROM user_verifications
+ WHERE user_id = NEW.user_id;
-UPDATE users SET verification_status = status_json WHERE id = NEW.user_id;
-RETURN NEW;
+ UPDATE users SET verification_status = status_json WHERE id = NEW.user_id;
+ RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_sync_verifications
- AFTER INSERT OR UPDATE ON user_verifications
- FOR EACH ROW EXECUTE FUNCTION sync_user_verification_status();
\ No newline at end of file
+ AFTER INSERT OR UPDATE
+ ON user_verifications
+ FOR EACH ROW
+EXECUTE FUNCTION sync_user_verification_status();
+
+-- | 字段名 | 类型 | 技术含义 | 业务目的与设计思考 |
+-- | :--- | :--- | :--- | :--- |
+-- | **`id`** | BIGINT | **主键 (Primary Key)** | **这张认证申请单的唯一编号**。
后端代码在处理“审核通过”这个动作时,操作的是这个 ID(例如:`Approve(verificationId)`)。 |
+-- | **`user_id`** | BIGINT | **外键 (Foreign Key)** | **谁在申请?**
关联到 `users` 表。通过这个字段,我们可以查到申请人的昵称、手机号等信息。 |
+-- | **`role`** | VARCHAR | 普通字段 | **申请什么身份?**
枚举值:`player` (打手), `owner` (店长)。
因为一个用户可以同时是打手和店长,所以需要区分这条记录是申请哪个身份的。 |
+-- | **`status`** | VARCHAR | 状态字段 | **当前进度如何?**
枚举值:`pending` (待审核), `approved` (已通过), `rejected` (已驳回)。 |
+-- | **`materials`** | **JSONB** | **非结构化数据** | **提交了什么证明材料?**
**精华设计**:这里不使用多张表或多个字段,而是用 JSON 存。因为打手需要传“身份证+段位图”,店长需要传“营业执照”。不同角色的材料结构不同,用 JSONB 最灵活,以后改规则不需要改表结构。 |
+-- | **`reject_reason`** | TEXT | 文本 | **驳回理由**。
只有当 `status` = 'rejected' 时才有值,告诉用户哪里填错了。 |
+-- | **`reviewed_by`** | BIGINT | 审计字段 | **谁审核的?**
记录是哪个管理员(Admin ID)点击了通过或拒绝。用于内部追责和审计。 |
+-- | **`reviewed_at`** | TIMESTAMPTZ | 时间字段 | **什么时候审核的?**
用于统计管理员的工作效率,或者展示给用户“审核耗时”。 |
+-- | **`created_at`** | TIMESTAMPTZ | 时间字段 | **申请提交时间**。 |
+-- | **`updated_at`** | TIMESTAMPTZ | 时间字段 | **最后更新时间**。
比如用户重新上传了图片,这个时间会变。 |
\ No newline at end of file
diff --git a/desc/sql/wallet/wallet_transactions.sql b/desc/sql/wallet/wallet_transactions.sql
new file mode 100644
index 0000000..9a307e7
--- /dev/null
+++ b/desc/sql/wallet/wallet_transactions.sql
@@ -0,0 +1,33 @@
+CREATE TABLE wallet_transactions (
+ id BIGINT PRIMARY KEY,
+ user_id BIGINT NOT NULL,
+ type VARCHAR(20) NOT NULL,
+ amount DECIMAL(12,2) NOT NULL,
+ balance_after DECIMAL(12,2) NOT NULL,
+ description VARCHAR(500) NOT NULL,
+ order_id BIGINT,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ search_text TEXT GENERATED ALWAYS AS (
+ type || ' ' || description
+ ) STORED,
+
+ CONSTRAINT chk_transaction_type CHECK (type IN ('topup', 'payment', 'income', 'withdrawal', 'refund'))
+);
+
+-- 基础索引
+CREATE INDEX idx_transactions_user ON wallet_transactions(user_id, created_at DESC);
+CREATE INDEX idx_transactions_order ON wallet_transactions(order_id) WHERE order_id IS NOT NULL;
+CREATE INDEX idx_transactions_type ON wallet_transactions(user_id, type, created_at DESC);
+
+-- 三元组索引用于交易描述搜索
+CREATE INDEX idx_transactions_search_trgm ON wallet_transactions USING gin(search_text gin_trgm_ops);
+
+-- 复合索引优化用户交易查询
+CREATE INDEX idx_transactions_user_type_created ON wallet_transactions(user_id, type, created_at DESC);
+
+-- 时间范围索引 (用于统计)
+CREATE INDEX idx_transactions_created_amount ON wallet_transactions(created_at DESC, amount);
+
+-- 订单关联索引
+CREATE INDEX idx_transactions_order_type ON wallet_transactions(order_id, type)
+ WHERE order_id IS NOT NULL;
\ No newline at end of file
diff --git a/desc/sql/wallet/wallets.sql b/desc/sql/wallet/wallets.sql
new file mode 100644
index 0000000..a2427c9
--- /dev/null
+++ b/desc/sql/wallet/wallets.sql
@@ -0,0 +1,17 @@
+CREATE TABLE wallets (
+ user_id BIGINT PRIMARY KEY,
+ balance DECIMAL(12,2) NOT NULL DEFAULT 0.00,
+ frozen_balance DECIMAL(12,2) NOT NULL DEFAULT 0.00,
+ version INT NOT NULL DEFAULT 1, -- 必须使用乐观锁
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ CONSTRAINT chk_balance_non_negative CHECK (balance >= 0),
+ CONSTRAINT chk_frozen_non_negative CHECK (frozen_balance >= 0)
+);
+
+CREATE INDEX idx_wallets_updated ON wallets(updated_at DESC);
+
+CREATE TRIGGER trigger_wallets_updated_at
+ BEFORE UPDATE ON wallets
+ FOR EACH ROW
+ EXECUTE FUNCTION update_updated_at_column();
\ No newline at end of file