commit 79b21c7264cee1b8bac5a9d5307f3dac3ece378a
Author: 王性驊 <igs170911@gmail.com>
Date:   Wed Mar 12 21:46:41 2025 +0800

    init project

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..957184a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea/
+etc/gateway.yaml
+.DS_Store
\ No newline at end of file
diff --git a/.golangci.yaml b/.golangci.yaml
new file mode 100755
index 0000000..3505d4f
--- /dev/null
+++ b/.golangci.yaml
@@ -0,0 +1,135 @@
+run:
+  timeout: 3m
+  # Exit code when at least one issue was found.
+  # Default: 1
+  issues-exit-code: 2
+  # Include test files or not.
+  # Default: true
+  tests: false
+
+# Reference URL: https://golangci-lint.run/usage/linters/
+linters:
+  # Disable everything by default so upgrades to not include new - default
+  # enabled-  linters.
+  disable-all: true
+  # Specifically enable linters we want to use.
+  enable:
+    #    - depguard
+    - errcheck
+    #    - godot
+    - gofmt
+    - goimports
+    - gosimple
+    - govet
+    - ineffassign
+    - misspell
+    - revive
+    - typecheck
+    - unused
+    - asasalint
+    - asciicheck
+    - bidichk
+    - bodyclose
+    - contextcheck
+    - wastedassign
+    - whitespace
+    - thelper
+    - tparallel
+    - unconvert
+    - unparam
+    - usestdlibvars
+    - tenv
+    - testableexamples
+    - stylecheck
+    - sqlclosecheck
+    - nosprintfhostport
+    - paralleltest
+    - prealloc
+    - predeclared
+    - promlinter
+    - reassign
+    - rowserrcheck
+    - nakedret
+    - nestif
+    - nilerr
+    - nilnil
+    - nlreturn
+    - noctx
+    - nolintlint
+    - nonamedreturns
+    - decorder
+    - dogsled
+    - dupword
+    - durationcheck
+    - errchkjson
+    - errname
+    - errorlint
+    # - execinquery
+    - exhaustive
+    - exportloopref
+    - forbidigo
+    - forcetypeassert
+    - gochecknoinits
+    - gocognit
+    - goconst
+    - gocritic
+    - gocyclo
+    - goheader
+    - gomoddirectives
+    - goprintffuncname
+    - gosec
+    - grouper
+    - importas
+    - interfacebloat
+    - lll
+    - loggercheck
+    - maintidx
+    - makezero
+
+issues:
+  exclude-rules:
+    - path: _test\.go
+      linters:
+        - funlen
+        - goconst
+        - interfacer
+        - dupl
+        - lll
+        - goerr113
+        - errcheck
+        - gocritic
+        - cyclop
+        - wrapcheck
+        - gocognit
+        - contextcheck
+
+  exclude-dirs:
+    - internal/module/seckill/usecase
+#    - internal/logic
+
+  exclude-files:
+    - .*_test.go
+
+
+
+linters-settings:
+  gci:
+    sections:
+      - standard # Standard section: captures all standard packages.
+      - default # Default section: contains all imports that could not be matched to another section type.
+  gocognit:
+    # Minimal code complexity to report.
+    # Default: 30 (but we recommend 10-20)
+    min-complexity: 40
+  nestif:
+    # Minimal complexity of if statements to report.
+    # Default: 5
+    min-complexity: 10
+  lll:
+    # Max line length, lines longer will be reported.
+    # '\t' is counted as 1 character by default, and can be changed with the tab-width option.
+    # Default: 120.
+    line-length: 200
+    # Tab width in spaces.
+    # Default: 1
+    tab-width: 1
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..adf9df9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+#goctl api plugin -plugin goctl-swagger="swagger -filename gateway.json -host dev-api.30cm.net" -api ./generate/api/gateway.api -dir .
+#goctl api go -api ./generate/api/gateway.api -dir . -style go_zero
+
+GOFMT ?= gofmt
+GOFILES := $(shell find . -name "*.go")
+
+.PHONY: fmt
+fmt: # 格式優化
+	$(GOFMT) -s -w $(GOFILES)
+	goimports -w  ./
+	golangci-lint run
+
+
+.PHONY: gen-doc
+gen-doc: # 格式優化
+	goctl api plugin -plugin goctl-swagger="swagger -filename gateway.json -host dev-api.30cm.net" -api ./generate/api/gateway.api -dir .
+
+.PHONY: gen-api
+gen-api: # 格式優化
+	goctl api go -api ./generate/api/gateway.api -dir . -style go_zero
diff --git a/etc/gateway.example.yaml b/etc/gateway.example.yaml
new file mode 100644
index 0000000..61471bb
--- /dev/null
+++ b/etc/gateway.example.yaml
@@ -0,0 +1,43 @@
+Name: gateway
+Host: 0.0.0.0
+Port: 8888
+
+DevServer:
+  Enabled: false
+  Port: 6470
+  MetricsPath: /metrics
+  EnableMetrics: true
+
+PyroScope:
+  Enable: false
+  URL: http://10.0.0.1:4040
+  Token: "xxx"
+
+RedisCfg:
+  Host: 127.0.0.1:6379
+  Type: node
+
+MemberRPC:
+  Etcd:
+    Hosts:
+      - 10.0.0.13:2379
+    Key: member.rpc
+NotificationRPC:
+  Etcd:
+    Hosts:
+      - 10.0.0.13:2379
+    Key: notification.rpc
+PermissionRPC:
+  Etcd:
+    Hosts:
+      - 10.0.0.13:2379
+    Key: permission.rpc
+
+MailSender: service@30cm.net
+
+Token:
+  Expired: 86500s
+  RefreshExpired: 86500s
+
+Member:
+  ForgetTimeOutInSec: 180
\ No newline at end of file
diff --git a/gateway.go b/gateway.go
new file mode 100644
index 0000000..4fb39ac
--- /dev/null
+++ b/gateway.go
@@ -0,0 +1,32 @@
+package main
+
+import (
+	"flag"
+
+	"github.com/zeromicro/go-zero/core/logx"
+
+	"biz-member-gateway/internal/config"
+	"biz-member-gateway/internal/handler"
+	"biz-member-gateway/internal/svc"
+
+	"github.com/zeromicro/go-zero/core/conf"
+	"github.com/zeromicro/go-zero/rest"
+)
+
+var configFile = flag.String("f", "etc/gateway.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)
+
+	logx.Infof("Starting server at %s:%d...\n", c.Host, c.Port)
+	server.Start()
+}
diff --git a/gateway.json b/gateway.json
new file mode 100644
index 0000000..3280d91
--- /dev/null
+++ b/gateway.json
@@ -0,0 +1,785 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "title": "",
+    "version": ""
+  },
+  "host": "dev-api.30cm.net",
+  "schemes": [
+    "http",
+    "https"
+  ],
+  "consumes": [
+    "application/json"
+  ],
+  "produces": [
+    "application/json"
+  ],
+  "paths": {
+    "/api/v1/member": {
+      "post": {
+        "summary": "創建新會員",
+        "description": "創建一個全新的帳號,創完成之後會自動登入",
+        "operationId": "AccountCreate",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/LoginTokenResp"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "description": " -------------------------------------------",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/CreateAccountRequest"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ],
+        "consumes": [
+          "multipart/form-data"
+        ]
+      }
+    },
+    "/api/v1/member/check-verify-code": {
+      "post": {
+        "summary": "確認邀請 - 綁定會員",
+        "description": "確認驗證碼是否有效",
+        "operationId": "CheckVerifyCode",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/RespOK"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/CheckoutVerifyReq"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      }
+    },
+    "/api/v1/member/forget-password-code": {
+      "post": {
+        "summary": "發送忘記密碼驗證",
+        "description": "發送忘記密碼驗證(三分鐘內只能發一次信)",
+        "operationId": "ForgetPasswordCode",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/RespOK"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/ForgetPasswordCodeReq"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      }
+    },
+    "/api/v1/member/info": {
+      "get": {
+        "summary": "取得會員資訊",
+        "operationId": "Info",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/UserInfo"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "required",
+            "in": "header",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      },
+      "put": {
+        "summary": "更新會員詳細資訊",
+        "operationId": "ModifyMemberInfo",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/UserInfo"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/BindingUserInfoReq"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      }
+    },
+    "/api/v1/member/login": {
+      "post": {
+        "summary": "登入",
+        "description": "會員登入",
+        "operationId": "Login",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/LoginTokenResp"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/LoginReq"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ],
+        "consumes": [
+          "multipart/form-data"
+        ]
+      }
+    },
+    "/api/v1/member/logout": {
+      "get": {
+        "summary": "會員登出",
+        "operationId": "Logout",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/RespOK"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "required",
+            "in": "header",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      }
+    },
+    "/api/v1/member/modify-passwd": {
+      "put": {
+        "summary": "修改密碼",
+        "description": "修改密碼",
+        "operationId": "ModifyPasswdHandler",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/RespOK"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/ModifyPasswdReq"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      }
+    },
+    "/api/v1/member/pre-verify": {
+      "put": {
+        "summary": "預先驗證驗證碼",
+        "description": "忘記密碼的時候看 ui. 流程要預先驗證一次才給送,",
+        "operationId": "PreVerifyUpdatePasswordCode",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/RespOK"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/PreVerifyForgetPasswdReq"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      }
+    },
+    "/api/v1/member/refresh_access_token": {
+      "put": {
+        "summary": "更新 Access Token",
+        "description": "用 RefreshToken 換取 AccessToken",
+        "operationId": "RefreshAccessToken",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/LoginTokenResp"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/UpdateTokenReq"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      }
+    },
+    "/api/v1/member/update-password": {
+      "put": {
+        "summary": "更新密碼(要發送驗證碼才可以的流程)",
+        "description": "更新密碼(要發送驗證碼才可以的流程)",
+        "operationId": "UpdatePassword",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/RespOK"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/UpdatePasswordReq"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      }
+    },
+    "/api/v1/member/verify": {
+      "post": {
+        "summary": "發送邀請 - 綁定會員",
+        "description": "可以依照類別(手機驗證,email驗證),同一個類型十分鐘內只能發送一次",
+        "operationId": "SendVerifyCode",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/RespOK"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/VerificationCodeRequest"
+            }
+          }
+        ],
+        "tags": [
+          "member"
+        ]
+      }
+    }
+  },
+  "definitions": {
+    "BaseResponse": {
+      "type": "object",
+      "properties": {
+        "status": {
+          "$ref": "#/definitions/Status",
+          "description": " 狀態"
+        },
+        "data": {
+          "type": "object",
+          "description": " 資料"
+        }
+      },
+      "title": "BaseResponse",
+      "required": [
+        "status",
+        "data"
+      ]
+    },
+    "BindingUserInfoReq": {
+      "type": "object",
+      "properties": {
+        "preferred_language": {
+          "type": "string",
+          "description": " 使用語言"
+        },
+        "currency": {
+          "type": "string"
+        },
+        "avatar_url": {
+          "type": "string",
+          "description": " 頭像 URL(可選)"
+        },
+        "nickname": {
+          "type": "string"
+        },
+        "full_name": {
+          "type": "string",
+          "description": " 用戶全名"
+        },
+        "gender_code": {
+          "type": "string",
+          "description": " 性別代碼"
+        },
+        "birthdate": {
+          "type": "string",
+          "description": " 生日 (格式: unix)"
+        },
+        "address": {
+          "type": "string",
+          "description": " 地址"
+        }
+      },
+      "title": "BindingUserInfoReq",
+      "required": [
+        "gender_code"
+      ]
+    },
+    "CheckoutVerifyReq": {
+      "type": "object",
+      "properties": {
+        "account": {
+          "type": "string",
+          "description": " 帳號名稱"
+        },
+        "code_type": {
+          "type": "string",
+          "description": " 驗證碼類型 1 信箱 2 手機"
+        },
+        "verify_code": {
+          "type": "string",
+          "description": " 驗證碼,長度為6"
+        },
+        "uid": {
+          "type": "string"
+        }
+      },
+      "title": "CheckoutVerifyReq",
+      "required": [
+        "account",
+        "code_type",
+        "verify_code",
+        "uid"
+      ]
+    },
+    "CreateAccountRequest": {
+      "type": "object",
+      "properties": {
+        "account": {
+          "type": "string",
+          "description": " 帳號名稱(line code 輸入在這邊)"
+        },
+        "token": {
+          "type": "string",
+          "description": " 密碼或平台token,密碼請 sha256 轉碼,如果三方token 請隨便給一個 sha256 字串"
+        },
+        "token_check": {
+          "type": "string",
+          "description": " 密碼或平台token,token 請保持原樣,填在這邊,不用管 token"
+        },
+        "platform": {
+          "type": "string",
+          "description": " 平台名稱 (platform) 平台、google、line"
+        },
+        "account_type": {
+          "type": "string",
+          "description": " 帳號類型 phone(手機)、email(信箱)、platform(自定義帳號) -\u003e (如果為第三方都寫 platform)"
+        }
+      },
+      "title": "CreateAccountRequest",
+      "required": [
+        "account",
+        "token",
+        "token_check",
+        "platform",
+        "account_type"
+      ]
+    },
+    "ForgetPasswordCodeReq": {
+      "type": "object",
+      "properties": {
+        "account": {
+          "type": "string",
+          "description": " 帳號名稱"
+        },
+        "account_type": {
+          "type": "string",
+          "description": " 帳號類型 (phone) 手機 (email) 信箱"
+        }
+      },
+      "title": "ForgetPasswordCodeReq",
+      "required": [
+        "account",
+        "account_type"
+      ]
+    },
+    "LoginReq": {
+      "type": "object",
+      "properties": {
+        "account": {
+          "type": "string",
+          "description": " 帳號名稱"
+        },
+        "token": {
+          "type": "string",
+          "description": " 密碼或平台token,密碼請 sha256 轉碼"
+        },
+        "platform": {
+          "type": "string",
+          "description": " 平台名稱 platform, google"
+        },
+        "account_type": {
+          "type": "string",
+          "description": " 帳號類型 1 手機 2 信箱 3 自定義帳號"
+        }
+      },
+      "title": "LoginReq",
+      "required": [
+        "account",
+        "token",
+        "platform",
+        "account_type"
+      ]
+    },
+    "LoginTokenResp": {
+      "type": "object",
+      "properties": {
+        "uid": {
+          "type": "string",
+          "description": "  Account"
+        },
+        "access_token": {
+          "type": "string",
+          "description": " 訪問令牌 預設 5 分鐘過期"
+        },
+        "refresh_token": {
+          "type": "string",
+          "description": " 刷新令牌 (預設一天過期,只能用一次),當呼叫更新token api 時,會自動把舊的失效,變成新的 refresh_token ,前端要記得過其實協助刷新,刷新不過表示全失效了(重新登入)"
+        },
+        "token_type": {
+          "type": "string",
+          "description": " Bearer"
+        }
+      },
+      "title": "LoginTokenResp",
+      "required": [
+        "uid",
+        "access_token",
+        "refresh_token",
+        "token_type"
+      ]
+    },
+    "MemberLoginHeader": {
+      "type": "object",
+      "title": "MemberLoginHeader"
+    },
+    "ModifyPasswdReq": {
+      "type": "object",
+      "properties": {
+        "token": {
+          "type": "string",
+          "description": " 密碼或平台token,密碼請 sha256 轉碼"
+        },
+        "token_check": {
+          "type": "string",
+          "description": " 密碼或平台token,密碼請 sha256 轉碼"
+        }
+      },
+      "title": "ModifyPasswdReq",
+      "required": [
+        "token",
+        "token_check"
+      ]
+    },
+    "PreVerifyForgetPasswdReq": {
+      "type": "object",
+      "properties": {
+        "identifier": {
+          "type": "string",
+          "description": " 聯繫方式,可以是 email 或 phone"
+        },
+        "verify_code": {
+          "type": "string",
+          "description": " 驗證碼,長度為6"
+        }
+      },
+      "title": "PreVerifyForgetPasswdReq",
+      "required": [
+        "identifier",
+        "verify_code"
+      ]
+    },
+    "RespOK": {
+      "type": "object",
+      "title": "RespOK"
+    },
+    "Status": {
+      "type": "object",
+      "properties": {
+        "code": {
+          "type": "integer",
+          "format": "int64",
+          "description": " 狀態碼"
+        },
+        "message": {
+          "type": "string",
+          "description": " 訊息"
+        },
+        "data": {
+          "type": "object",
+          "description": " 可選的數據,當有返回時才出現"
+        },
+        "error": {
+          "type": "object",
+          "description": " 可選的錯誤信息"
+        }
+      },
+      "title": "Status",
+      "required": [
+        "code",
+        "message"
+      ]
+    },
+    "UpdatePasswordReq": {
+      "type": "object",
+      "properties": {
+        "account": {
+          "type": "string",
+          "description": " 帳號名稱"
+        },
+        "verify_code": {
+          "type": "string",
+          "description": " 驗證碼,長度為6"
+        },
+        "token": {
+          "type": "string",
+          "description": " 密碼或平台token,密碼請 sha256 轉碼"
+        },
+        "token_check": {
+          "type": "string",
+          "description": " 密碼或平台token,密碼請 sha256 轉碼"
+        }
+      },
+      "title": "UpdatePasswordReq",
+      "required": [
+        "account",
+        "verify_code",
+        "token",
+        "token_check"
+      ]
+    },
+    "UpdateTokenReq": {
+      "type": "object",
+      "properties": {
+        "uid": {
+          "type": "string",
+          "description": " 誰要更新"
+        },
+        "token": {
+          "type": "string",
+          "description": " access token -\u003e 已過期要被更新的"
+        },
+        "refresh_token": {
+          "type": "string",
+          "description": " refresh token -\u003e  重點,要驗證他的"
+        }
+      },
+      "title": "UpdateTokenReq",
+      "required": [
+        "uid",
+        "token",
+        "refresh_token"
+      ]
+    },
+    "UserInfo": {
+      "type": "object",
+      "properties": {
+        "platform": {
+          "type": "string",
+          "description": " 用戶平台 platform, google, line"
+        },
+        "uid": {
+          "type": "string",
+          "description": " 用戶 UID"
+        },
+        "avatar_url": {
+          "type": "string",
+          "description": " 頭像 URL(可選)"
+        },
+        "full_name": {
+          "type": "string",
+          "description": " 用戶全名"
+        },
+        "nickname": {
+          "type": "string",
+          "description": " 暱稱(可選)"
+        },
+        "gender_code": {
+          "type": "string",
+          "description": " 性別代碼 mail, femail ,sec"
+        },
+        "birthdate": {
+          "type": "string",
+          "description": " 生日 (格式: 19930417)"
+        },
+        "phone_number": {
+          "type": "string",
+          "description": " 電話"
+        },
+        "address": {
+          "type": "string",
+          "description": " 地址"
+        },
+        "email": {
+          "type": "string",
+          "description": " 驗證後的信箱"
+        },
+        "alarm_category": {
+          "type": "string",
+          "description": " 告警狀態"
+        },
+        "user_status": {
+          "type": "string",
+          "description": " 用戶狀態"
+        },
+        "preferred_language": {
+          "type": "string",
+          "description": " 使用語言"
+        },
+        "currency": {
+          "type": "string",
+          "description": " 使用幣種"
+        },
+        "update_at": {
+          "type": "string"
+        },
+        "create_at": {
+          "type": "string"
+        }
+      },
+      "title": "UserInfo",
+      "required": [
+        "platform",
+        "uid",
+        "avatar_url",
+        "full_name",
+        "nickname",
+        "gender_code",
+        "birthdate",
+        "phone_number",
+        "address",
+        "email",
+        "alarm_category",
+        "user_status",
+        "preferred_language",
+        "currency",
+        "update_at",
+        "create_at"
+      ]
+    },
+    "VerificationCodeRequest": {
+      "type": "object",
+      "properties": {
+        "identifier": {
+          "type": "string",
+          "description": " 聯繫方式,可以是 email 或 phone"
+        },
+        "code_type": {
+          "type": "string",
+          "description": " 驗證碼類型"
+        }
+      },
+      "title": "VerificationCodeRequest",
+      "required": [
+        "identifier",
+        "code_type"
+      ]
+    },
+    "VerifyHeader": {
+      "type": "object",
+      "title": "VerifyHeader"
+    }
+  },
+  "securityDefinitions": {
+    "apiKey": {
+      "type": "apiKey",
+      "description": "Enter JWT Bearer token **_only_**",
+      "name": "Authorization",
+      "in": "header"
+    }
+  }
+}
diff --git a/generate/api/gateway.api b/generate/api/gateway.api
new file mode 100644
index 0000000..854e4a6
--- /dev/null
+++ b/generate/api/gateway.api
@@ -0,0 +1,21 @@
+syntax = "v1"
+
+type Status {
+	Code    int64       `json:"code"` // 狀態碼
+	Message string      `json:"message"` // 訊息
+	Data    interface{} `json:"data,omitempty"` // 可選的數據,當有返回時才出現
+	Error   interface{} `json:"error,omitempty"` // 可選的錯誤信息
+}
+
+type BaseResponse {
+	Status Status      `json:"status"` // 狀態
+	Data   interface{} `json:"data"` // 資料
+}
+
+type VerifyHeader {
+	Token string `header:"token" validate:"required"`
+}
+
+type RespOK {}
+
+import "member.api"
diff --git a/generate/api/member.api b/generate/api/member.api
new file mode 100644
index 0000000..3db016f
--- /dev/null
+++ b/generate/api/member.api
@@ -0,0 +1,252 @@
+syntax = "v1"
+
+info(
+	title: "Portal-Api-Gateway (PGW)"
+	desc: "netpute web portal api gateway"
+	author: "daniel wang"
+	email: "daniel.wang@30cm.net"
+	version: "0.0.1"
+)
+
+type MemberLoginHeader {
+	DeviceID    string `header:"device_id"`
+	IpAddress	string `header:"ip_address"`
+	Brewser     string `header:"brewser"`
+}
+
+// -------------------------------------------
+
+type CreateAccountRequest {
+	Account     string `json:"account" validate:"required"`                            // 帳號名稱(line code 輸入在這邊)
+	Token       string `json:"token" validate:"required"`                              // 密碼或平台token,密碼請 sha256 轉碼,如果三方token 請隨便給一個 sha256 字串
+	TokenCheck  string `json:"token_check" validate:"required"` 		               // 密碼或平台token,token 請保持原樣,填在這邊,不用管 token
+	Platform    string `json:"platform" validate:"oneof=platform google line"`         // 平台名稱 (platform) 平台、google、line
+	AccountType string `json:"account_type" validate:"oneof=phone email platform"`     // 帳號類型 phone(手機)、email(信箱)、platform(自定義帳號) -> (如果為第三方都寫 platform)
+	MemberLoginHeader
+}
+
+type LoginReq {
+	Account     string `json:"account" validate:"required"`                         // 帳號名稱
+	Token       string `json:"token"`                                              // 密碼或平台token,密碼請 sha256 轉碼
+	Platform    string `json:"platform" validate:"oneof=platform google line"`     // 平台名稱 platform, google
+	AccountType string `json:"account_type" validate:"oneof=phone email platform"` // 帳號類型 1 手機 2 信箱 3 自定義帳號
+	MemberLoginHeader
+}
+
+type LoginTokenResp {
+	UID          string `json:"uid"`           //  Account
+	AccessToken  string `json:"access_token"`  // 訪問令牌 預設 5 分鐘過期
+	RefreshToken string `json:"refresh_token"` // 刷新令牌 (預設一天過期,只能用一次),當呼叫更新token api 時,會自動把舊的失效,變成新的 refresh_token ,前端要記得過其實協助刷新,刷新不過表示全失效了(重新登入)
+	TokenType    string `json:"token_type"`    // Bearer
+}
+
+
+type ForgetPasswordCodeReq {
+	Account     string `json:"account" validate:"required"`                     // 帳號名稱
+	AccountType string `json:"account_type" validate:"oneof=phone email"` 		// 帳號類型 (phone) 手機 (email) 信箱
+}
+
+type PreVerifyForgetPasswdReq {
+	Identifier string `json:"identifier" validate:"required"`         // 聯繫方式,可以是 email 或 phone
+	VerifyCode string `json:"verify_code" validate:"required,len=6"`  // 驗證碼,長度為6
+}
+
+type UpdateTokenReq {
+	UID          string `json:"uid" validate:"required"`           // 誰要更新
+	Token        string `json:"token" validate:"required"`         // access token -> 已過期要被更新的
+	RefreshToken string `json:"refresh_token" validate:"required"` // refresh token ->  重點,要驗證他的
+																   //	MemberLoginHeader
+}
+
+type UpdatePasswordReq {
+	Account    string `json:"account" validate:"required"`           		// 帳號名稱
+	VerifyCode string `json:"verify_code" validate:"required,len=6"` 		// 驗證碼,長度為6
+	Token      string `json:"token" validate:"required,len=64"`             // 密碼或平台token,密碼請 sha256 轉碼
+	TokenCheck string `json:"token_check" validate:"required,len=64"`       // 密碼或平台token,密碼請 sha256 轉碼
+}
+
+@server(
+	group:  member
+	prefix: /api/v1
+	schemes: https
+	timeout: 10s
+)
+service gateway {
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary:"創建新會員"
+		description: "創建一個全新的帳號,創完成之後會自動登入"
+	)
+	@handler AccountCreate
+	post /member (CreateAccountRequest) returns (LoginTokenResp)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-401 (BaseResponse) // 輸入的帳號密碼未經驗證-> 帳號密碼錯誤的意思 */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary:"登入"
+		description: "會員登入"
+	)
+	@handler Login
+	post /member/login (LoginReq) returns (LoginTokenResp)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary:"發送忘記密碼驗證"
+		description: "發送忘記密碼驗證(三分鐘內只能發一次信)"
+	)
+	@handler ForgetPasswordCode
+	post /member/forget-password-code (ForgetPasswordCodeReq) returns (RespOK)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-401 (BaseResponse) // 無效的驗證碼 */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary:"更新密碼(要發送驗證碼才可以的流程)"
+		description: "更新密碼(要發送驗證碼才可以的流程)"
+	)
+	@handler UpdatePassword
+	put /member/update-password (UpdatePasswordReq) returns (RespOK)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-401 (BaseResponse) // 無效的驗證碼 */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary:"預先驗證驗證碼"
+		description: "忘記密碼的時候看 ui. 流程要預先驗證一次才給送,"
+	)
+	@handler PreVerifyUpdatePasswordCode
+	put /member/pre-verify (PreVerifyForgetPasswdReq) returns (RespOK)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-401 (BaseResponse) // 無效的 Refresh Token */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary:"更新 Access Token"
+		description: "用 RefreshToken 換取 AccessToken"
+	)
+	@handler RefreshAccessToken
+	put /member/refresh_access_token (UpdateTokenReq) returns (LoginTokenResp)
+}
+
+// ------------------- 要登入之後才可以做的事情 ------------------------
+
+type UserInfo {
+	Platform 		  string  `json:"platform"`               // 用戶平台 platform, google, line
+	UID               string  `json:"uid"`                    // 用戶 UID
+	AvatarURL         string  `json:"avatar_url"`             // 頭像 URL(可選)
+	FullName          string  `json:"full_name"`              // 用戶全名
+	Nickname          string  `json:"nickname"`               // 暱稱(可選)
+	GenderCode        string  `json:"gender_code"`            // 性別代碼 mail, femail ,sec
+	Birthdate         string  `json:"birthdate"`              // 生日 (格式: 19930417)
+	PhoneNumber       string  `json:"phone_number"`           // 電話
+	Address           string  `json:"address"`                // 地址
+	Email             string  `json:"email"`                  // 驗證後的信箱
+	AlarmCategory     string  `json:"alarm_category"`         // 告警狀態
+	UserStatus        string  `json:"user_status"`            // 用戶狀態
+	PreferredLanguage string  `json:"preferred_language"`     // 使用語言
+	Currency          string  `json:"currency"`               // 使用幣種
+	UpdateAt          string  `json:"update_at"`
+	CreateAt          string  `json:"create_at"`
+}
+
+type BindingUserInfoReq {
+	VerifyHeader
+	PreferredLanguage string  `json:"preferred_language,optional" validate:"oneof=zh-tw en-us"` // 使用語言
+	Currency          string  `json:"currency,optional" validate:"oneof=TWD USD"`
+	AvatarURL         string  `json:"avatar_url,optional"`                                      // 頭像 URL(可選)
+	Nickname          string  `json:"nickname,optional"`
+	FullName          string  `json:"full_name,optional"`                                       // 用戶全名
+	GenderCode        string  `json:"gender_code" validate:"oneof=secret male female"`          // 性別代碼
+	Birthday         string  `json:"birthday,optional" validate:"rfc3339"`                    // 生日 (格式: unix)
+	Address           string  `json:"address,optional"`                                         // 地址
+}
+
+type VerificationCodeRequest {
+	VerifyHeader
+	Identifier 		string 	  `json:"identifier" validate:"required"`                         // 聯繫方式,可以是 email 或 phone
+	CodeType   		string    `json:"code_type" validate:"oneof=email phone forget_password"` // 驗證碼類型
+}
+
+type CheckoutVerifyReq {
+	VerifyHeader
+	Account    string `json:"account" validate:"required"`            // 帳號名稱
+	CodeType   string `json:"code_type" validate:"oneof=email phone"` // 驗證碼類型 1 信箱 2 手機
+	VerifyCode string `json:"verify_code" validate:"required,len=6"`  // 驗證碼,長度為6
+	UID string `json:"uid" validate:"required"`
+}
+
+type ModifyPasswdReq {
+	VerifyHeader
+	NewToken      string `json:"token" validate:"required,len=64"`             // 密碼或平台token,密碼請 sha256 轉碼
+	NewTokenCheck string `json:"token_check" validate:"required,len=64"`       // 密碼或平台token,密碼請 sha256 轉碼
+}
+
+@server(
+	group:  member
+	prefix: /api/v1
+	schemes: https
+	timeout: 10s
+	middleware: AuthMiddleware
+)
+
+service gateway {
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-401 (BaseResponse) // 無效的Token */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary: "會員登出"
+	)
+	@handler Logout
+	get /member/logout (VerifyHeader) returns (RespOK)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-401 (BaseResponse) // 無效的Token */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary: "取得會員資訊"
+	)
+	@handler Info
+	get /member/info (VerifyHeader) returns (UserInfo)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-403 (BaseResponse) // 無效的Token */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary: "更新會員詳細資訊"
+	)
+	@handler ModifyMemberInfo
+	put /member/info (BindingUserInfoReq) returns (UserInfo)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-401 (BaseResponse) // 無效的Token */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary: "發送邀請 - 綁定會員"
+		description: "可以依照類別(手機驗證,email驗證),同一個類型十分鐘內只能發送一次"
+	)
+	@handler SendVerifyCode
+	post /member/verify (VerificationCodeRequest) returns (RespOK)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-401 (BaseResponse) // 無效的驗證碼 */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary: "確認邀請 - 綁定會員"
+		description: "確認驗證碼是否有效"
+	)
+	@handler CheckVerifyCode
+	post /member/check-verify-code (CheckoutVerifyReq) returns (RespOK)
+
+/*	@respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
+/*	@respdoc-401 (BaseResponse) // 無效的驗證碼 */
+/*  @respdoc-500 (BaseResponse) // 伺服器出錯 */
+	@doc(
+		summary: "修改密碼"
+		description: "修改密碼"
+	)
+	@handler ModifyPasswdHandler
+	put /member/modify-passwd (ModifyPasswdReq) returns (RespOK)
+}
\ No newline at end of file
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..9200160
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,126 @@
+module biz-member-gateway
+
+go 1.24.0
+
+require (
+	code.30cm.net/digimon/app-cloudep-member-server v0.0.0-20250208060713-714163bbe6c6
+	code.30cm.net/digimon/app-cloudep-notification-service v0.0.2
+	code.30cm.net/digimon/app-cloudep-permission-server v0.0.2
+	code.30cm.net/digimon/library-go/errors v1.2.2
+	code.30cm.net/digimon/library-go/errs v1.2.14
+	code.30cm.net/digimon/library-go/validator v1.0.0
+	code.30cm.net/digimon/proto-all v0.0.0-20250309093454-2a2927ced9e2
+	github.com/go-playground/validator/v10 v10.25.0
+	github.com/golang/protobuf v1.5.4
+	github.com/grafana/pyroscope-go v1.2.0
+	github.com/shopspring/decimal v1.4.0
+	github.com/zeromicro/go-zero v1.8.1
+)
+
+require (
+	github.com/Masterminds/semver v1.4.2 // indirect
+	github.com/Masterminds/sprig v2.16.0+incompatible // indirect
+	github.com/PuerkitoBio/goquery v1.5.0 // indirect
+	github.com/andybalholm/cascadia v1.0.0 // indirect
+	github.com/aokoli/goutils v1.0.1 // indirect
+	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
+	github.com/coreos/go-semver v0.3.1 // indirect
+	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
+	github.com/fatih/color v1.18.0 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.8 // indirect
+	github.com/go-logr/logr v1.4.2 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/go-openapi/jsonpointer v0.19.6 // indirect
+	github.com/go-openapi/jsonreference v0.20.2 // indirect
+	github.com/go-openapi/swag v0.22.4 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
+	github.com/golang/mock v1.6.0 // indirect
+	github.com/google/gnostic-models v0.6.8 // indirect
+	github.com/google/go-cmp v0.6.0 // indirect
+	github.com/google/gofuzz v1.2.0 // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/gorilla/css v1.0.0 // indirect
+	github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
+	github.com/huandu/xstrings v1.2.0 // indirect
+	github.com/imdario/mergo v0.3.6 // indirect
+	github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.17.11 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
+	github.com/matcornic/hermes/v2 v2.1.0 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/olekukonko/tablewriter v0.0.5 // indirect
+	github.com/openzipkin/zipkin-go v0.4.3 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
+	github.com/prometheus/client_golang v1.21.0 // indirect
+	github.com/prometheus/client_model v0.6.1 // indirect
+	github.com/prometheus/common v0.62.0 // indirect
+	github.com/prometheus/procfs v0.15.1 // indirect
+	github.com/redis/go-redis/v9 v9.7.1 // indirect
+	github.com/rivo/uniseg v0.2.0 // indirect
+	github.com/russross/blackfriday/v2 v2.0.1 // indirect
+	github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
+	github.com/spaolacci/murmur3 v1.1.0 // indirect
+	github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
+	github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 // indirect
+	github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe // indirect
+	go.etcd.io/etcd/api/v3 v3.5.15 // indirect
+	go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
+	go.etcd.io/etcd/client/v3 v3.5.15 // indirect
+	go.mongodb.org/mongo-driver v1.17.3 // indirect
+	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+	go.opentelemetry.io/otel v1.34.0 // indirect
+	go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
+	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect
+	go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect
+	go.opentelemetry.io/otel/metric v1.34.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+	go.opentelemetry.io/otel/trace v1.34.0 // indirect
+	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
+	go.uber.org/atomic v1.10.0 // indirect
+	go.uber.org/automaxprocs v1.6.0 // indirect
+	go.uber.org/multierr v1.9.0 // indirect
+	go.uber.org/zap v1.24.0 // indirect
+	golang.org/x/crypto v0.33.0 // indirect
+	golang.org/x/net v0.35.0 // indirect
+	golang.org/x/oauth2 v0.24.0 // indirect
+	golang.org/x/sys v0.30.0 // indirect
+	golang.org/x/term v0.29.0 // indirect
+	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/time v0.10.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/grpc v1.70.0 // indirect
+	google.golang.org/protobuf v1.36.5 // indirect
+	gopkg.in/inf.v0 v0.9.1 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	k8s.io/api v0.29.3 // indirect
+	k8s.io/apimachinery v0.29.4 // indirect
+	k8s.io/client-go v0.29.3 // indirect
+	k8s.io/klog/v2 v2.110.1 // indirect
+	k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
+	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
+	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
+	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
+	sigs.k8s.io/yaml v1.4.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..8da771a
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,366 @@
+code.30cm.net/digimon/app-cloudep-member-server v0.0.0-20250208060713-714163bbe6c6 h1:yDeI9SQyPd2XDHoxpaLavw5AU0s4NPNa4is9G/ZbxNM=
+code.30cm.net/digimon/app-cloudep-member-server v0.0.0-20250208060713-714163bbe6c6/go.mod h1:4r33vmU2j7PZWzWFIn+7NAE05fuF/6/kdVdfnyHmf6Y=
+code.30cm.net/digimon/app-cloudep-notification-service v0.0.2 h1:6GkX4oPXbftD4DV1wBM0e0SD9KdfdcNTxLSEASzEaWo=
+code.30cm.net/digimon/app-cloudep-notification-service v0.0.2/go.mod h1:TdUdjsHafpkrQl6AKPDv3VdH1tSX4f+UWqnW5TaPvsM=
+code.30cm.net/digimon/app-cloudep-permission-server v0.0.2 h1:1n8RVs1/R3z1WA7uPPUC+EuQIrIqHA9Uj8Cy2eFP2rg=
+code.30cm.net/digimon/app-cloudep-permission-server v0.0.2/go.mod h1:ga0L3jN4FspRHfifGpT2nIzW0APPgzQqv5tBLf2JM2Q=
+code.30cm.net/digimon/library-go/errors v1.2.2 h1:InY2PB+6eSZi91n6dcnAwsihOQuqe+GrrDMKlHJPxXA=
+code.30cm.net/digimon/library-go/errors v1.2.2/go.mod h1:4yVE3lyHi/cLB3W59Tw70FBqO10ZhXo470NWp7aQ0tc=
+code.30cm.net/digimon/library-go/errs v1.2.14 h1:Un9wcIIjjJW8D2i0ISf8ibzp9oNT4OqLsaSKW0T4RJU=
+code.30cm.net/digimon/library-go/errs v1.2.14/go.mod h1:Hs4v7SbXNggDVBGXSYsFMjkii1qLF+rugrIpWePN4/o=
+code.30cm.net/digimon/library-go/validator v1.0.0 h1:F48CU0+Z4ZZFhKHvjgm4u0M7v5wanFfVLKz+d92o+ks=
+code.30cm.net/digimon/library-go/validator v1.0.0/go.mod h1:haNzZMm0PhdvhuX8R6N/ZMp4CjOhs7/dHgTHzeOTNrY=
+code.30cm.net/digimon/proto-all v0.0.0-20250309093454-2a2927ced9e2 h1:eF4/vGVf9Q+EW1wVMSeklXbHm4Hvm38Iwlz+VaDh9YM=
+code.30cm.net/digimon/proto-all v0.0.0-20250309093454-2a2927ced9e2/go.mod h1:jyO0y8lBk81kiBd5BP5bDo1rRDkKBN1983FnAANocR8=
+github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
+github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/sprig v2.16.0+incompatible h1:QZbMUPxRQ50EKAq3LFMnxddMu88/EUUG3qmxwtDmPsY=
+github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
+github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
+github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
+github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
+github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
+github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
+github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
+github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
+github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg=
+github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
+github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
+github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
+github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
+github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
+github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
+github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
+github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
+github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
+github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
+github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
+github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
+github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
+github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+github.com/grafana/pyroscope-go v1.2.0 h1:aILLKjTj8CS8f/24OPMGPewQSYlhmdQMBmol1d3KGj8=
+github.com/grafana/pyroscope-go v1.2.0/go.mod h1:2GHr28Nr05bg2pElS+dDsc98f3JTUh2f6Fz1hWXrqwk=
+github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
+github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
+github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
+github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
+github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
+github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
+github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
+github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ=
+github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
+github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/matcornic/hermes/v2 v2.1.0 h1:9TDYFBPFv6mcXanaDmRDEp/RTWj0dTTi+LpFnnnfNWc=
+github.com/matcornic/hermes/v2 v2.1.0/go.mod h1:2+ziJeoyRfaLiATIL8VZ7f9hpzH4oDHqTmn0bhrsgVI=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
+github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
+github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
+github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
+github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
+github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
+github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
+github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
+github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
+github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
+github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc=
+github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
+github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
+github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 h1:L0rPdfzq43+NV8rfIx2kA4iSSLRj2jN5ijYHoeXRwvQ=
+github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w=
+github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe h1:9YnI5plmy+ad6BM+JCLJb2ZV7/TNiE5l7SNKfumYKgc=
+github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe/go.mod h1:JTFJA/t820uFDoyPpErFQ3rb3amdZoPtxcKervG0OE4=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
+github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
+github.com/zeromicro/go-zero v1.8.1 h1:iUYQEMQzS9Pb8ebzJtV3FGtv/YTjZxAh/NvLW/316wo=
+github.com/zeromicro/go-zero v1.8.1/go.mod h1:gc54Ad4qt7OJ0PbKajnYsSKsZBYN4JLRIXKlqDX2A2I=
+go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
+go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
+go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
+go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
+go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
+go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
+go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
+go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
+go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
+go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY=
+go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
+go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
+go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
+go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
+go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
+go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
+go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
+go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
+go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
+golang.org/x/crypto v0.0.0-20181029175232-7e6ffbd03851/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
+golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
+golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
+golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
+golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
+golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
+golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
+gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
+k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
+k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q=
+k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y=
+k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
+k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
+k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
+k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
+k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
+k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
+k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
+k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
+sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
diff --git a/internal/config/config.go b/internal/config/config.go
new file mode 100644
index 0000000..f04d1ed
--- /dev/null
+++ b/internal/config/config.go
@@ -0,0 +1,33 @@
+package config
+
+import (
+	"time"
+
+	"github.com/zeromicro/go-zero/core/stores/redis"
+
+	"github.com/zeromicro/go-zero/rest"
+	"github.com/zeromicro/go-zero/zrpc"
+)
+
+type Config struct {
+	rest.RestConf
+	MemberRPC       zrpc.RpcClientConf
+	PermissionRPC   zrpc.RpcClientConf
+	NotificationRPC zrpc.RpcClientConf
+	// PyroScope 監控
+	PyroScope struct {
+		Enable bool
+		URL    string
+		Token  string
+	}
+	Token struct {
+		Expired        time.Duration
+		RefreshExpired time.Duration
+	}
+	// Redis Cluster
+	RedisCfg redis.RedisConf
+	Member   struct {
+		ForgetTimeOutInSec int64
+	}
+	MailSender string
+}
diff --git a/internal/domain/const.go b/internal/domain/const.go
new file mode 100644
index 0000000..91b5839
--- /dev/null
+++ b/internal/domain/const.go
@@ -0,0 +1,9 @@
+package domain
+
+const SuccessCode = 10200
+const SuccessMessage = "success"
+
+const DefaultLang = "zh-tw"
+const DefaultCurrency = "USDT"
+const DefaultRole = "user"
+const TokenTypeBearer = "Bearer"
diff --git a/internal/domain/error.go b/internal/domain/error.go
new file mode 100644
index 0000000..6eba736
--- /dev/null
+++ b/internal/domain/error.go
@@ -0,0 +1,8 @@
+package domain
+
+const (
+	APIErrorCode = 1 + iota
+	TokenNotTheSameAPIErrorCode
+	FailedToCheckVerifyCodeErrorCode
+	FailedToSetVerifyCodeErrorCode
+)
diff --git a/internal/domain/gender.go b/internal/domain/gender.go
new file mode 100644
index 0000000..eeaef16
--- /dev/null
+++ b/internal/domain/gender.go
@@ -0,0 +1,39 @@
+package domain
+
+import (
+	"fmt"
+)
+
+// 字串轉數字的對應表
+var genderStringToCode = map[string]int64{
+	"secret": 0,
+	"male":   1,
+	"female": 2,
+}
+
+// 數字轉字串的對應表
+var genderCodeToString = map[int64]string{
+	0: "secret",
+	1: "male",
+	2: "female",
+}
+
+// StringToGenderCode 將 "male"/"female"/"secret" 轉為對應數字
+func StringToGenderCode(gender string) (int64, error) {
+	code, ok := genderStringToCode[gender]
+	if !ok {
+		return -1, fmt.Errorf("invalid gender string")
+	}
+
+	return code, nil
+}
+
+// GenderCodeToString 將 0/1/2 轉為對應字串 "male"/"female"/"secret"
+func GenderCodeToString(code int64) (string, error) {
+	gender, ok := genderCodeToString[code]
+	if !ok {
+		return "", fmt.Errorf("invalid gender code")
+	}
+
+	return gender, nil
+}
diff --git a/internal/domain/payload.go b/internal/domain/payload.go
new file mode 100644
index 0000000..33f255f
--- /dev/null
+++ b/internal/domain/payload.go
@@ -0,0 +1,47 @@
+package domain
+
+import (
+	"context"
+
+	"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/token"
+)
+
+func UID(ctx context.Context) string {
+	if uid, ok := ctx.Value(token.UID.String()).(string); ok {
+		return uid
+	}
+
+	return ""
+}
+
+func Scope(ctx context.Context) string {
+	if scope, ok := ctx.Value(token.Scope.String()).(string); ok {
+		return scope
+	}
+
+	return ""
+}
+
+func Role(ctx context.Context) string {
+	if role, ok := ctx.Value(token.Role.String()).(string); ok {
+		return role
+	}
+
+	return ""
+}
+
+func DeviceID(ctx context.Context) string {
+	if deviceID, ok := ctx.Value(token.Device.String()).(string); ok {
+		return deviceID
+	}
+
+	return ""
+}
+
+func Account(ctx context.Context) string {
+	if account, ok := ctx.Value(token.Account.String()).(string); ok {
+		return account
+	}
+
+	return ""
+}
diff --git a/internal/domain/redis.go b/internal/domain/redis.go
new file mode 100644
index 0000000..5ff8646
--- /dev/null
+++ b/internal/domain/redis.go
@@ -0,0 +1,23 @@
+package domain
+
+import "strings"
+
+type RedisKey string
+
+const (
+	SendCodeRedisKey RedisKey = "vc"
+)
+
+func (key RedisKey) ToString() string {
+	return "biz-member-gw:" + string(key)
+}
+
+func (key RedisKey) With(s ...string) RedisKey {
+	parts := append([]string{string(key)}, s...)
+
+	return RedisKey(strings.Join(parts, ":"))
+}
+
+func GetSendCodeRedisKey(id string) string {
+	return SendCodeRedisKey.With(id).ToString()
+}
diff --git a/internal/handler/member/account_create_handler.go b/internal/handler/member/account_create_handler.go
new file mode 100644
index 0000000..d2e3f62
--- /dev/null
+++ b/internal/handler/member/account_create_handler.go
@@ -0,0 +1,68 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"net/http"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// AccountCreateHandler 創建新會員
+func AccountCreateHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.CreateAccountRequest
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewAccountCreateLogic(r.Context(), svcCtx)
+		resp, err := l.AccountCreate(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/check_verify_code_handler.go b/internal/handler/member/check_verify_code_handler.go
new file mode 100644
index 0000000..834cc4a
--- /dev/null
+++ b/internal/handler/member/check_verify_code_handler.go
@@ -0,0 +1,79 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// CheckVerifyCodeHandler 確認邀請 - 綁定會員
+func CheckVerifyCodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.CheckoutVerifyReq
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewCheckVerifyCodeLogic(r.Context(), svcCtx)
+		resp, err := l.CheckVerifyCode(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/forget_password_code_handler.go b/internal/handler/member/forget_password_code_handler.go
new file mode 100644
index 0000000..47fe49e
--- /dev/null
+++ b/internal/handler/member/forget_password_code_handler.go
@@ -0,0 +1,67 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// 發送忘記密碼驗證
+func ForgetPasswordCodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.ForgetPasswordCodeReq
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewForgetPasswordCodeLogic(r.Context(), svcCtx)
+		resp, err := l.ForgetPasswordCode(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/info_handler.go b/internal/handler/member/info_handler.go
new file mode 100644
index 0000000..53145d4
--- /dev/null
+++ b/internal/handler/member/info_handler.go
@@ -0,0 +1,67 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// 取得會員資訊
+func InfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.VerifyHeader
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewInfoLogic(r.Context(), svcCtx)
+		resp, err := l.Info(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/login_handler.go b/internal/handler/member/login_handler.go
new file mode 100644
index 0000000..a4acda6
--- /dev/null
+++ b/internal/handler/member/login_handler.go
@@ -0,0 +1,67 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"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
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewLoginLogic(r.Context(), svcCtx)
+		resp, err := l.Login(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/logout_handler.go b/internal/handler/member/logout_handler.go
new file mode 100644
index 0000000..c177165
--- /dev/null
+++ b/internal/handler/member/logout_handler.go
@@ -0,0 +1,55 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/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.VerifyHeader
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewLogoutLogic(r.Context(), svcCtx)
+		resp, err := l.Logout(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/modify_member_info_handler.go b/internal/handler/member/modify_member_info_handler.go
new file mode 100644
index 0000000..9994696
--- /dev/null
+++ b/internal/handler/member/modify_member_info_handler.go
@@ -0,0 +1,67 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// 更新會員詳細資訊
+func ModifyMemberInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.BindingUserInfoReq
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewModifyMemberInfoLogic(r.Context(), svcCtx)
+		resp, err := l.ModifyMemberInfo(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/modify_passwd_handler.go b/internal/handler/member/modify_passwd_handler.go
new file mode 100644
index 0000000..dc33449
--- /dev/null
+++ b/internal/handler/member/modify_passwd_handler.go
@@ -0,0 +1,67 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// 修改密碼
+func ModifyPasswdHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.ModifyPasswdReq
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewModifyPasswdLogic(r.Context(), svcCtx)
+		resp, err := l.ModifyPasswd(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/pre_verify_update_password_code_handler.go b/internal/handler/member/pre_verify_update_password_code_handler.go
new file mode 100644
index 0000000..72b4595
--- /dev/null
+++ b/internal/handler/member/pre_verify_update_password_code_handler.go
@@ -0,0 +1,67 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// 預先驗證驗證碼
+func PreVerifyUpdatePasswordCodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.PreVerifyForgetPasswdReq
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewPreVerifyUpdatePasswordCodeLogic(r.Context(), svcCtx)
+		resp, err := l.PreVerifyUpdatePasswordCode(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/refresh_access_token_handler.go b/internal/handler/member/refresh_access_token_handler.go
new file mode 100644
index 0000000..54d4422
--- /dev/null
+++ b/internal/handler/member/refresh_access_token_handler.go
@@ -0,0 +1,67 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// 更新 Access Token
+func RefreshAccessTokenHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.UpdateTokenReq
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewRefreshAccessTokenLogic(r.Context(), svcCtx)
+		resp, err := l.RefreshAccessToken(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/send_verify_code_handler.go b/internal/handler/member/send_verify_code_handler.go
new file mode 100644
index 0000000..db5e877
--- /dev/null
+++ b/internal/handler/member/send_verify_code_handler.go
@@ -0,0 +1,67 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// SendVerifyCodeHandler 發送邀請 - 綁定會員
+func SendVerifyCodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.VerificationCodeRequest
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewSendVerifyCodeLogic(r.Context(), svcCtx)
+		resp, err := l.SendVerifyCode(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/member/update_password_handler.go b/internal/handler/member/update_password_handler.go
new file mode 100644
index 0000000..113f177
--- /dev/null
+++ b/internal/handler/member/update_password_handler.go
@@ -0,0 +1,67 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"net/http"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+
+	"biz-member-gateway/internal/logic/member"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+// 更新密碼(要發送驗證碼才可以的流程)
+func UpdatePasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.UpdatePasswordReq
+		if err := httpx.Parse(r, &req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		if err := svcCtx.Validate.ValidateAll(req); err != nil {
+			e := ers.InvalidFormat(err.Error())
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: err.Error(),
+				},
+			})
+
+			return
+		}
+
+		l := member.NewUpdatePasswordLogic(r.Context(), svcCtx)
+		resp, err := l.UpdatePassword(&req)
+		if err != nil {
+			e := ers.FromError(err)
+			if e.FullCode() == 0 {
+				e = ers.FromGRPCError(err)
+			}
+			httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.BaseResponse{
+				Status: types.Status{
+					Code:    int64(e.FullCode()),
+					Message: e.Error(),
+				},
+			})
+		} else {
+			httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, types.BaseResponse{
+				Status: types.Status{
+					Code:    domain.SuccessCode,
+					Message: domain.SuccessMessage,
+				},
+				Data: resp,
+			})
+		}
+	}
+}
diff --git a/internal/handler/routes.go b/internal/handler/routes.go
new file mode 100644
index 0000000..695be74
--- /dev/null
+++ b/internal/handler/routes.go
@@ -0,0 +1,105 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.8.1
+
+package handler
+
+import (
+	"net/http"
+	"time"
+
+	member "biz-member-gateway/internal/handler/member"
+	"biz-member-gateway/internal/svc"
+
+	"github.com/zeromicro/go-zero/rest"
+)
+
+func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
+	server.AddRoutes(
+		[]rest.Route{
+			{
+				// 創建新會員
+				Method:  http.MethodPost,
+				Path:    "/member",
+				Handler: member.AccountCreateHandler(serverCtx),
+			},
+			{
+				// 發送忘記密碼驗證
+				Method:  http.MethodPost,
+				Path:    "/member/forget-password-code",
+				Handler: member.ForgetPasswordCodeHandler(serverCtx),
+			},
+			{
+				// 登入
+				Method:  http.MethodPost,
+				Path:    "/member/login",
+				Handler: member.LoginHandler(serverCtx),
+			},
+			{
+				// 預先驗證驗證碼
+				Method:  http.MethodPut,
+				Path:    "/member/pre-verify",
+				Handler: member.PreVerifyUpdatePasswordCodeHandler(serverCtx),
+			},
+			{
+				// 更新 Access Token
+				Method:  http.MethodPut,
+				Path:    "/member/refresh_access_token",
+				Handler: member.RefreshAccessTokenHandler(serverCtx),
+			},
+			{
+				// 更新密碼(要發送驗證碼才可以的流程)
+				Method:  http.MethodPut,
+				Path:    "/member/update-password",
+				Handler: member.UpdatePasswordHandler(serverCtx),
+			},
+		},
+		rest.WithPrefix("/api/v1"),
+		rest.WithTimeout(10000*time.Millisecond),
+	)
+
+	server.AddRoutes(
+		rest.WithMiddlewares(
+			[]rest.Middleware{serverCtx.AuthMiddleware},
+			[]rest.Route{
+				{
+					// 確認邀請 - 綁定會員
+					Method:  http.MethodPost,
+					Path:    "/member/check-verify-code",
+					Handler: member.CheckVerifyCodeHandler(serverCtx),
+				},
+				{
+					// 取得會員資訊
+					Method:  http.MethodGet,
+					Path:    "/member/info",
+					Handler: member.InfoHandler(serverCtx),
+				},
+				{
+					// 更新會員詳細資訊
+					Method:  http.MethodPut,
+					Path:    "/member/info",
+					Handler: member.ModifyMemberInfoHandler(serverCtx),
+				},
+				{
+					// 會員登出
+					Method:  http.MethodGet,
+					Path:    "/member/logout",
+					Handler: member.LogoutHandler(serverCtx),
+				},
+				{
+					// 修改密碼
+					Method:  http.MethodPut,
+					Path:    "/member/modify-passwd",
+					Handler: member.ModifyPasswdHandler(serverCtx),
+				},
+				{
+					// 發送邀請 - 綁定會員
+					Method:  http.MethodPost,
+					Path:    "/member/verify",
+					Handler: member.SendVerifyCodeHandler(serverCtx),
+				},
+			}...,
+		),
+		rest.WithPrefix("/api/v1"),
+		rest.WithTimeout(10000*time.Millisecond),
+	)
+}
diff --git a/internal/logic/member/account_create_logic.go b/internal/logic/member/account_create_logic.go
new file mode 100644
index 0000000..b2d75b4
--- /dev/null
+++ b/internal/logic/member/account_create_logic.go
@@ -0,0 +1,242 @@
+package member
+
+import (
+	bizDomain "biz-member-gateway/internal/domain"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+
+	memberUC "code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/usecase"
+	tokenUC "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/usecase"
+
+	"time"
+
+	"code.30cm.net/digimon/library-go/errs"
+	"code.30cm.net/digimon/library-go/errs/code"
+
+	"context"
+
+	"github.com/golang/protobuf/proto"
+
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	memberProto "code.30cm.net/digimon/proto-all/pkg/member"
+	permissionProto "code.30cm.net/digimon/proto-all/pkg/permission"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type AccountCreateLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewAccountCreateLogic 創建新會員
+func NewAccountCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AccountCreateLogic {
+	return &AccountCreateLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+// AccountCreate 建立新帳號 -> 業務邏輯
+func (l *AccountCreateLogic) AccountCreate(req *types.CreateAccountRequest) (resp *types.LoginTokenResp, err error) {
+	platform := member.GetPlatformByPlatformCode(req.Platform)
+	if platform == member.PlatformNone {
+		// http 400
+		return nil, errs.InvalidFormat("platform not support")
+	}
+
+	// Step 1: 根據平台構建相關數據
+	createAccountReq, bindingUserReq, createUserInfoRequest, err := l.buildAccountRequests(req, platform)
+	if err != nil {
+		// 裡面有錯從 Grpc 已經轉換完成
+		return nil, err
+	}
+
+	// Step 2: 建立帳號
+	_, err = l.svcCtx.MemberRPC.CreateUserAccount(l.ctx, &memberProto.CreateLoginUserReq{
+		LoginId:  createAccountReq.LoginID,
+		Token:    createAccountReq.Token,
+		Platform: createAccountReq.Platform.ToInt64(),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	// Step 3: 綁定帳號並獲取 UID
+	account, err := l.svcCtx.MemberRPC.BindAccount(l.ctx, &memberProto.BindingUserReq{
+		LoginId: bindingUserReq.LoginID,
+		Type:    int64(bindingUserReq.Type),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	// Step 4: 更新使用者資訊
+	_, err = l.svcCtx.MemberRPC.BindUserInfo(l.ctx, &memberProto.CreateUserInfoReq{
+		Uid:         account.Uid,
+		AlarmType:   memberProto.AlarmType(createUserInfoRequest.AlarmCategory),
+		Status:      memberProto.MemberStatus(createUserInfoRequest.UserStatus),
+		Language:    createUserInfoRequest.PreferredLanguage,
+		Currency:    createUserInfoRequest.Currency,
+		Avatar:      createUserInfoRequest.AvatarURL,
+		NickName:    createUserInfoRequest.Nickname,
+		FullName:    createUserInfoRequest.FullName,
+		Gender:      createUserInfoRequest.GenderCode,
+		Birthdate:   createUserInfoRequest.Birthdate,
+		PhoneNumber: createUserInfoRequest.PhoneNumber,
+		Email:       createUserInfoRequest.Email,
+		Address:     createUserInfoRequest.Address,
+	})
+	if err != nil {
+		return nil, err
+	}
+	// TODO 綁定角色
+
+	// Step 5: 生成 Token
+	t, err := l.generateToken(req, account.GetUid())
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.LoginTokenResp{
+		UID:          account.GetUid(),
+		AccessToken:  t.AccessToken,
+		RefreshToken: t.RefreshToken,
+		TokenType:    bizDomain.TokenTypeBearer,
+	}, nil
+}
+
+// 構建不同平台的請求數據
+func (l *AccountCreateLogic) buildAccountRequests(req *types.CreateAccountRequest, platform member.Platform) (
+	memberUC.CreateLoginUserRequest, memberUC.BindingUser, memberUC.CreateUserInfoRequest, error) {
+	var createAccountReq memberUC.CreateLoginUserRequest
+	var bindingUserReq memberUC.BindingUser
+	var createUserInfoRequest memberUC.CreateUserInfoRequest
+	switch platform {
+	case member.Digimon:
+		// 驗證 Token 是否一致
+		if req.Token != req.TokenCheck {
+			return createAccountReq, bindingUserReq, createUserInfoRequest, errs.NewError(
+				code.CloudEPMember, code.InvalidRange, bizDomain.TokenNotTheSameAPIErrorCode,
+				"failed to verify check token",
+			)
+		}
+		createAccountReq = memberUC.CreateLoginUserRequest{
+			LoginID:  req.Account,
+			Token:    req.Token,
+			Platform: platform,
+		}
+		bindingUserReq = memberUC.BindingUser{
+			LoginID: req.Account,
+			Type:    member.GetAccountTypeByCode(req.AccountType),
+		}
+		createUserInfoRequest = memberUC.CreateUserInfoRequest{
+			AlarmCategory:     member.AlarmNoAlert,
+			UserStatus:        member.AccountStatusUnverified,
+			PreferredLanguage: bizDomain.DefaultLang,
+			Currency:          bizDomain.DefaultCurrency,
+		}
+
+	case member.Google:
+		googleToken, err := l.svcCtx.MemberRPC.VerifyGoogleAuthResult(l.ctx, &memberProto.VerifyAuthResultReq{
+			Account: proto.String(req.Account),
+			Token:   req.Token,
+		})
+		if err != nil {
+			return createAccountReq, bindingUserReq, createUserInfoRequest, err
+		}
+
+		createAccountReq = memberUC.CreateLoginUserRequest{
+			LoginID:  *googleToken.Email,
+			Token:    "",
+			Platform: platform,
+		}
+		bindingUserReq = memberUC.BindingUser{
+			LoginID: *googleToken.Email,
+			Type:    member.GetAccountTypeByCode(req.AccountType),
+		}
+		createUserInfoRequest = memberUC.CreateUserInfoRequest{
+			AvatarURL:         googleToken.Picture,
+			FullName:          googleToken.Name,
+			Nickname:          googleToken.Name,
+			Email:             googleToken.Email,
+			AlarmCategory:     member.AlarmNoAlert,
+			UserStatus:        member.AccountStatusActive,
+			PreferredLanguage: bizDomain.DefaultLang,
+			Currency:          bizDomain.DefaultCurrency,
+		}
+
+	case member.Line:
+		// 用 code 換取 line access token
+		lineAccessToken, err := l.svcCtx.MemberRPC.LineCodeToAccessToken(l.ctx, &memberProto.LineGetTokenReq{
+			Code: req.Account,
+		})
+		if err != nil {
+			return createAccountReq, bindingUserReq, createUserInfoRequest, err
+		}
+		// 用 access token 換取 line 使用者資料
+		userInfo, err := l.svcCtx.MemberRPC.LineGetProfileByAccessToken(l.ctx, &memberProto.LineGetUserInfoReq{
+			Token: lineAccessToken.Token,
+		})
+		if err != nil {
+			return createAccountReq, bindingUserReq, createUserInfoRequest, err
+		}
+
+		createAccountReq = memberUC.CreateLoginUserRequest{
+			LoginID:  userInfo.UserId,
+			Token:    "",
+			Platform: platform,
+		}
+		bindingUserReq = memberUC.BindingUser{
+			LoginID: userInfo.UserId,
+			Type:    member.GetAccountTypeByCode(req.AccountType),
+		}
+
+		createUserInfoRequest = memberUC.CreateUserInfoRequest{
+			AvatarURL:         proto.String(userInfo.PictureUrl),
+			FullName:          proto.String(userInfo.DisplayName),
+			Nickname:          proto.String(userInfo.DisplayName),
+			AlarmCategory:     member.AlarmNoAlert,
+			UserStatus:        member.AccountStatusActive,
+			PreferredLanguage: bizDomain.DefaultLang,
+			Currency:          bizDomain.DefaultCurrency,
+		}
+	case member.PlatformNone:
+	default:
+		return createAccountReq, bindingUserReq, createUserInfoRequest, errs.InvalidFormat("invalid platform")
+	}
+
+	return createAccountReq, bindingUserReq, createUserInfoRequest, nil
+}
+
+// 生成 Token 這裡是註冊的,所以角色是固定的,不能讓平常註冊的使用者選擇角色
+func (l *AccountCreateLogic) generateToken(req *types.CreateAccountRequest, uid string) (tokenUC.AccessTokenResponse, error) {
+	credentials := "client_credentials"
+	// 生成預設的Role -> 所有邏輯上的其他角色,應該都要從 user 開始,後續再通過admin 做更改
+	// 第一個 admin 應該是直接插入資料庫的
+	t, err := l.svcCtx.TokenRPC.NewToken(l.ctx, &permissionProto.AuthorizationReq{
+		GrantType:      credentials,
+		DeviceId:       req.DeviceID,
+		Scope:          bizDomain.DefaultRole,
+		IsRefreshToken: true,
+		Account:        req.Account,
+		Uid:            uid,
+		Expires:        proto.Int64(time.Now().UTC().Add(l.svcCtx.Config.Token.Expired).UnixNano()),        // 指定到期的時間
+		RefreshExpire:  proto.Int64(time.Now().UTC().Add(l.svcCtx.Config.Token.RefreshExpired).UnixNano()), // 指定到期的時間
+		Data:           map[string]string{},
+		Role:           bizDomain.DefaultRole,
+	})
+	if err != nil {
+		return tokenUC.AccessTokenResponse{}, errs.FromGRPCError(err)
+	}
+
+	return tokenUC.AccessTokenResponse{
+		AccessToken:  t.AccessToken,
+		ExpiresIn:    t.ExpiresIn,
+		RefreshToken: t.RefreshToken,
+	}, nil
+}
diff --git a/internal/logic/member/check_verify_code_logic.go b/internal/logic/member/check_verify_code_logic.go
new file mode 100644
index 0000000..5fa5a91
--- /dev/null
+++ b/internal/logic/member/check_verify_code_logic.go
@@ -0,0 +1,104 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"context"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+	"code.30cm.net/digimon/library-go/errs"
+	"code.30cm.net/digimon/library-go/errs/code"
+
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+
+	memberProto "code.30cm.net/digimon/proto-all/pkg/member"
+)
+
+type CheckVerifyCodeLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewCheckVerifyCodeLogic 確認邀請 - 綁定會員
+func NewCheckVerifyCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckVerifyCodeLogic {
+	return &CheckVerifyCodeLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *CheckVerifyCodeLogic) CheckVerifyCode(req *types.CheckoutVerifyReq) (resp *types.RespOK, err error) {
+	var account string
+	accountType := member.GetAccountTypeByCode(req.CodeType)
+
+	switch accountType {
+	case member.AccountTypePhone:
+		phone, isPhone := normalizeTaiwanMobile(req.Account)
+		if !isPhone {
+			return nil, errs.InvalidFormat("phone number is invalid")
+		}
+		account = phone
+	case member.AccountTypeMail:
+		if !isValidEmail(req.Account) {
+			return nil, errs.InvalidFormat("email is invalid")
+		}
+		account = req.Account
+	default:
+		return nil, errs.InvalidFormat("invalid account type")
+	}
+
+	ct := member.GetGetCodeNameByCode(req.CodeType)
+	if _, err := l.svcCtx.MemberRPC.CheckRefreshCode(l.ctx, &memberProto.VerifyRefreshCodeReq{
+		VerifyCode: req.VerifyCode,
+		Account:    account,
+		CodeType:   int32(ct),
+	}); err != nil {
+		return nil, errs.Forbidden("failed to get verify code").Wrap(err)
+	}
+
+	// 處理綁定 Email 或 Phone
+	switch ct {
+	case member.GenerateCodeTypeEmail:
+		if _, err := l.svcCtx.MemberRPC.BindVerifyEmail(l.ctx, &memberProto.BindVerifyEmailReq{
+			Uid:   req.UID,
+			Email: account,
+		}); err != nil {
+			return nil, errs.DatabaseErrorWithScope(
+				code.CloudEPMember, domain.FailedToCheckVerifyCodeErrorCode,
+				"failed to bind email", err.Error())
+		}
+	case member.GenerateCodeTypePhone:
+		if _, err := l.svcCtx.MemberRPC.BindVerifyPhone(l.ctx, &memberProto.BindVerifyPhoneReq{
+			Uid:   req.UID,
+			Phone: account,
+		}); err != nil {
+			return nil, errs.DatabaseErrorWithScope(
+				code.CloudEPMember, domain.FailedToCheckVerifyCodeErrorCode, "failed to bind phone", err.Error())
+		}
+	}
+
+	// 更新帳戶狀態
+	_, err = l.svcCtx.MemberRPC.UpdateStatus(l.ctx, &memberProto.UpdateStatusReq{
+		Uid:    req.UID,
+		Status: memberProto.MemberStatus(member.AccountStatusActive),
+	})
+	if err != nil {
+		return nil, errs.DatabaseErrorWithScope(
+			code.CloudEPMember, domain.FailedToCheckVerifyCodeErrorCode, "failed to update status", err.Error())
+	}
+
+	// 最後刪除驗證碼
+	if _, err := l.svcCtx.MemberRPC.VerifyRefreshCode(l.ctx, &memberProto.VerifyRefreshCodeReq{
+		VerifyCode: req.VerifyCode,
+		Account:    account,
+		CodeType:   int32(ct),
+	}); err != nil {
+		return nil, errs.Forbidden("failed to get verify code").Wrap(err)
+	}
+
+	return &types.RespOK{}, nil
+}
diff --git a/internal/logic/member/forget_password_code_logic.go b/internal/logic/member/forget_password_code_logic.go
new file mode 100644
index 0000000..840d669
--- /dev/null
+++ b/internal/logic/member/forget_password_code_logic.go
@@ -0,0 +1,192 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+	"bytes"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+	"code.30cm.net/digimon/library-go/errs/code"
+
+	"context"
+	"fmt"
+	"html/template"
+
+	"code.30cm.net/digimon/library-go/errs"
+
+	ntpl "code.30cm.net/digimon/app-cloudep-notification-service/pkg/domain/template"
+	memberProto "code.30cm.net/digimon/proto-all/pkg/member"
+	notificationProto "code.30cm.net/digimon/proto-all/pkg/notification"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type ForgetPasswordCodeLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewForgetPasswordCodeLogic 發送忘記密碼驗證
+func NewForgetPasswordCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ForgetPasswordCodeLogic {
+	return &ForgetPasswordCodeLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *ForgetPasswordCodeLogic) ForgetPasswordCode(req *types.ForgetPasswordCodeReq) (resp *types.RespOK, err error) {
+	// 1. 驗證並標準化帳號
+	acc, err := normalizeAccount(req.AccountType, req.Account)
+	if err != nil {
+		return nil, err
+	}
+
+	// 2. 統一檢查冷卻時間與平台正確性
+	if err = l.checkAccountValidity(acc); err != nil {
+		return nil, err
+	}
+
+	// 3. 生成驗證碼
+	vcode, err := l.svcCtx.MemberRPC.GenerateRefreshCode(l.ctx, &memberProto.GenerateRefreshCodeReq{
+		Account:  acc,
+		CodeType: int32(member.GenerateCodeTypeForgetPassword),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	// 4. 取得用戶資訊
+	uidInfo, err := l.svcCtx.MemberRPC.GetUIDByAccount(l.ctx, &memberProto.GetUIDByAccountReq{Account: acc})
+	if err != nil {
+		return nil, err
+	}
+	userInfo, err := l.svcCtx.MemberRPC.GetUserInfo(l.ctx, &memberProto.GetUserInfoReq{Uid: uidInfo.GetUid()})
+	if err != nil {
+		return nil, err
+	}
+
+	// 5. 發送驗證碼
+	nickname := getEmailShowName(userInfo)
+	if err = l.sendVerificationCode(req.AccountType, acc, userInfo.Data, vcode.Data.VerifyCode, nickname); err != nil {
+		return nil, err
+	}
+
+	// 6. 設置 Redis 驗證碼鍵值
+	rk := domain.GetSendCodeRedisKey(fmt.Sprintf("%s:%d", acc, member.GenerateCodeTypeForgetPassword))
+	l.setRedisKeyWithExpiry(rk, vcode.Data.VerifyCode, int(l.svcCtx.Config.Member.ForgetTimeOutInSec))
+
+	return &types.RespOK{}, nil
+}
+
+// normalizeAccount 驗證並標準化帳號
+func normalizeAccount(accountType, account string) (string, error) {
+	at := member.GetAccountTypeByCode(accountType)
+	switch at {
+	case member.AccountTypePhone:
+		phone, ok := normalizeTaiwanMobile(account)
+		if !ok {
+			return "", errs.InvalidFormat("phone number is invalid")
+		}
+		return phone, nil
+	case member.AccountTypeMail:
+		if !isValidEmail(account) {
+			return "", errs.InvalidFormat("email is invalid")
+		}
+		return account, nil
+	default:
+		return "", errs.InvalidFormat("unsupported account type")
+	}
+}
+
+// checkAccountValidity 同時檢查發送冷卻與平台正確性
+func (l *ForgetPasswordCodeLogic) checkAccountValidity(acc string) error {
+	// 檢查發送冷卻
+	rk := domain.GetSendCodeRedisKey(fmt.Sprintf("%s:%d", acc, member.GenerateCodeTypeForgetPassword))
+	if cached, err := l.svcCtx.Redis.GetCtx(l.ctx, rk); err != nil || cached != "" {
+		return errs.InvalidRange("verification code already sent, please wait for system to send again")
+	}
+
+	// 檢查平台是否正確(只允許平台帳號進行忘記密碼操作)
+	accountInfo, err := l.svcCtx.MemberRPC.GetUserAccountInfo(l.ctx, &memberProto.GetUIDByAccountReq{Account: acc})
+	if err != nil {
+		return err
+	}
+	if accountInfo.Data.Platform != member.Digimon.ToInt64() {
+		return errs.InvalidResourceState("failed to send verify code since platform not correct")
+	}
+	return nil
+}
+
+// sendVerificationCode 根據帳號類型發送驗證碼
+func (l *ForgetPasswordCodeLogic) sendVerificationCode(accountType, acc string, info *memberProto.UserInfo, verifyCode, nickname string) error {
+	switch member.GetAccountTypeByCode(accountType) {
+	case member.AccountTypePhone:
+		// TODO: 傳送簡訊
+		fmt.Printf("SMS Template: %s\n", verifyCode)
+	case member.AccountTypeMail:
+		return l.sendVerificationEmail(acc, info, verifyCode, nickname)
+	default:
+		return errs.InvalidResourceState("unsupported account type")
+	}
+	return nil
+}
+
+type EmailTmpInfo struct {
+	Username   string
+	VerifyCode string
+}
+
+// sendVerificationEmail 發送驗證郵件
+func (l *ForgetPasswordCodeLogic) sendVerificationEmail(recipientEmail string, info *memberProto.UserInfo, verifyCode, nickname string) error {
+	tpl, err := l.svcCtx.NotificationRPC.GetStaticTemplate(l.ctx, &notificationProto.TemplateReq{
+		Language:   info.Language,
+		TemplateId: string(ntpl.ForgetPasswordVerify),
+	})
+	if err != nil {
+		return err
+	}
+
+	tmpl, err := template.New("ForgetPasswordEmail").Parse(tpl.GetBody())
+	if err != nil {
+		return err
+	}
+
+	emailParams := EmailTmpInfo{Username: nickname, VerifyCode: verifyCode}
+	var buf bytes.Buffer
+	if err := tmpl.Execute(&buf, emailParams); err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.NotificationRPC.SendMail(l.ctx, &notificationProto.SendMailReq{
+		To:      recipientEmail,
+		Subject: tpl.Title,
+		Body:    buf.String(),
+		From:    l.svcCtx.Config.MailSender,
+	})
+	return err
+}
+
+// getEmailShowName 取得寄信用的顯示名稱
+func getEmailShowName(info *memberProto.GetUserInfoResp) string {
+	if info.Data.FullName != nil {
+		return *info.Data.FullName
+	}
+	if info.Data.NickName != nil {
+		return *info.Data.NickName
+	}
+	return info.Data.Uid
+}
+
+// setRedisKeyWithExpiry 設置 Redis 鍵與過期時間
+func (l *ForgetPasswordCodeLogic) setRedisKeyWithExpiry(rk, verifyCode string, expiry int) {
+	if status, err := l.svcCtx.Redis.SetnxExCtx(l.ctx, rk, verifyCode, expiry); err != nil || !status {
+		_ = errs.DatabaseErrorWithScopeL(code.CloudEPMember, domain.FailedToSetVerifyCodeErrorCode,
+			logx.WithContext(l.ctx),
+			[]logx.LogField{
+				{Key: "redisKey", Value: rk},
+				{Key: "error", Value: err.Error()},
+			}, "failed to set redis expire").Wrap(err)
+	}
+}
diff --git a/internal/logic/member/info_logic.go b/internal/logic/member/info_logic.go
new file mode 100644
index 0000000..67ceb88
--- /dev/null
+++ b/internal/logic/member/info_logic.go
@@ -0,0 +1,95 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+	"biz-member-gateway/internal/utils"
+	"context"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+	memberProto "code.30cm.net/digimon/proto-all/pkg/member"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type InfoLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewInfoLogic 取得會員資訊
+func NewInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *InfoLogic {
+	return &InfoLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *InfoLogic) Info(req *types.VerifyHeader) (resp *types.UserInfo, err error) {
+	info, err := l.svcCtx.MemberRPC.GetUserInfo(l.ctx, &memberProto.GetUserInfoReq{
+		Uid: domain.UID(l.ctx),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	accountInfo, err := l.svcCtx.MemberRPC.GetUserAccountInfo(l.ctx, &memberProto.GetUIDByAccountReq{
+		Account: domain.Account(l.ctx),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	alarmType := member.AlarmType(info.Data.AlarmType)
+	status := member.Status(info.Data.Status)
+	res := &types.UserInfo{
+		Platform:          member.Platform(accountInfo.Data.Platform).ToString(),
+		UID:               domain.UID(l.ctx),
+		UpdateAt:          utils.UnixToRfc3339(info.Data.UpdateTime),
+		CreateAt:          utils.UnixToRfc3339(info.Data.CreateTime),
+		AlarmCategory:     alarmType.CodeToString(),
+		UserStatus:        status.CodeToString(),
+		PreferredLanguage: info.Data.Language,
+		Currency:          info.Data.Currency,
+	}
+
+	if info.Data.AvatarUrl != nil {
+		res.AvatarURL = *info.Data.AvatarUrl
+	}
+	if info.Data.FullName != nil {
+		res.FullName = *info.Data.FullName
+	}
+	if info.Data.NickName != nil {
+		res.Nickname = *info.Data.NickName
+	}
+
+	if info.Data.GenderCode != nil {
+		gc, err := domain.GenderCodeToString(*info.Data.GenderCode)
+		if err != nil {
+			gc = "secret"
+		}
+		res.GenderCode = gc
+	}
+
+	if info.Data.Birthday != nil {
+		// RFC 3339
+		res.Birthdate = utils.UnixToRfc3339(*info.Data.Birthday)
+	}
+
+	if info.Data.Phone != nil {
+		res.PhoneNumber = *info.Data.Phone
+	}
+
+	if info.Data.Address != nil {
+		res.Address = *info.Data.Address
+	}
+
+	if info.Data.Email != nil {
+		res.Email = *info.Data.Email
+	}
+
+	return res, nil
+}
diff --git a/internal/logic/member/login_logic.go b/internal/logic/member/login_logic.go
new file mode 100644
index 0000000..ac993ef
--- /dev/null
+++ b/internal/logic/member/login_logic.go
@@ -0,0 +1,168 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	bizDomain "biz-member-gateway/internal/domain"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+	"context"
+	"regexp"
+	"strings"
+	"time"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+	memberUC "code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/usecase"
+	"code.30cm.net/digimon/library-go/errs"
+	memberProto "code.30cm.net/digimon/proto-all/pkg/member"
+	permissionProto "code.30cm.net/digimon/proto-all/pkg/permission"
+	"github.com/golang/protobuf/proto"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type LoginLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewLoginLogic 登入
+func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
+	return &LoginLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginTokenResp, err error) {
+	var result memberUC.VerifyAuthResultResponse
+	// Step 1 驗證進來的 Token
+	platform := member.GetPlatformByPlatformCode(req.Platform)
+
+	switch platform {
+	case member.Digimon:
+		switch member.GetAccountTypeByCode(req.AccountType) {
+		case member.AccountTypePhone:
+			phone, isPhone := normalizeTaiwanMobile(req.Account)
+			if !isPhone {
+				return nil, errs.InvalidFormat("phone number is invalid")
+			}
+			req.Account = phone
+		case member.AccountTypeMail:
+			if !isValidEmail(req.Account) {
+				return nil, errs.InvalidFormat("email is invalid")
+			}
+		}
+
+		// 原始平台驗證
+		res, err := l.svcCtx.MemberRPC.VerifyPlatformAuthResult(l.ctx, &memberProto.VerifyAuthResultReq{
+			Account: proto.String(req.Account),
+			Token:   req.Token,
+		})
+		if err != nil {
+			return nil, err
+		}
+		result.Status = res.Status
+	case member.Google:
+		// 原始平台驗證
+		_, err := l.svcCtx.MemberRPC.VerifyGoogleAuthResult(l.ctx, &memberProto.VerifyAuthResultReq{
+			Account: proto.String(req.Account),
+			Token:   req.Token,
+		})
+
+		if err != nil {
+			return nil, err
+		}
+		result.Status = true
+	case member.Line:
+		// 原始平台驗證
+		accessToken, err := l.svcCtx.MemberRPC.LineCodeToAccessToken(l.ctx, &memberProto.LineGetTokenReq{
+			Code: req.Account,
+		})
+		if err != nil {
+			return nil, err
+		}
+
+		// 換資料
+		userInfo, err := l.svcCtx.MemberRPC.LineGetProfileByAccessToken(l.ctx, &memberProto.LineGetUserInfoReq{
+			Token: accessToken.Token,
+		})
+		if err != nil {
+			return nil, err
+		}
+
+		result.Status = true
+		req.Account = userInfo.UserId
+	case member.PlatformNone:
+	default:
+		return nil, errs.InvalidFormat("invalid platform")
+	}
+
+	if !result.Status {
+		return nil, errs.Unauthorized("failed to validate password ")
+	}
+
+	account, err := l.svcCtx.MemberRPC.GetUIDByAccount(l.ctx, &memberProto.GetUIDByAccountReq{
+		Account: req.Account,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	credentials := "client_credentials"
+	// TODO 去拿這個使用者Role
+	role := bizDomain.DefaultRole
+
+	t, err := l.svcCtx.TokenRPC.NewToken(l.ctx, &permissionProto.AuthorizationReq{
+		GrantType:      credentials,
+		DeviceId:       req.DeviceID,
+		Scope:          role,
+		IsRefreshToken: true,
+		Account:        req.Account,
+		Uid:            account.GetUid(),
+		Expires:        proto.Int64(time.Now().UTC().Add(l.svcCtx.Config.Token.Expired).UnixNano()),        // 指定到期的時間
+		RefreshExpire:  proto.Int64(time.Now().UTC().Add(l.svcCtx.Config.Token.RefreshExpired).UnixNano()), // 指定到期的時間
+		Data:           map[string]string{},
+		Role:           role,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.LoginTokenResp{
+		UID:          account.GetUid(),
+		AccessToken:  t.AccessToken,
+		RefreshToken: t.RefreshToken,
+		TokenType:    domain.TokenTypeBearer,
+	}, nil
+}
+
+// 標準化號碼並驗證是否為合法台灣手機號碼
+func normalizeTaiwanMobile(phone string) (string, bool) {
+	// 移除空格
+	phone = strings.ReplaceAll(phone, " ", "")
+
+	// 移除 "+886" 並將剩餘部分標準化
+	if strings.HasPrefix(phone, "+886") {
+		phone = strings.TrimPrefix(phone, "+886")
+		if !strings.HasPrefix(phone, "0") {
+			phone = "0" + phone
+		}
+	}
+
+	// 正則表達式驗證標準化後的號碼
+	regex := regexp.MustCompile(`^(09\d{8})$`)
+	if regex.MatchString(phone) {
+		return phone, true
+	}
+
+	return "", false
+}
+
+// 驗證 Email 格式的函數
+func isValidEmail(email string) bool {
+	// 定義正則表達式
+	regex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
+
+	return regex.MatchString(email)
+}
diff --git a/internal/logic/member/logout_logic.go b/internal/logic/member/logout_logic.go
new file mode 100644
index 0000000..9d324f1
--- /dev/null
+++ b/internal/logic/member/logout_logic.go
@@ -0,0 +1,38 @@
+package member
+
+import (
+	"context"
+
+	permissionProto "code.30cm.net/digimon/proto-all/pkg/permission"
+
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type LogoutLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewLogoutLogic 會員登出
+func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogic {
+	return &LogoutLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *LogoutLogic) Logout(req *types.VerifyHeader) (*types.RespOK, error) {
+	_, err := l.svcCtx.TokenRPC.CancelToken(l.ctx, &permissionProto.CancelTokenReq{
+		Token: req.Token,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.RespOK{}, nil
+}
diff --git a/internal/logic/member/modify_member_info_logic.go b/internal/logic/member/modify_member_info_logic.go
new file mode 100644
index 0000000..b3998fa
--- /dev/null
+++ b/internal/logic/member/modify_member_info_logic.go
@@ -0,0 +1,144 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+	"biz-member-gateway/internal/utils"
+	"context"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+	memberProto "code.30cm.net/digimon/proto-all/pkg/member"
+	"github.com/golang/protobuf/proto"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type ModifyMemberInfoLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// 更新會員詳細資訊
+func NewModifyMemberInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ModifyMemberInfoLogic {
+	return &ModifyMemberInfoLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *ModifyMemberInfoLogic) ModifyMemberInfo(req *types.BindingUserInfoReq) (*types.UserInfo, error) {
+	update := &memberProto.UpdateUserInfoReq{
+		Uid: domain.UID(l.ctx),
+	}
+	if req.Nickname != "" {
+		update.NickName = proto.String(req.Nickname)
+	}
+
+	if req.PreferredLanguage != "" {
+		update.Language = proto.String(req.PreferredLanguage)
+	}
+
+	if req.Currency != "" {
+		update.Currency = proto.String(req.Currency)
+	}
+
+	if req.AvatarURL != "" {
+		update.Avatar = proto.String(req.AvatarURL)
+	}
+
+	if req.FullName != "" {
+		update.FullName = proto.String(req.FullName)
+	}
+
+	if req.GenderCode != "" {
+		gc, err := domain.StringToGenderCode(req.GenderCode)
+		if err != nil {
+			gc = 0
+		}
+		update.Gender = proto.Int64(gc)
+	}
+
+	if req.Birthday != "" {
+		unix, err := utils.Rfc3339ToUnix(req.Birthday)
+		if err == nil {
+			update.Birthdate = proto.Int64(unix)
+		}
+	}
+
+	if req.Address != "" {
+		update.Address = proto.String(req.Address)
+	}
+
+	_, err := l.svcCtx.MemberRPC.UpdateUserInfo(l.ctx, update)
+	if err != nil {
+		return nil, err
+	}
+
+	// 再次取得資訊
+	info, err := l.svcCtx.MemberRPC.GetUserInfo(l.ctx, &memberProto.GetUserInfoReq{
+		Uid: domain.UID(l.ctx),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	accountInfo, err := l.svcCtx.MemberRPC.GetUserAccountInfo(l.ctx, &memberProto.GetUIDByAccountReq{
+		Account: domain.Account(l.ctx),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	alarmType := member.AlarmType(info.Data.AlarmType)
+	status := member.Status(info.Data.Status)
+	res := &types.UserInfo{
+		Platform:          member.Platform(accountInfo.Data.Platform).ToString(),
+		UID:               domain.UID(l.ctx),
+		UpdateAt:          utils.UnixToRfc3339(info.Data.UpdateTime),
+		CreateAt:          utils.UnixToRfc3339(info.Data.CreateTime),
+		AlarmCategory:     alarmType.CodeToString(),
+		UserStatus:        status.CodeToString(),
+		PreferredLanguage: info.Data.Language,
+		Currency:          info.Data.Currency,
+	}
+
+	if info.Data.AvatarUrl != nil {
+		res.AvatarURL = *info.Data.AvatarUrl
+	}
+	if info.Data.FullName != nil {
+		res.FullName = *info.Data.FullName
+	}
+	if info.Data.NickName != nil {
+		res.Nickname = *info.Data.NickName
+	}
+
+	if info.Data.GenderCode != nil {
+		gc, err := domain.GenderCodeToString(*info.Data.GenderCode)
+		if err != nil {
+			gc = "secret"
+		}
+		res.GenderCode = gc
+	}
+
+	if info.Data.Birthday != nil {
+		// RFC 3339
+		res.Birthdate = utils.UnixToRfc3339(*info.Data.Birthday)
+	}
+
+	if info.Data.Phone != nil {
+		res.PhoneNumber = *info.Data.Phone
+	}
+
+	if info.Data.Address != nil {
+		res.Address = *info.Data.Address
+	}
+
+	if info.Data.Email != nil {
+		res.Email = *info.Data.Email
+	}
+
+	return res, nil
+}
diff --git a/internal/logic/member/modify_passwd_logic.go b/internal/logic/member/modify_passwd_logic.go
new file mode 100644
index 0000000..88f1d9d
--- /dev/null
+++ b/internal/logic/member/modify_passwd_logic.go
@@ -0,0 +1,64 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"context"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+	"code.30cm.net/digimon/library-go/errs"
+	memberRPC "code.30cm.net/digimon/proto-all/pkg/member"
+	permissionProto "code.30cm.net/digimon/proto-all/pkg/permission"
+
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type ModifyPasswdLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewModifyPasswdLogic 修改密碼
+func NewModifyPasswdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ModifyPasswdLogic {
+	return &ModifyPasswdLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *ModifyPasswdLogic) ModifyPasswd(req *types.ModifyPasswdReq) (*types.RespOK, error) {
+	account := domain.Account(l.ctx)
+
+	// 檢查是否有此帳號
+	_, err := l.svcCtx.MemberRPC.GetUIDByAccount(l.ctx, &memberRPC.GetUIDByAccountReq{
+		Account: account,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	if req.NewToken != req.NewTokenCheck {
+		return nil, errs.InvalidFormat("failed to check token")
+	}
+
+	// 更新
+	_, err = l.svcCtx.MemberRPC.UpdateUserToken(l.ctx, &memberRPC.UpdateTokenReq{
+		Account:  account,
+		Token:    req.NewToken,
+		Platform: member.Digimon.ToInt64(),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	// 登出所有,請使用者中新登入 -> 射後不理,因為已經更新成功了
+	_, _ = l.svcCtx.TokenRPC.CancelToken(l.ctx, &permissionProto.CancelTokenReq{
+		Token: req.Token,
+	})
+
+	return &types.RespOK{}, nil
+}
diff --git a/internal/logic/member/pre_verify_update_password_code_logic.go b/internal/logic/member/pre_verify_update_password_code_logic.go
new file mode 100644
index 0000000..adbe592
--- /dev/null
+++ b/internal/logic/member/pre_verify_update_password_code_logic.go
@@ -0,0 +1,42 @@
+package member
+
+import (
+	"context"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+	"code.30cm.net/digimon/library-go/errs"
+	memberProto "code.30cm.net/digimon/proto-all/pkg/member"
+
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type PreVerifyUpdatePasswordCodeLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewPreVerifyUpdatePasswordCodeLogic 預先驗證驗證碼
+func NewPreVerifyUpdatePasswordCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PreVerifyUpdatePasswordCodeLogic {
+	return &PreVerifyUpdatePasswordCodeLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *PreVerifyUpdatePasswordCodeLogic) PreVerifyUpdatePasswordCode(req *types.PreVerifyForgetPasswdReq) (resp *types.RespOK, err error) {
+	// 先驗證,不刪除
+	if _, err := l.svcCtx.MemberRPC.CheckRefreshCode(l.ctx, &memberProto.VerifyRefreshCodeReq{
+		Account:    req.Identifier,
+		CodeType:   int32(member.GenerateCodeTypeForgetPassword),
+		VerifyCode: req.VerifyCode,
+	}); err != nil {
+		return nil, errs.Forbidden("failed to get verify code")
+	}
+
+	return
+}
diff --git a/internal/logic/member/refresh_access_token_logic.go b/internal/logic/member/refresh_access_token_logic.go
new file mode 100644
index 0000000..e4b789b
--- /dev/null
+++ b/internal/logic/member/refresh_access_token_logic.go
@@ -0,0 +1,61 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+	"context"
+
+	"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/token"
+	"code.30cm.net/digimon/library-go/errs"
+	"code.30cm.net/digimon/proto-all/pkg/permission"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type RefreshAccessTokenLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewRefreshAccessTokenLogic 更新 Access Token
+func NewRefreshAccessTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RefreshAccessTokenLogic {
+	return &RefreshAccessTokenLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *RefreshAccessTokenLogic) RefreshAccessToken(req *types.UpdateTokenReq) (*types.LoginTokenResp, error) {
+	data, err := l.svcCtx.TokenRPC.GetSystemClaimByAccessToken(l.ctx, &permission.GetSystemClaimReq{
+		AccessToken: req.Token,
+		IsExpired:   false,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	uid := data.Data[token.UID.String()]
+	if uid != req.UID {
+		return nil, errs.Forbidden("failed to verify token user")
+	}
+
+	t, err := l.svcCtx.TokenRPC.RefreshToken(l.ctx, &permission.RefreshTokenReq{
+		Token:    req.RefreshToken,
+		Scope:    data.Data[token.Scope.String()],
+		Expires:  0, // 指定到期的時間,不給會交由底層給(token repo)
+		DeviceId: data.Data[token.Device.String()],
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.LoginTokenResp{
+		UID:          req.UID,
+		AccessToken:  t.Token,
+		RefreshToken: t.OneTimeToken,
+		TokenType:    domain.TokenTypeBearer,
+	}, nil
+}
diff --git a/internal/logic/member/send_verify_code_logic.go b/internal/logic/member/send_verify_code_logic.go
new file mode 100644
index 0000000..fa7674b
--- /dev/null
+++ b/internal/logic/member/send_verify_code_logic.go
@@ -0,0 +1,157 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"bytes"
+	"context"
+	"fmt"
+	"html/template"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+	ntpl "code.30cm.net/digimon/app-cloudep-notification-service/pkg/domain/template"
+	"code.30cm.net/digimon/library-go/errs"
+	"code.30cm.net/digimon/library-go/errs/code"
+	memberProto "code.30cm.net/digimon/proto-all/pkg/member"
+	notificationProto "code.30cm.net/digimon/proto-all/pkg/notification"
+
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type SendVerifyCodeLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewSendVerifyCodeLogic 發送邀請 - 綁定會員
+func NewSendVerifyCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendVerifyCodeLogic {
+	return &SendVerifyCodeLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *SendVerifyCodeLogic) SendVerifyCode(req *types.VerificationCodeRequest) (resp *types.RespOK, err error) {
+	at := member.GetGetCodeNameByCode(req.CodeType)
+	// 1. 驗證並標準化帳號
+	acc, err := normalizeAccount(req.CodeType, req.Identifier)
+	if err != nil {
+		return nil, err
+	}
+
+	// 2. 統一檢查冷卻時間與平台正確性
+	if err = l.checkAccountValidity(acc, at); err != nil {
+		return nil, err
+	}
+
+	// 3. 生成驗證碼
+	vcode, err := l.svcCtx.MemberRPC.GenerateRefreshCode(l.ctx, &memberProto.GenerateRefreshCodeReq{
+		Account:  acc,
+		CodeType: int32(at),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	// 4. 取得用戶資訊
+	uidInfo, err := l.svcCtx.MemberRPC.GetUIDByAccount(l.ctx, &memberProto.GetUIDByAccountReq{Account: acc})
+	if err != nil {
+		return nil, err
+	}
+	userInfo, err := l.svcCtx.MemberRPC.GetUserInfo(l.ctx, &memberProto.GetUserInfoReq{Uid: uidInfo.GetUid()})
+	if err != nil {
+		return nil, err
+	}
+
+	// 5. 發送驗證碼
+	nickname := getEmailShowName(userInfo)
+	if err = l.sendVerificationCode(req.CodeType, acc, userInfo.Data, vcode.Data.VerifyCode, nickname); err != nil {
+		return nil, err
+	}
+
+	// 6. 設置 Redis 驗證碼鍵值
+	rk := domain.GetSendCodeRedisKey(fmt.Sprintf("%s:%d", acc, at))
+	l.setRedisKeyWithExpiry(rk, vcode.Data.VerifyCode, int(l.svcCtx.Config.Member.ForgetTimeOutInSec))
+
+	return &types.RespOK{}, nil
+}
+
+// checkAccountValidity 同時檢查發送冷卻與平台正確性
+func (l *SendVerifyCodeLogic) checkAccountValidity(acc string, gc member.GenerateCodeType) error {
+	// 檢查發送冷卻
+	rk := domain.GetSendCodeRedisKey(fmt.Sprintf("%s:%d", acc, gc))
+	if cached, err := l.svcCtx.Redis.GetCtx(l.ctx, rk); err != nil || cached != "" {
+		return errs.InvalidRange("verification code already sent, please wait for system to send again")
+	}
+
+	// 檢查平台是否正確(只允許平台帳號進行綁定)
+	accountInfo, err := l.svcCtx.MemberRPC.GetUserAccountInfo(l.ctx, &memberProto.GetUIDByAccountReq{Account: acc})
+	if err != nil {
+		return err
+	}
+	if accountInfo.Data.Platform != member.Digimon.ToInt64() {
+		return errs.InvalidResourceState("failed to send verify code since platform not correct")
+	}
+	return nil
+}
+
+// sendVerificationEmail 發送驗證郵件
+func (l *SendVerifyCodeLogic) sendVerificationEmail(recipientEmail string, info *memberProto.UserInfo, verifyCode, nickname string) error {
+	tpl, err := l.svcCtx.NotificationRPC.GetStaticTemplate(l.ctx, &notificationProto.TemplateReq{
+		Language:   info.Language,
+		TemplateId: string(ntpl.BindingEmail),
+	})
+	if err != nil {
+		return err
+	}
+
+	tmpl, err := template.New("SendVerificationEmail").Parse(tpl.GetBody())
+	if err != nil {
+		return err
+	}
+
+	emailParams := EmailTmpInfo{Username: nickname, VerifyCode: verifyCode}
+	var buf bytes.Buffer
+	if err := tmpl.Execute(&buf, emailParams); err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.NotificationRPC.SendMail(l.ctx, &notificationProto.SendMailReq{
+		To:      recipientEmail,
+		Subject: tpl.Title,
+		Body:    buf.String(),
+		From:    l.svcCtx.Config.MailSender,
+	})
+	return err
+}
+
+// sendVerificationCode 根據帳號類型發送驗證碼
+func (l *SendVerifyCodeLogic) sendVerificationCode(accountType, acc string, info *memberProto.UserInfo, verifyCode, nickname string) error {
+	switch member.GetAccountTypeByCode(accountType) {
+	case member.AccountTypePhone:
+		// TODO: 傳送簡訊
+		fmt.Printf("SMS Template: %s\n", verifyCode)
+	case member.AccountTypeMail:
+		return l.sendVerificationEmail(acc, info, verifyCode, nickname)
+	default:
+		return errs.InvalidResourceState("unsupported account type")
+	}
+	return nil
+}
+
+// setRedisKeyWithExpiry 設置 Redis 鍵與過期時間
+func (l *SendVerifyCodeLogic) setRedisKeyWithExpiry(rk, verifyCode string, expiry int) {
+	if status, err := l.svcCtx.Redis.SetnxExCtx(l.ctx, rk, verifyCode, expiry); err != nil || !status {
+		_ = errs.DatabaseErrorWithScopeL(code.CloudEPMember, domain.FailedToSetVerifyCodeErrorCode,
+			logx.WithContext(l.ctx),
+			[]logx.LogField{
+				{Key: "func", Value: "SendVerifyCodeLogic.setRedisKeyWithExpiry"},
+				{Key: "redisKey", Value: rk},
+				{Key: "error", Value: err.Error()},
+			}, "failed to set redis expire").Wrap(err)
+	}
+}
diff --git a/internal/logic/member/update_password_logic.go b/internal/logic/member/update_password_logic.go
new file mode 100644
index 0000000..2772e58
--- /dev/null
+++ b/internal/logic/member/update_password_logic.go
@@ -0,0 +1,92 @@
+package member
+
+import (
+	"biz-member-gateway/internal/domain"
+	"context"
+	"fmt"
+
+	"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
+	ers "code.30cm.net/digimon/library-go/errors"
+	"code.30cm.net/digimon/library-go/errs"
+	memberProto "code.30cm.net/digimon/proto-all/pkg/member"
+	permissionProto "code.30cm.net/digimon/proto-all/pkg/permission"
+
+	"biz-member-gateway/internal/svc"
+	"biz-member-gateway/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdatePasswordLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// NewUpdatePasswordLogic 更新密碼(要發送驗證碼才可以的流程)
+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) (*types.RespOK, error) {
+	// 驗證密碼,兩次密碼要一致
+	if req.Token != req.TokenCheck {
+		return nil, errs.InvalidFormat("password confirmation does not match")
+	}
+
+	// 驗證碼
+	_, err := l.svcCtx.MemberRPC.VerifyRefreshCode(l.ctx, &memberProto.VerifyRefreshCodeReq{
+		Account:    req.Account,
+		CodeType:   int32(member.GenerateCodeTypeForgetPassword),
+		VerifyCode: req.VerifyCode,
+	})
+
+	if err != nil {
+		// 表使沒有這驗證碼 -> 未授權
+		return nil, errs.Unauthorized("failed to get verify code")
+	}
+
+	info, err := l.svcCtx.MemberRPC.GetUserAccountInfo(l.ctx, &memberProto.GetUIDByAccountReq{Account: req.Account})
+	if err != nil {
+		return nil, err
+	}
+
+	if info.Data.Platform != member.Digimon.ToInt64() {
+		return nil, ers.InvalidRange("invalid platform")
+	}
+
+	// 更新
+	_, err = l.svcCtx.MemberRPC.UpdateUserToken(l.ctx, &memberProto.UpdateTokenReq{
+		Account:  req.Account,
+		Token:    req.Token,
+		Platform: member.Digimon.ToInt64(),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	// 刪除驗證碼
+	rk := domain.GetSendCodeRedisKey(fmt.Sprintf("%s:%s", req.Account, member.GenerateCodeTypeForgetPassword))
+	_, err = l.svcCtx.Redis.Del(rk)
+	if err != nil {
+		return nil, ers.DBError("failed to get account data")
+	}
+
+	// 登出舊 Token 如果有的話
+	ac, err := l.svcCtx.MemberRPC.GetUIDByAccount(l.ctx, &memberProto.GetUIDByAccountReq{Account: req.Account})
+	if err != nil {
+		return nil, err
+	}
+
+	_, err = l.svcCtx.TokenRPC.CancelTokens(l.ctx, &permissionProto.DoTokenByUIDReq{Uid: ac.GetUid()})
+	if err != nil {
+		return nil, err
+	}
+
+	// 返回成功響應
+	return &types.RespOK{}, nil
+}
diff --git a/internal/middleware/auth_middleware.go b/internal/middleware/auth_middleware.go
new file mode 100644
index 0000000..3690cd9
--- /dev/null
+++ b/internal/middleware/auth_middleware.go
@@ -0,0 +1,84 @@
+package middleware
+
+import (
+	"biz-member-gateway/internal/types"
+	"context"
+	"net/http"
+
+	"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/token"
+	ers "code.30cm.net/digimon/library-go/errors"
+	permissionRPC "code.30cm.net/digimon/proto-all/pkg/permission"
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+type AuthMiddlewareParam struct {
+	TokenRPC permissionRPC.TokenServiceClient
+}
+
+type AuthMiddleware struct {
+	TokenRPC permissionRPC.TokenServiceClient
+}
+
+func NewAuthMiddleware(param AuthMiddlewareParam) *AuthMiddleware {
+	return &AuthMiddleware{TokenRPC: param.TokenRPC}
+}
+
+func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		// 解析 Header
+		header := types.VerifyHeader{}
+		if err := httpx.ParseHeaders(r, &header); err != nil {
+			m.writeErrorResponse(w, r, http.StatusBadRequest, "Failed to parse headers", int64(ers.InvalidFormat().FullCode()))
+
+			return
+		}
+
+		// 驗證 Token
+		claim, err := m.TokenRPC.GetSystemClaimByAccessToken(r.Context(), &permissionRPC.GetSystemClaimReq{
+			AccessToken: header.Token,
+			IsExpired:   true,
+		})
+		if err != nil {
+			// 是否需要紀錄錯誤,是不是只要紀錄除了驗證失敗或過期之外的真錯誤
+			m.writeErrorResponse(w, r,
+				http.StatusUnauthorized, "failed to verify toke",
+				int64(100400))
+
+			return
+		}
+
+		// 驗證 Token 是否在黑名單中
+		if _, err := m.TokenRPC.ValidationToken(r.Context(), &permissionRPC.ValidationTokenReq{Token: header.Token}); err != nil {
+			m.writeErrorResponse(w, r, http.StatusForbidden,
+				"failed to get toke",
+				int64(100400))
+
+			return
+		}
+
+		// 設置 context 並傳遞給下一個處理器
+		ctx := SetContext(r, claim.Data)
+		//nolint:contextcheck
+		next(w, r.WithContext(ctx))
+	}
+}
+
+func SetContext(r *http.Request, claim map[string]string) context.Context {
+	ctx := context.WithValue(r.Context(), token.Role.String(), claim[token.Role.String()])
+	ctx = context.WithValue(ctx, token.UID.String(), claim[token.UID.String()])
+	ctx = context.WithValue(ctx, token.Account.String(), claim[token.Account.String()])
+	ctx = context.WithValue(ctx, token.Scope.String(), claim[token.Scope.String()])
+	ctx = context.WithValue(ctx, token.Device.String(), claim[token.Device.String()])
+
+	return ctx
+}
+
+// writeErrorResponse 用於處理錯誤回應
+func (m *AuthMiddleware) writeErrorResponse(w http.ResponseWriter, r *http.Request, statusCode int, message string, code int64) {
+	httpx.WriteJsonCtx(r.Context(), w, statusCode, types.BaseResponse{
+		Status: types.Status{
+			Code:    code,
+			Message: message,
+		},
+	})
+}
diff --git a/internal/svc/pyroscope.go b/internal/svc/pyroscope.go
new file mode 100644
index 0000000..f9e1d80
--- /dev/null
+++ b/internal/svc/pyroscope.go
@@ -0,0 +1,46 @@
+package svc
+
+import (
+	"biz-member-gateway/internal/config"
+	"context"
+	"fmt"
+	"os"
+
+	"github.com/grafana/pyroscope-go"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+func InitPyroScope(c config.Config) {
+	if c.PyroScope.Enable {
+		podName := os.Getenv("POD_NAME")
+
+		_, err := pyroscope.Start(pyroscope.Config{
+			ApplicationName: fmt.Sprintf("biz-%s-%s", c.Name, podName),
+			ServerAddress:   c.PyroScope.URL,
+			Logger:          logx.WithContext(context.Background()),
+			ProfileTypes: []pyroscope.ProfileType{
+				pyroscope.ProfileCPU,
+
+				pyroscope.ProfileInuseObjects,
+				pyroscope.ProfileAllocObjects,
+				pyroscope.ProfileInuseSpace,
+				pyroscope.ProfileAllocSpace,
+
+				pyroscope.ProfileGoroutines,
+
+				pyroscope.ProfileMutexCount,
+				pyroscope.ProfileMutexDuration,
+
+				pyroscope.ProfileBlockCount,
+				pyroscope.ProfileBlockDuration,
+			},
+		})
+		if err != nil {
+			logx.WithCallerSkip(1).WithFields(logx.LogField{
+				Key: "error", Value: err.Error(),
+			}).Error("failed to init pyroscope")
+
+			panic(fmt.Sprintf("Pyroscope start err: %s", err.Error()))
+		}
+	}
+}
diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go
new file mode 100644
index 0000000..6eb826d
--- /dev/null
+++ b/internal/svc/service_context.go
@@ -0,0 +1,64 @@
+package svc
+
+import (
+	"biz-member-gateway/internal/config"
+	"biz-member-gateway/internal/middleware"
+
+	"github.com/zeromicro/go-zero/core/limit"
+	"github.com/zeromicro/go-zero/core/stores/redis"
+
+	"github.com/zeromicro/go-zero/rest"
+	"github.com/zeromicro/go-zero/zrpc"
+
+	ers "code.30cm.net/digimon/library-go/errors"
+	"code.30cm.net/digimon/library-go/errors/code"
+	vi "code.30cm.net/digimon/library-go/validator"
+	memberRPC "code.30cm.net/digimon/proto-all/pkg/member"
+	notificationRPC "code.30cm.net/digimon/proto-all/pkg/notification"
+	permissionRPC "code.30cm.net/digimon/proto-all/pkg/permission"
+)
+
+type ServiceContext struct {
+	Config         config.Config
+	AuthMiddleware rest.Middleware
+
+	MemberRPC       memberRPC.AccountClient
+	TokenRPC        permissionRPC.TokenServiceClient
+	NotificationRPC notificationRPC.SenderServiceClient
+
+	// 但是底線就是 redis 其他db 絕對不行
+	Redis *redis.Redis
+
+	VerifyLimiter *limit.PeriodLimit
+	Validate      vi.Validate
+}
+
+func NewServiceContext(c config.Config) *ServiceContext {
+	ers.Scope = code.CloudEPPortalGW
+	tokenService := permissionRPC.NewTokenServiceClient(zrpc.MustNewClient(c.PermissionRPC).Conn())
+
+	InitPyroScope(c)
+
+	// 啟動Redis
+	newRedis, err := redis.NewRedis(c.RedisCfg)
+	if err != nil {
+		panic(err)
+	}
+
+	return &ServiceContext{
+		Config: c,
+		AuthMiddleware: middleware.NewAuthMiddleware(middleware.AuthMiddlewareParam{
+			TokenRPC: tokenService,
+		}).Handle,
+		MemberRPC:       memberRPC.NewAccountClient(zrpc.MustNewClient(c.MemberRPC).Conn()),
+		TokenRPC:        tokenService,
+		NotificationRPC: notificationRPC.NewSenderServiceClient(zrpc.MustNewClient(c.NotificationRPC).Conn()),
+		Redis:           newRedis,
+		Validate: vi.MustValidator(
+			WithDecimalGt(),
+			WithDecimalGte(),
+			WithHTTPURL(),
+			WithRfc3339Format(),
+		),
+	}
+}
diff --git a/internal/svc/validate.go b/internal/svc/validate.go
new file mode 100755
index 0000000..cb8ff40
--- /dev/null
+++ b/internal/svc/validate.go
@@ -0,0 +1,83 @@
+package svc
+
+import (
+	"regexp"
+	"time"
+
+	vi "code.30cm.net/digimon/library-go/validator"
+	"github.com/go-playground/validator/v10"
+	"github.com/shopspring/decimal"
+)
+
+// WithDecimalGt 是否大於等於
+func WithDecimalGt() vi.Option {
+	return vi.Option{
+		ValidatorName: "decimalGt",
+		ValidatorFunc: func(fl validator.FieldLevel) bool {
+			if val, ok := fl.Field().Interface().(string); ok {
+				value, err := decimal.NewFromString(val)
+				if err != nil {
+					return false
+				}
+
+				conditionValue, err := decimal.NewFromString(fl.Param())
+				if err != nil {
+					return false
+				}
+
+				return value.GreaterThan(conditionValue)
+			}
+
+			return true
+		},
+	}
+}
+
+// WithDecimalGte 是否大於等於
+func WithDecimalGte() vi.Option {
+	return vi.Option{
+		ValidatorName: "decimalGte",
+		ValidatorFunc: func(fl validator.FieldLevel) bool {
+			if val, ok := fl.Field().Interface().(string); ok {
+				value, err := decimal.NewFromString(val)
+				if err != nil {
+					return false
+				}
+
+				conditionValue, err := decimal.NewFromString(fl.Param())
+				if err != nil {
+					return false
+				}
+
+				return value.GreaterThanOrEqual(conditionValue)
+			}
+
+			return true
+		},
+	}
+}
+
+func WithHTTPURL() vi.Option {
+	return vi.Option{
+		ValidatorName: "httpurl",
+		ValidatorFunc: func(fl validator.FieldLevel) bool {
+			value := fl.Field().String()
+			// 使用正則檢查是否以 http:// 或 https:// 開頭
+			re := regexp.MustCompile(`^https?://`)
+
+			return re.MatchString(value)
+		},
+	}
+}
+
+func WithRfc3339Format() vi.Option {
+	return vi.Option{
+		ValidatorName: "rfc3339",
+		ValidatorFunc: func(fl validator.FieldLevel) bool {
+			// 使用正則檢查是否以 http:// 或 https:// 開頭
+			_, err := time.Parse(time.RFC3339, fl.Field().String())
+
+			return err == nil
+		},
+	}
+}
diff --git a/internal/types/types.go b/internal/types/types.go
new file mode 100644
index 0000000..8f1b7f6
--- /dev/null
+++ b/internal/types/types.go
@@ -0,0 +1,127 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.8.1
+
+package types
+
+type BaseResponse struct {
+	Status Status      `json:"status"` // 狀態
+	Data   interface{} `json:"data"`   // 資料
+}
+
+type BindingUserInfoReq struct {
+	VerifyHeader
+	PreferredLanguage string `json:"preferred_language,optional" validate:"oneof=zh-tw en-us"` // 使用語言
+	Currency          string `json:"currency,optional" validate:"oneof=TWD USD"`
+	AvatarURL         string `json:"avatar_url,optional"` // 頭像 URL(可選)
+	Nickname          string `json:"nickname,optional"`
+	FullName          string `json:"full_name,optional"`                              // 用戶全名
+	GenderCode        string `json:"gender_code" validate:"oneof=secret male female"` // 性別代碼
+	Birthday          string `json:"birthday,optional" validate:"rfc3339"`            // 生日 (格式: unix)
+	Address           string `json:"address,optional"`                                // 地址
+}
+
+type CheckoutVerifyReq struct {
+	VerifyHeader
+	Account    string `json:"account" validate:"required"`            // 帳號名稱
+	CodeType   string `json:"code_type" validate:"oneof=email phone"` // 驗證碼類型 1 信箱 2 手機
+	VerifyCode string `json:"verify_code" validate:"required,len=6"`  // 驗證碼,長度為6
+	UID        string `json:"uid" validate:"required"`
+}
+
+type CreateAccountRequest struct {
+	Account     string `json:"account" validate:"required"`                        // 帳號名稱(line code 輸入在這邊)
+	Token       string `json:"token" validate:"required"`                          // 密碼或平台token,密碼請 sha256 轉碼,如果三方token 請隨便給一個 sha256 字串
+	TokenCheck  string `json:"token_check" validate:"required"`                    // 密碼或平台token,token 請保持原樣,填在這邊,不用管 token
+	Platform    string `json:"platform" validate:"oneof=platform google line"`     // 平台名稱 (platform) 平台、google、line
+	AccountType string `json:"account_type" validate:"oneof=phone email platform"` // 帳號類型 phone(手機)、email(信箱)、platform(自定義帳號) -> (如果為第三方都寫 platform)
+	MemberLoginHeader
+}
+
+type ForgetPasswordCodeReq struct {
+	Account     string `json:"account" validate:"required"`               // 帳號名稱
+	AccountType string `json:"account_type" validate:"oneof=phone email"` // 帳號類型 (phone) 手機 (email) 信箱
+}
+
+type LoginReq struct {
+	Account     string `json:"account" validate:"required"`                        // 帳號名稱
+	Token       string `json:"token"`                                              // 密碼或平台token,密碼請 sha256 轉碼
+	Platform    string `json:"platform" validate:"oneof=platform google line"`     // 平台名稱 platform, google
+	AccountType string `json:"account_type" validate:"oneof=phone email platform"` // 帳號類型 1 手機 2 信箱 3 自定義帳號
+	MemberLoginHeader
+}
+
+type LoginTokenResp struct {
+	UID          string `json:"uid"`           //  Account
+	AccessToken  string `json:"access_token"`  // 訪問令牌 預設 5 分鐘過期
+	RefreshToken string `json:"refresh_token"` // 刷新令牌 (預設一天過期,只能用一次),當呼叫更新token api 時,會自動把舊的失效,變成新的 refresh_token ,前端要記得過其實協助刷新,刷新不過表示全失效了(重新登入)
+	TokenType    string `json:"token_type"`    // Bearer
+}
+
+type MemberLoginHeader struct {
+	DeviceID  string `header:"device_id"`
+	IpAddress string `header:"ip_address"`
+	Brewser   string `header:"brewser"`
+}
+
+type ModifyPasswdReq struct {
+	VerifyHeader
+	NewToken      string `json:"token" validate:"required,len=64"`       // 密碼或平台token,密碼請 sha256 轉碼
+	NewTokenCheck string `json:"token_check" validate:"required,len=64"` // 密碼或平台token,密碼請 sha256 轉碼
+}
+
+type PreVerifyForgetPasswdReq struct {
+	Identifier string `json:"identifier" validate:"required"`        // 聯繫方式,可以是 email 或 phone
+	VerifyCode string `json:"verify_code" validate:"required,len=6"` // 驗證碼,長度為6
+}
+
+type RespOK struct {
+}
+
+type Status struct {
+	Code    int64       `json:"code"`            // 狀態碼
+	Message string      `json:"message"`         // 訊息
+	Data    interface{} `json:"data,omitempty"`  // 可選的數據,當有返回時才出現
+	Error   interface{} `json:"error,omitempty"` // 可選的錯誤信息
+}
+
+type UpdatePasswordReq struct {
+	Account    string `json:"account" validate:"required"`            // 帳號名稱
+	VerifyCode string `json:"verify_code" validate:"required,len=6"`  // 驗證碼,長度為6
+	Token      string `json:"token" validate:"required,len=64"`       // 密碼或平台token,密碼請 sha256 轉碼
+	TokenCheck string `json:"token_check" validate:"required,len=64"` // 密碼或平台token,密碼請 sha256 轉碼
+}
+
+type UpdateTokenReq struct {
+	UID          string `json:"uid" validate:"required"`           // 誰要更新
+	Token        string `json:"token" validate:"required"`         // access token -> 已過期要被更新的
+	RefreshToken string `json:"refresh_token" validate:"required"` // refresh token ->  重點,要驗證他的
+}
+
+type UserInfo struct {
+	Platform          string `json:"platform"`           // 用戶平台 platform, google, line
+	UID               string `json:"uid"`                // 用戶 UID
+	AvatarURL         string `json:"avatar_url"`         // 頭像 URL(可選)
+	FullName          string `json:"full_name"`          // 用戶全名
+	Nickname          string `json:"nickname"`           // 暱稱(可選)
+	GenderCode        string `json:"gender_code"`        // 性別代碼 mail, femail ,sec
+	Birthdate         string `json:"birthdate"`          // 生日 (格式: 19930417)
+	PhoneNumber       string `json:"phone_number"`       // 電話
+	Address           string `json:"address"`            // 地址
+	Email             string `json:"email"`              // 驗證後的信箱
+	AlarmCategory     string `json:"alarm_category"`     // 告警狀態
+	UserStatus        string `json:"user_status"`        // 用戶狀態
+	PreferredLanguage string `json:"preferred_language"` // 使用語言
+	Currency          string `json:"currency"`           // 使用幣種
+	UpdateAt          string `json:"update_at"`
+	CreateAt          string `json:"create_at"`
+}
+
+type VerificationCodeRequest struct {
+	VerifyHeader
+	Identifier string `json:"identifier" validate:"required"`                         // 聯繫方式,可以是 email 或 phone
+	CodeType   string `json:"code_type" validate:"oneof=email phone forget_password"` // 驗證碼類型
+}
+
+type VerifyHeader struct {
+	Token string `header:"token" validate:"required"`
+}
diff --git a/internal/utils/time.go b/internal/utils/time.go
new file mode 100644
index 0000000..2e6f387
--- /dev/null
+++ b/internal/utils/time.go
@@ -0,0 +1,17 @@
+package utils
+
+import "time"
+
+func UnixToRfc3339(t int64) string {
+	res := time.Unix(0, t).UTC()
+
+	return res.Format(time.RFC3339)
+}
+
+func Rfc3339ToUnix(rfc string) (int64, error) {
+	t, err := time.Parse(time.RFC3339, rfc)
+	if err != nil {
+		return 0, err
+	}
+	return t.UnixNano(), nil
+}
diff --git a/internal/utils/utils.go b/internal/utils/utils.go
new file mode 100644
index 0000000..22c890c
--- /dev/null
+++ b/internal/utils/utils.go
@@ -0,0 +1,3 @@
+package utils
+
+func ToPointer[T any](v T) *T { return &v }