commit 513a29003106f6e7f288e98b386bc3b30f95422d Author: 王性驊 Date: Wed Oct 1 00:53:31 2025 +0800 feat: add login page diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/backend.iml b/.idea/backend.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/backend.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 0000000..dcca0e3 --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..e066844 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..d843f34 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..47781f2 --- /dev/null +++ b/Makefile @@ -0,0 +1,76 @@ +GO_CTL_NAME=goctl +GO_ZERO_STYLE=go_zero +GO ?= go +GOFMT ?= gofmt "-s" +GOFILES := $(shell find . -name "*.go") +LDFLAGS := -s -w +VERSION="v1.0.0" +DOCKER_REPO="refactor-service" + +.PHONY: test +test: # 進行測試 + go test -v --cover ./... + + +.PHONY: gen-api +gen-api: # 產生 api + goctl api go -api ./generate/api/gateway.api -dir . -style go_zero + +.PHONY: gen-doc +gen-doc: # 生成 Swagger 文檔 +# go-doc openapi --api ./generate/api/gateway.api --filename gateway.json --host dev-api.truheart.com.tw --basepath /api/v1 + go-doc -a generate/api/gateway.api -d ./ -f gateway -s openapi3.0 + + +.PHONY: fmt +fmt: # 格式優化 + $(GOFMT) -w $(GOFILES) + goimports -w ./ + golangci-lint run + +.PHONY: build +build: # 編譯專案 + go build -ldflags "$(LDFLAGS)" -o bin/gateway cmd/gateway/main.go + +.PHONY: run +run: # 運行專案 + go run cmd/gateway/main.go + +.PHONY: clean +clean: # 清理編譯文件 + rm -rf bin/ + +.PHONY: docker-build +docker-build: # 構建 Docker 映像 + docker build -t $(DOCKER_REPO):$(VERSION) . + +.PHONY: docker-run +docker-run: # 運行 Docker 容器 + docker run -p 8888:8888 $(DOCKER_REPO):$(VERSION) + +.PHONY: install +install: # 安裝依賴 + go mod tidy + go mod download + +.PHONY: lint +lint: # 代碼檢查 + golangci-lint run + +.PHONY: help +help: # 顯示幫助信息 + @echo "Available commands:" + @echo " test - 運行測試" + @echo " gen-api - 產生 api" + @echo " gen-swagger - 生成 JSON 格式 Swagger 文檔" + @echo " gen-swagger-yaml - 生成 YAML 格式 Swagger 文檔" + @echo " fmt - 格式化代碼" + @echo " build - 編譯專案" + @echo " run - 運行專案" + @echo " clean - 清理編譯文件" + @echo " docker-build - 構建 Docker 映像" + @echo " docker-run - 運行 Docker 容器" + @echo " install - 安裝依賴" + @echo " lint - 代碼檢查" + @echo " help - 顯示幫助信息" + diff --git a/cursor.md b/cursor.md new file mode 100644 index 0000000..42a031b --- /dev/null +++ b/cursor.md @@ -0,0 +1,59 @@ +# 🏛️ Cursor Rules: Go Kernel Mode 1.24.2 + +**最高指導原則:** 以 Linus Torvalds 的實用主義為核心,融合 Go 語言三巨頭 (Pike, Thompson, Griesemer) 的簡潔與併發哲學。 + +**核心目標:** 構建極致高效 (Performance-First)、極度簡潔 (Simplicity)、且完全受控 (Control) 的 Go 後端服務。 + +--- + +## 1. 最小化原則 (Minimization Principle) + +**核心:保持規模的簡潔、專注、易於理解。** + +| 規則 ID | 規則內容 (Go 實踐) | 哲學依據 | +| :--- | :--- | :--- | +| **M1** | **Stdlib-First:** **強制優先** 使用 Go 標準庫 (`net/http`, `context`, `sync`, `io`, `database/sql`)。任何外部依賴的引入必須有不可替代的理由。 | **Pike/Thompson:** 簡潔與正交性。 | +| **M2** | **No-Fat-Frameworks:** **禁止** 使用大型、功能過載的 Web 框架。僅允許使用 Go 標準庫或極輕量的路由/工具庫 (如 `go-chi/chi`),以維持對 **性能瓶頸的完全控制**。 | **Linus Torvalds:** 實用與控制。 | +| **M3** | **Small Interfaces:** 介面 (Interfaces) 必須 **小且專一** (`er` 慣例,如 `io.Reader`)。介面應定義在使用者 (Consumer) 端,以促進模組間的低耦合。 | **Robert Griesemer:** 語言設計的嚴謹。 | +| **M4** | **Explicit Errors:** 錯誤處理必須使用 **Go 慣用的多回傳值 (`value, error`)** 模式。**嚴禁** 使用 `panic` 或 `recover` 來控制正常的業務流程錯誤。 | **Go 慣例:** 錯誤是流程的一部分。 | + +--- + +## 2. 結構化原則 (Structural Principle) + +**核心:使用分層架構管理職責邊界,促進可測試性和可維護性。** + +| 規則 ID | 規則內容 (Go 實踐) | 哲學依據 | +| :--- | :--- | :--- | +| **S1** | **Internal-First Layout:** 核心業務邏輯必須放置在 **`internal/`** 目錄中,不允許被外部專案直接導入。執行入口點必須在 **`cmd/{servicename}`**。 | **Griesemer/Go 慣例:** 嚴謹的專案封裝。 | +| **S2** | **Clean Architecture Layers:** 服務應至少分為 **`transport`** (I/O, 如 `net/http` Handler)、**`service`** (核心業務邏輯) 和 **`repository`** (數據存取)。層次間透過 **介面** 隔離。 | **Pike/Thompson:** 關注點分離 (Separation of Concerns)。 | +| **S3** | **Concurrency Management:** 併發操作 (如背景任務、Worker Pool) 必須明確使用 **Goroutine 和 Channel** 封裝。**Goroutine 的生命週期** 必須使用 **`context.Context`** 進行取消與超時控制。 | **Rob Pike:** Go 併發模型的正確實踐。 | +| **S4** | **Config Isolation:** 設定檔 (`.env`, 環境變數) 只能在 **`cmd`** 或專門的 **`config`** 套件中讀取。核心業務邏輯 (Service Layer) 絕不允許直接存取環境變數。 | **Linus Torvalds:** 清晰的邊界控制。 | + +--- + +## 3. 精準引用原則 (Precise Reference Principle) + +**核心:所有依賴必須顯式、高效、可追溯。** + +| 規則 ID | 規則內容 (Go 實踐) | 哲學依據 | +| :--- | :--- | :--- | +| **P1** | **Raw SQL Control:** 數據層存取必須使用 **原生 SQL** 語句,並搭配 **`sqlx`** 或 **`sqlc`** 輔助。**嚴禁** 使用大型、會自動生成複雜查詢的 ORM,以確保對效能的完全控制。 | **Linus Torvalds:** 控制數據流與效率。 | +| **P2** | **Dependency Injection (DI):** 所有依賴(例如 `service` 依賴 `repository`)必須通過 **建構函式** (例如 `NewUserService(repo Repository)`) 顯式注入,避免使用全局變數。 | **Griesemer/Go 慣例:** 顯式的依賴關係。 | +| **P3** | **I/O Context Passing:** 任何涉及 I/O (網路、DB) 的函式,其第一個參數必須是 **`context.Context`**,以便傳播截止時間和取消訊號。 | **Go 慣例:** 資源管理的標準。 | +| **P4** | **Structured Logging:** 使用 **`log/slog`** 進行結構化日誌記錄。日誌中必須包含 Request ID 或 Trace ID,以實現 **可觀察性 (Observability)**。 | **Linus/實用主義:** 快速診斷問題的能力。 | + +--- + +## 4. 一致性原則 (Consistency Principle) + +**核心:保持專案的程式碼風格、命名方式、結構的統一,減少團隊摩擦。** + +| 規則 ID | 規則內容 (Go 實踐) | 哲學依據 | +| :--- | :--- | :--- | +| **C1** | **Mandatory Formatting:** 所有提交的程式碼必須通過 **`gofmt`** 或 **`goimports`** 格式化。**禁止** 違反官方程式碼風格的程式碼。 | **Robert Griesemer:** 語言設計的嚴謹性與工程規範。 | +| **C2** | **Go Naming Conventions:** 變數和函式應**簡短**。未導出的私有成員使用**小寫開頭**。簡寫必須保持一致(如 HTTP 寫成 `http`,而非 `Http`)。 | **Rob Pike:** 簡潔與慣例。 | +| **C3** | **Testing In-Package:** 單元測試程式碼 (e.g., `_test.go`) 應與被測試程式碼保持在 **同一套件**,以實現對私有函式和變數的全面測試。 | **Linus/Go 慣例:** 穩定性與可靠性。 | +| **C4** | **Go Modules & Version:** 專案必須使用 **Go Modules** 進行依賴管理,並且針對 **Go 1.25** 版本進行編寫和測試。| **Linus/Griesemer:** 版本控制的嚴格性與相容性。 | + +--- diff --git a/etc/gateway.yaml b/etc/gateway.yaml new file mode 100644 index 0000000..70d230c --- /dev/null +++ b/etc/gateway.yaml @@ -0,0 +1,3 @@ +Name: gateway +Host: 0.0.0.0 +Port: 8888 diff --git a/gateway.go b/gateway.go new file mode 100644 index 0000000..6f9c854 --- /dev/null +++ b/gateway.go @@ -0,0 +1,31 @@ +package main + +import ( + "flag" + "fmt" + + "backend/internal/config" + "backend/internal/handler" + "backend/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) + + fmt.Printf("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..cd17ef3 --- /dev/null +++ b/gateway.json @@ -0,0 +1,2931 @@ +{ + "info": { + "contact": { + "email": "igs170911@gmail.com", + "name": "Daniel Wang" + }, + "description": "This is Digimon Platform ", + "title": "Digimon Platform API Gateway", + "version": "v1" + }, + "openapi": "3.0.3", + "paths": { + "/api/v1/account/bind": { + "post": { + "description": "將帳號綁定到指定的用戶 UID", + "operationId": "accountBindAccount", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "login_id": { + "type": "string" + }, + "type": { + "type": "integer" + }, + "uid": { + "type": "string" + } + }, + "required": [ + "uid", + "login_id", + "type" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "login_id": { + "type": "string" + }, + "type": { + "type": "integer" + }, + "uid": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 綁定成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40004: ErrorResp - 參數驗證失敗\n40005: ErrorResp - UID 格式錯誤\n40001: ErrorResp - 帳號格式錯誤" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 帳號不存在" + }, + "409": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 帳號已綁定" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "綁定帳號" + } + }, + "/api/v1/account/bind-email": { + "post": { + "description": "綁定並驗證用戶的 Email 地址", + "operationId": "accountBindVerifyEmail", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "email": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "required": [ + "uid", + "email" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 綁定成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40005: ErrorResp - UID 格式錯誤\n40006: ErrorResp - 郵箱格式錯誤" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 用戶不存在" + }, + "409": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 郵箱已綁定" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 郵箱未驗證" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "綁定驗證 Email" + } + }, + "/api/v1/account/bind-info": { + "post": { + "description": "初次綁定用戶的詳細資料", + "operationId": "accountBindUserInfo", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "address": { + "type": "string" + }, + "alarm_type": { + "type": "integer" + }, + "avatar": { + "type": "string" + }, + "birthdate": { + "type": "integer" + }, + "currency": { + "type": "string" + }, + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "gender": { + "type": "integer" + }, + "language": { + "type": "string" + }, + "nick_name": { + "type": "string" + }, + "phone_number": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "uid": { + "type": "string" + } + }, + "required": [ + "uid", + "alarm_type", + "status", + "language", + "currency", + "avatar", + "nick_name", + "full_name", + "gender", + "birthdate", + "phone_number", + "email", + "address" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 綁定成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40004: ErrorResp - 參數驗證失敗\n40005: ErrorResp - UID 格式錯誤\n40006: ErrorResp - 郵箱格式錯誤\n40007: ErrorResp - 電話格式錯誤" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 用戶不存在" + }, + "409": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 用戶資料已存在" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 用戶資料不完整" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "綁定用戶資料" + } + }, + "/api/v1/account/bind-phone": { + "post": { + "description": "綁定並驗證用戶的電話號碼", + "operationId": "accountBindVerifyPhone", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "phone": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "required": [ + "uid", + "phone" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 綁定成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40005: ErrorResp - UID 格式錯誤\n40007: ErrorResp - 電話格式錯誤" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 用戶不存在" + }, + "409": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 電話已綁定" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 電話未驗證" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "綁定驗證 Phone" + } + }, + "/api/v1/account/check-code": { + "post": { + "description": "檢查驗證碼是否正確,但不刪除驗證碼", + "operationId": "accountCheckRefreshCode", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "account": { + "type": "string" + }, + "code_type": { + "type": "integer" + }, + "verify_code": { + "type": "string" + } + }, + "required": [ + "account", + "code_type", + "verify_code" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 檢查成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40010: ErrorResp - 驗證碼類型無效\n40011: ErrorResp - 驗證碼格式錯誤\n40001: ErrorResp - 帳號格式錯誤\n40004: ErrorResp - 參數驗證失敗" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 驗證碼不存在" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 驗證碼無效或已過期" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "檢查驗證碼" + } + }, + "/api/v1/account/create": { + "post": { + "description": "創建新的用戶帳號,支援多平台登入", + "operationId": "accountCreateUserAccount", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "login_id": { + "type": "string" + }, + "platform": { + "type": "integer" + }, + "token": { + "type": "string" + } + }, + "required": [ + "login_id", + "platform", + "token" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 創建成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40001: ErrorResp - 帳號格式錯誤\n40002: ErrorResp - 密碼強度不足\n40003: ErrorResp - 平台類型無效" + }, + "409": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 帳號已存在" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 請求格式正確但語義錯誤" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "創建用戶帳號" + } + }, + "/api/v1/account/generate-code": { + "post": { + "description": "為指定帳號生成驗證碼,用於忘記密碼等功能", + "operationId": "accountGenerateRefreshCode", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "account": { + "type": "string" + }, + "code_type": { + "type": "integer" + } + }, + "required": [ + "account", + "code_type" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "properties": { + "verify_code": { + "type": "string" + } + }, + "required": [ + "verify_code" + ], + "type": "object" + } + }, + "type": "object" + } + } + }, + "description": "// 生成成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40001: ErrorResp - 帳號格式錯誤\n40004: ErrorResp - 參數驗證失敗\n40010: ErrorResp - 驗證碼類型無效" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 帳號不存在" + }, + "429": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 請求過於頻繁" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "生成驗證碼" + } + }, + "/api/v1/account/info": { + "post": { + "description": "根據帳號獲取用戶的帳號資訊", + "operationId": "accountGetUserAccountInfo", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "account": { + "type": "string" + } + }, + "required": [ + "account" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "properties": { + "login_id": { + "type": "string" + }, + "platform": { + "type": "integer" + }, + "token": { + "type": "string" + } + }, + "required": [ + "login_id", + "platform", + "token" + ], + "type": "object" + } + }, + "type": "object" + } + } + }, + "description": "// 獲取成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40001: ErrorResp - 帳號格式錯誤\n40004: ErrorResp - 參數驗證失敗" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 帳號不存在" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "獲取帳號資訊" + } + }, + "/api/v1/account/line/profile": { + "post": { + "description": "使用 LINE Access Token 獲取用戶資料", + "operationId": "accountLineGetProfileByAccessToken", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "token": { + "type": "string" + } + }, + "required": [ + "token" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "display_name": { + "type": "string" + }, + "picture_url": { + "type": "string" + }, + "status_message": { + "type": "string" + }, + "user_id": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 獲取成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40004: ErrorResp - 參數驗證失敗\n40012: ErrorResp - Token 格式錯誤" + }, + "401": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// Token 無效或過期" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// LINE 用戶資料獲取失敗" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "LINE 獲取用戶資料" + } + }, + "/api/v1/account/line/token": { + "post": { + "description": "使用 LINE 授權碼獲取 Access Token", + "operationId": "accountLineCodeToAccessToken", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "string" + } + }, + "required": [ + "code" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "token": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 獲取成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40004: ErrorResp - 參數驗證失敗\n40013: ErrorResp - 授權碼格式錯誤" + }, + "401": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 授權碼無效或過期" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// LINE 認證失敗" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "LINE 獲取 Access Token" + } + }, + "/api/v1/account/list": { + "post": { + "description": "分頁獲取用戶列表,支援篩選條件", + "operationId": "accountListMember", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "alarm_type": { + "type": "integer" + }, + "create_end_time": { + "type": "integer" + }, + "create_start_time": { + "type": "integer" + }, + "page_index": { + "type": "integer" + }, + "page_size": { + "type": "integer" + }, + "status": { + "type": "integer" + } + }, + "required": [ + "alarm_type", + "status", + "create_start_time", + "create_end_time", + "page_size", + "page_index" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "items": { + "properties": { + "address": { + "type": "string" + }, + "alarm_type": { + "type": "integer" + }, + "avatar_url": { + "type": "string" + }, + "birthday": { + "type": "integer" + }, + "create_time": { + "type": "integer" + }, + "currency": { + "type": "string" + }, + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "gender_code": { + "type": "integer" + }, + "language": { + "type": "string" + }, + "nick_name": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "uid": { + "type": "string" + }, + "update_time": { + "type": "integer" + } + }, + "required": [ + "uid", + "avatar_url", + "full_name", + "nick_name", + "gender_code", + "birthday", + "phone", + "email", + "address", + "alarm_type", + "status", + "language", + "currency", + "create_time", + "update_time" + ], + "type": "object" + }, + "type": "array" + }, + "pager": { + "properties": { + "index": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "total": { + "type": "integer" + } + }, + "required": [ + "total", + "size", + "index" + ], + "type": "object" + } + }, + "type": "object" + } + } + }, + "description": "// 獲取成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40004: ErrorResp - 參數驗證失敗\n40009: ErrorResp - 分頁參數無效" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "獲取用戶列表" + } + }, + "/api/v1/account/uid": { + "post": { + "description": "根據帳號獲取對應的用戶 UID", + "operationId": "accountGetUIDByAccount", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "account": { + "type": "string" + } + }, + "required": [ + "account" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "account": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 獲取成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40004: ErrorResp - 參數驗證失敗\n40001: ErrorResp - 帳號格式錯誤" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 帳號不存在" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "獲取用戶 UID" + } + }, + "/api/v1/account/update-info": { + "post": { + "description": "更新用戶的個人資料資訊", + "operationId": "accountUpdateUserInfo", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "address": { + "type": "string" + }, + "alarm_type": { + "type": "integer" + }, + "avatar": { + "type": "string" + }, + "birthdate": { + "type": "integer" + }, + "currency": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "gender": { + "type": "integer" + }, + "language": { + "type": "string" + }, + "nick_name": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "uid": { + "type": "string" + } + }, + "required": [ + "uid", + "language", + "currency", + "nick_name", + "avatar", + "alarm_type", + "status", + "full_name", + "gender", + "birthdate", + "address" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 更新成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40006: ErrorResp - 郵箱格式錯誤\n40007: ErrorResp - 電話格式錯誤\n40004: ErrorResp - 參數驗證失敗\n40005: ErrorResp - UID 格式錯誤" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 用戶不存在" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 用戶資料無效" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "更新用戶資料" + } + }, + "/api/v1/account/update-status": { + "post": { + "description": "更新用戶的帳號狀態", + "operationId": "accountUpdateStatus", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "type": "integer" + }, + "uid": { + "type": "string" + } + }, + "required": [ + "uid", + "status" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 更新成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40004: ErrorResp - 參數驗證失敗\n40005: ErrorResp - UID 格式錯誤\n40008: ErrorResp - 狀態值無效" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 用戶不存在" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 狀態更新無效" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "更新用戶狀態" + } + }, + "/api/v1/account/update-token": { + "post": { + "description": "更新指定帳號的密碼", + "operationId": "accountUpdateUserToken", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "account": { + "type": "string" + }, + "platform": { + "type": "integer" + }, + "token": { + "type": "string" + } + }, + "required": [ + "account", + "token", + "platform" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 更新成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40003: ErrorResp - 平台類型無效\n40001: ErrorResp - 帳號格式錯誤\n40002: ErrorResp - 密碼強度不足" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 帳號不存在" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "更新用戶密碼" + } + }, + "/api/v1/account/user-info": { + "post": { + "description": "根據 UID 或暱稱獲取用戶詳細資訊", + "operationId": "accountGetUserInfo", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "nick_name": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "required": [ + "uid", + "nick_name" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "properties": { + "address": { + "type": "string" + }, + "alarm_type": { + "type": "integer" + }, + "avatar_url": { + "type": "string" + }, + "birthday": { + "type": "integer" + }, + "create_time": { + "type": "integer" + }, + "currency": { + "type": "string" + }, + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "gender_code": { + "type": "integer" + }, + "language": { + "type": "string" + }, + "nick_name": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "uid": { + "type": "string" + }, + "update_time": { + "type": "integer" + } + }, + "required": [ + "uid", + "avatar_url", + "full_name", + "nick_name", + "gender_code", + "birthday", + "phone", + "email", + "address", + "alarm_type", + "status", + "language", + "currency", + "create_time", + "update_time" + ], + "type": "object" + } + }, + "type": "object" + } + } + }, + "description": "// 獲取成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40004: ErrorResp - 參數驗證失敗\n40005: ErrorResp - UID 格式錯誤" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 用戶不存在" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "獲取用戶資訊" + } + }, + "/api/v1/account/verify-code": { + "post": { + "description": "驗證並使用驗證碼,驗證後會刪除驗證碼", + "operationId": "accountVerifyRefreshCode", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "account": { + "type": "string" + }, + "code_type": { + "type": "integer" + }, + "verify_code": { + "type": "string" + } + }, + "required": [ + "account", + "code_type", + "verify_code" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 驗證成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40001: ErrorResp - 帳號格式錯誤\n40004: ErrorResp - 參數驗證失敗\n40010: ErrorResp - 驗證碼類型無效\n40011: ErrorResp - 驗證碼格式錯誤" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 驗證碼不存在" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 驗證碼無效或已過期" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "驗證驗證碼" + } + }, + "/api/v1/account/verify-google": { + "post": { + "description": "驗證 Google OAuth 登入是否有效", + "operationId": "accountVerifyGoogleAuthResult", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "account": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "required": [ + "token", + "account" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "aud": { + "type": "string" + }, + "email": { + "type": "string" + }, + "email_verified": { + "type": "string" + }, + "exp": { + "type": "string" + }, + "iat": { + "type": "string" + }, + "iss": { + "type": "string" + }, + "name": { + "type": "string" + }, + "picture": { + "type": "string" + }, + "status": { + "type": "boolean" + }, + "sub": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 驗證成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40012: ErrorResp - Token 格式錯誤\n40004: ErrorResp - 參數驗證失敗" + }, + "401": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// Token 無效或過期" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// Google 認證失敗" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "驗證 Google 認證" + } + }, + "/api/v1/account/verify-platform": { + "post": { + "description": "驗證平台登入認證是否有效", + "operationId": "accountVerifyPlatformAuthResult", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "account": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "required": [ + "token", + "account" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "type": "boolean" + } + }, + "type": "object" + } + } + }, + "description": "// 驗證成功" + }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ErrorResp" + }, + { + "$ref": "#/components/schemas/ErrorResp" + } + ] + } + } + }, + "description": "客戶端錯誤\n\nPossible errors:\n40004: ErrorResp - 參數驗證失敗\n40012: ErrorResp - Token 格式錯誤" + }, + "401": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// Token 無效或過期" + }, + "422": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 平台認證失敗" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "integer" + }, + "details": { + "type": "string" + }, + "error": { + "description": "可選的錯誤信息" + }, + "msg": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "// 服務器錯誤" + } + }, + "summary": "驗證平台認證" + } + }, + "/api/v1/health": { + "get": { + "description": "檢查系統服務狀態,用於監控和負載均衡器健康檢查。返回系統運行狀態信息。", + "operationId": "pingPing", + "responses": { + "200": { + "content": { + "application/json": { + "schema": {} + } + }, + "description": "" + } + }, + "summary": "系統健康檢查" + } + } + }, + "servers": [ + { + "url": "http://127.0.0.1:8888" + }, + { + "url": "https://127.0.0.1:8888" + } + ], + "x-date": "2025-10-01 00:43:58", + "x-description": "This is a go-doc generated swagger file.", + "x-generator": "go-doc", + "x-github": "https://github.com/danielchan-25/go-doc", + "x-source": "go-zero API specification" +} \ No newline at end of file diff --git a/generate/.DS_Store b/generate/.DS_Store new file mode 100644 index 0000000..9e74ae2 Binary files /dev/null and b/generate/.DS_Store differ diff --git a/generate/api/common.api b/generate/api/common.api new file mode 100755 index 0000000..808dba5 --- /dev/null +++ b/generate/api/common.api @@ -0,0 +1,33 @@ +syntax = "v1" + +// ================ 通用響應 ================ +type ( + // 成功響應 + OKResp { + Code int `json:"code"` + Msg string `json:"msg"` + Data interface{} `json:"data,omitempty"` + } + + // 分頁響應 + PagerResp { + Total int64 `json:"total"` + Size int64 `json:"size"` + Index int64 `json:"index"` + } + + // 錯誤響應 + ErrorResp { + Code int `json:"code"` + Msg string `json:"msg"` + Details string `json:"details,omitempty"` + Error interface{} `json:"error,omitempty"` // 可選的錯誤信息 + } + + BaseReq {} + + VerifyHeader { + Token string `header:"token" validate:"required"` + } +) + diff --git a/generate/api/gateway.api b/generate/api/gateway.api new file mode 100755 index 0000000..051d28d --- /dev/null +++ b/generate/api/gateway.api @@ -0,0 +1,20 @@ +syntax = "v1" + +info ( + title: "Digimon Platform API Gateway" + description: "This is Digimon Platform " + version: "v1" + contactName: "Daniel Wang" + contactEmail: "igs170911@gmail.com" + consumes: "application/json" + produces: "application/json" + schemes: "http,https" + host: "127.0.0.1:8888" +) + +import ( + "common.api" + "ping.api" + "member.api" +) + diff --git a/generate/api/member.api b/generate/api/member.api new file mode 100644 index 0000000..3091993 --- /dev/null +++ b/generate/api/member.api @@ -0,0 +1,554 @@ +syntax = "v1" + +// ================ 請求/響應結構 ================ +type ( + // 創建帳號請求 + CreateUserAccountReq { + LoginID string `json:"login_id" validate:"required,min=3,max=50"` + Platform string `json:"platform" validate:"required,oneof=platform google line apple"` + Token string `json:"token" validate:"required,min=8,max=128"` + } + + // 綁定用戶請求 + BindingUserReq { + UID string `json:"uid" validate:"required,min=1,max=50"` + LoginID string `json:"login_id" validate:"required,min=3,max=50"` + Type int `json:"type" validate:"required,oneof=1 2 3"` + } + + // 綁定用戶響應 + BindingUserResp { + UID string `json:"uid"` + LoginID string `json:"login_id"` + Type int `json:"type"` + } + + // 創建用戶資料請求 + CreateUserInfoReq { + UID string `json:"uid" validate:"required,min=1,max=50"` + AlarmType int `json:"alarm_type" validate:"required,oneof=0 1 2"` + Status int `json:"status" validate:"required,oneof=0 1 2 3"` + Language string `json:"language" validate:"required,min=2,max=10"` + Currency string `json:"currency" validate:"required,min=3,max=3"` + Avatar *string `json:"avatar,omitempty"` + NickName *string `json:"nick_name,omitempty" validate:"omitempty,min=1,max=50"` + FullName *string `json:"full_name,omitempty" validate:"omitempty,min=1,max=100"` + Gender *int64 `json:"gender,omitempty" validate:"omitempty,oneof=0 1 2"` + Birthdate *int64 `json:"birthdate,omitempty" validate:"omitempty,min=19000101,max=21001231"` + PhoneNumber *string `json:"phone_number,omitempty" validate:"omitempty,min=10,max=20"` + Email *string `json:"email,omitempty" validate:"omitempty,email"` + Address *string `json:"address,omitempty" validate:"omitempty,max=200"` + } + + // 獲取帳號資訊響應 + GetAccountInfoResp { + Data CreateUserAccountReq `json:"data"` + } + + // 更新用戶資料請求 + UpdateUserInfoReq { + UID string `json:"uid" validate:"required,min=1,max=50"` + Language *string `json:"language,omitempty" validate:"omitempty,min=2,max=10"` + Currency *string `json:"currency,omitempty" validate:"omitempty,min=3,max=3"` + NickName *string `json:"nick_name,omitempty" validate:"omitempty,min=1,max=50"` + Avatar *string `json:"avatar,omitempty"` + AlarmType *int `json:"alarm_type,omitempty" validate:"omitempty,oneof=0 1 2"` + Status *int `json:"status,omitempty" validate:"omitempty,oneof=0 1 2 3"` + FullName *string `json:"full_name,omitempty" validate:"omitempty,min=1,max=100"` + Gender *int64 `json:"gender,omitempty" validate:"omitempty,oneof=0 1 2"` + Birthdate *int64 `json:"birthdate,omitempty" validate:"omitempty,min=19000101,max=21001231"` + Address *string `json:"address,omitempty" validate:"omitempty,max=200"` + } + + // 獲取 UID 請求 + GetUIDByAccountReq { + Account string `json:"account" validate:"required,min=3,max=50"` + } + + // 獲取 UID 響應 + GetUIDByAccountResp { + UID string `json:"uid"` + Account string `json:"account"` + } + + // 更新密碼請求 + UpdateTokenReq { + Account string `json:"account" validate:"required,min=3,max=50"` + Token string `json:"token" validate:"required,min=8,max=128"` + Platform int `json:"platform" validate:"required,oneof=1 2 3 4"` + } + + // 生成驗證碼請求 + GenerateRefreshCodeReq { + Account string `json:"account" validate:"required,min=3,max=50"` + CodeType int `json:"code_type" validate:"required,oneof=1 2 3"` + } + + // 驗證碼響應 + VerifyCodeResp { + VerifyCode string `json:"verify_code"` + } + + // 生成驗證碼響應 + GenerateRefreshCodeResp { + Data VerifyCodeResp `json:"data"` + } + + // 驗證碼請求 + VerifyRefreshCodeReq { + Account string `json:"account" validate:"required,min=3,max=50"` + CodeType int `json:"code_type" validate:"required,oneof=1 2 3"` + VerifyCode string `json:"verify_code" validate:"required,min=4,max=10"` + } + + // 更新狀態請求 + UpdateStatusReq { + UID string `json:"uid" validate:"required,min=1,max=50"` + Status int `json:"status" validate:"required,oneof=0 1 2 3"` + } + + // 獲取用戶資訊請求 + GetUserInfoReq { + UID string `json:"uid,omitempty" validate:"omitempty,min=1,max=50"` + NickName *string `json:"nick_name,omitempty" validate:"omitempty,min=1,max=50"` + } + + // 用戶資訊 + UserInfo { + UID string `json:"uid"` + AvatarURL *string `json:"avatar_url,omitempty"` + FullName *string `json:"full_name,omitempty"` + NickName *string `json:"nick_name,omitempty"` + GenderCode *int64 `json:"gender_code,omitempty"` + Birthday *int64 `json:"birthday,omitempty"` + Phone *string `json:"phone,omitempty"` + Email *string `json:"email,omitempty"` + Address *string `json:"address,omitempty"` + AlarmType int `json:"alarm_type"` + Status int `json:"status"` + Language string `json:"language"` + Currency string `json:"currency"` + CreateTime int64 `json:"create_time"` + UpdateTime int64 `json:"update_time"` + } + + // 獲取用戶資訊響應 + GetUserInfoResp { + Data UserInfo `json:"data"` + } + + // 用戶列表請求 + ListUserInfoReq { + AlarmType *int `json:"alarm_type,omitempty" validate:"omitempty,oneof=0 1 2"` + Status *int `json:"status,omitempty" validate:"omitempty,oneof=0 1 2 3"` + CreateStartTime *int64 `json:"create_start_time,omitempty"` + CreateEndTime *int64 `json:"create_end_time,omitempty"` + PageSize int `json:"page_size" validate:"required,min=1,max=100"` + PageIndex int `json:"page_index" validate:"required,min=1"` + } + + // 用戶列表響應 + ListUserInfoResp { + Data []UserInfo `json:"data"` + Pager PagerResp `json:"pager"` + } + + // 驗證認證結果請求 + VerifyAuthResultReq { + Token string `json:"token" validate:"required,min=1"` + Account *string `json:"account,omitempty" validate:"omitempty,min=3,max=50"` + } + + // 驗證認證結果響應 + VerifyAuthResultResp { + Status bool `json:"status"` + } + + // Google 認證結果響應 + VerifyGoogleAuthResultResp { + Status bool `json:"status"` + Iss *string `json:"iss,omitempty"` + Sub *string `json:"sub,omitempty"` + Aud *string `json:"aud,omitempty"` + Exp *string `json:"exp,omitempty"` + Iat *string `json:"iat,omitempty"` + Email *string `json:"email,omitempty"` + EmailVerified *string `json:"email_verified,omitempty"` + Name *string `json:"name,omitempty"` + Picture *string `json:"picture,omitempty"` + } + + // 綁定驗證 Email 請求 + BindVerifyEmailReq { + UID string `json:"uid" validate:"required,min=1,max=50"` + Email string `json:"email" validate:"required,email"` + } + + // 綁定驗證 Phone 請求 + BindVerifyPhoneReq { + UID string `json:"uid" validate:"required,min=1,max=50"` + Phone string `json:"phone" validate:"required,min=10,max=20"` + } + + // LINE 獲取 Token 請求 + LineGetTokenReq { + Code string `json:"code" validate:"required,min=1"` + } + + // LINE Access Token 響應 + LineAccessTokenResp { + Token string `json:"token"` + } + + // LINE 用戶資料 + LineUserProfile { + DisplayName string `json:"display_name"` + UserID string `json:"user_id"` + PictureURL string `json:"picture_url"` + StatusMessage string `json:"status_message"` + } + + // LINE 獲取用戶資訊請求 + LineGetUserInfoReq { + Token string `json:"token" validate:"required,min=1"` + } +) + +// ================ API 路由 ================ +@server( + group: account + prefix: /api/v1/account +) +service gateway { + @doc( + summary: "創建帳號" + description: "創建新的帳號,支援多平台登入" + ) + /* + @respdoc-201 () // 創建成功 + @respdoc-400 ( + 40001: (ErrorResp) 帳號格式錯誤 + 40002: (ErrorResp) 密碼強度不足 + 40003: (ErrorResp) 平台類型無效 + ) // 客戶端錯誤 + @respdoc-409 (ErrorResp) // 帳號已存在 + @respdoc-500 (ErrorResp) // 服務器錯誤 + */ + @handler CreateUserAccount + post /create (CreateUserAccountReq) returns () + + @doc( + summary: "獲取帳號資訊" + description: "根據帳號獲取用戶的帳號資訊" + ) +/* + @respdoc-200 (GetAccountInfoResp) // 獲取成功 + @respdoc-400 ( + 40001: (ErrorResp) 帳號格式錯誤 + 40004: (ErrorResp) 參數驗證失敗 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 帳號不存在 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler GetUserAccountInfo + post /info (GetUIDByAccountReq) returns (GetAccountInfoResp) + + @doc( + summary: "更新用戶密碼" + description: "更新指定帳號的密碼" + ) +/* + @respdoc-200 (OKResp) // 更新成功 + @respdoc-400 ( + 40001: (ErrorResp) 帳號格式錯誤 + 40002: (ErrorResp) 密碼強度不足 + 40003: (ErrorResp) 平台類型無效 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 帳號不存在 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler UpdateUserToken + post /update-token (UpdateTokenReq) returns (OKResp) + + @doc( + summary: "獲取用戶 UID" + description: "根據帳號獲取對應的用戶 UID" + ) +/* + @respdoc-200 (GetUIDByAccountResp) // 獲取成功 + @respdoc-400 ( + 40001: (ErrorResp) 帳號格式錯誤 + 40004: (ErrorResp) 參數驗證失敗 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 帳號不存在 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler GetUIDByAccount + post /uid (GetUIDByAccountReq) returns (GetUIDByAccountResp) + + @doc( + summary: "綁定帳號" + description: "將帳號綁定到指定的用戶 UID" + ) +/* + @respdoc-200 (BindingUserResp) // 綁定成功 + @respdoc-400 ( + 40001: (ErrorResp) 帳號格式錯誤 + 40004: (ErrorResp) 參數驗證失敗 + 40005: (ErrorResp) UID 格式錯誤 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 帳號不存在 + @respdoc-409 (ErrorResp) // 帳號已綁定 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler BindAccount + post /bind (BindingUserReq) returns (BindingUserResp) + + @doc( + summary: "綁定用戶資料" + description: "初次綁定用戶的詳細資料" + ) +/* + @respdoc-200 (OKResp) // 綁定成功 + @respdoc-400 ( + 40004: (ErrorResp) 參數驗證失敗 + 40005: (ErrorResp) UID 格式錯誤 + 40006: (ErrorResp) 郵箱格式錯誤 + 40007: (ErrorResp) 電話格式錯誤 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 用戶不存在 + @respdoc-409 (ErrorResp) // 用戶資料已存在 + @respdoc-422 (ErrorResp) // 用戶資料不完整 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler BindUserInfo + post /bind-info (CreateUserInfoReq) returns (OKResp) + + @doc( + summary: "綁定驗證 Email" + description: "綁定並驗證用戶的 Email 地址" + ) +/* + @respdoc-200 (OKResp) // 綁定成功 + @respdoc-400 ( + 40005: (ErrorResp) UID 格式錯誤 + 40006: (ErrorResp) 郵箱格式錯誤 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 用戶不存在 + @respdoc-409 (ErrorResp) // 郵箱已綁定 + @respdoc-422 (ErrorResp) // 郵箱未驗證 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler BindVerifyEmail + post /bind-email (BindVerifyEmailReq) returns (OKResp) + + @doc( + summary: "綁定驗證 Phone" + description: "綁定並驗證用戶的電話號碼" + ) +/* + @respdoc-200 (OKResp) // 綁定成功 + @respdoc-400 ( + 40005: (ErrorResp) UID 格式錯誤 + 40007: (ErrorResp) 電話格式錯誤 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 用戶不存在 + @respdoc-409 (ErrorResp) // 電話已綁定 + @respdoc-422 (ErrorResp) // 電話未驗證 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler BindVerifyPhone + post /bind-phone (BindVerifyPhoneReq) returns (OKResp) + + @doc( + summary: "更新用戶資料" + description: "更新用戶的個人資料資訊" + ) +/* + @respdoc-200 (OKResp) // 更新成功 + @respdoc-400 ( + 40004: (ErrorResp) 參數驗證失敗 + 40005: (ErrorResp) UID 格式錯誤 + 40006: (ErrorResp) 郵箱格式錯誤 + 40007: (ErrorResp) 電話格式錯誤 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 用戶不存在 + @respdoc-422 (ErrorResp) // 用戶資料無效 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler UpdateUserInfo + post /update-info (UpdateUserInfoReq) returns (OKResp) + + @doc( + summary: "更新用戶狀態" + description: "更新用戶的帳號狀態" + ) +/* + @respdoc-200 (OKResp) // 更新成功 + @respdoc-400 ( + 40004: (ErrorResp) 參數驗證失敗 + 40005: (ErrorResp) UID 格式錯誤 + 40008: (ErrorResp) 狀態值無效 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 用戶不存在 + @respdoc-422 (ErrorResp) // 狀態更新無效 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler UpdateStatus + post /update-status (UpdateStatusReq) returns (OKResp) + + @doc( + summary: "獲取用戶資訊" + description: "根據 UID 或暱稱獲取用戶詳細資訊" + ) +/* + @respdoc-200 (GetUserInfoResp) // 獲取成功 + @respdoc-400 ( + 40004: (ErrorResp) 參數驗證失敗 + 40005: (ErrorResp) UID 格式錯誤 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 用戶不存在 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler GetUserInfo + post /user-info (GetUserInfoReq) returns (GetUserInfoResp) + + @doc( + summary: "獲取用戶列表" + description: "分頁獲取用戶列表,支援篩選條件" + ) +/* + @respdoc-200 (ListUserInfoResp) // 獲取成功 + @respdoc-400 ( + 40004: (ErrorResp) 參數驗證失敗 + 40009: (ErrorResp) 分頁參數無效 + ) // 客戶端錯誤 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler ListMember + post /list (ListUserInfoReq) returns (ListUserInfoResp) + + @doc( + summary: "生成驗證碼" + description: "為指定帳號生成驗證碼,用於忘記密碼等功能" + ) +/* + @respdoc-200 (GenerateRefreshCodeResp) // 生成成功 + @respdoc-400 ( + 40001: (ErrorResp) 帳號格式錯誤 + 40004: (ErrorResp) 參數驗證失敗 + 40010: (ErrorResp) 驗證碼類型無效 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 帳號不存在 + @respdoc-429 (ErrorResp) // 請求過於頻繁 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler GenerateRefreshCode + post /generate-code (GenerateRefreshCodeReq) returns (GenerateRefreshCodeResp) + + @doc( + summary: "驗證驗證碼" + description: "驗證並使用驗證碼,驗證後會刪除驗證碼" + ) +/* + @respdoc-200 (OKResp) // 驗證成功 + @respdoc-400 ( + 40001: (ErrorResp) 帳號格式錯誤 + 40004: (ErrorResp) 參數驗證失敗 + 40010: (ErrorResp) 驗證碼類型無效 + 40011: (ErrorResp) 驗證碼格式錯誤 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 驗證碼不存在 + @respdoc-422 (ErrorResp) // 驗證碼無效或已過期 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler VerifyRefreshCode + post /verify-code (VerifyRefreshCodeReq) returns (OKResp) + + @doc( + summary: "檢查驗證碼" + description: "檢查驗證碼是否正確,但不刪除驗證碼" + ) +/* + @respdoc-200 (OKResp) // 檢查成功 + @respdoc-400 ( + 40001: (ErrorResp) 帳號格式錯誤 + 40004: (ErrorResp) 參數驗證失敗 + 40010: (ErrorResp) 驗證碼類型無效 + 40011: (ErrorResp) 驗證碼格式錯誤 + ) // 客戶端錯誤 + @respdoc-404 (ErrorResp) // 驗證碼不存在 + @respdoc-422 (ErrorResp) // 驗證碼無效或已過期 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler CheckRefreshCode + post /check-code (VerifyRefreshCodeReq) returns (OKResp) + + @doc( + summary: "驗證 Google 認證" + description: "驗證 Google OAuth 登入是否有效" + ) +/* + @respdoc-200 (VerifyGoogleAuthResultResp) // 驗證成功 + @respdoc-400 ( + 40004: (ErrorResp) 參數驗證失敗 + 40012: (ErrorResp) Token 格式錯誤 + ) // 客戶端錯誤 + @respdoc-401 (ErrorResp) // Token 無效或過期 + @respdoc-422 (ErrorResp) // Google 認證失敗 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler VerifyGoogleAuthResult + post /verify-google (VerifyAuthResultReq) returns (VerifyGoogleAuthResultResp) + + @doc( + summary: "驗證平台認證" + description: "驗證平台登入認證是否有效" + ) +/* + @respdoc-200 (VerifyAuthResultResp) // 驗證成功 + @respdoc-400 ( + 40004: (ErrorResp) 參數驗證失敗 + 40012: (ErrorResp) Token 格式錯誤 + ) // 客戶端錯誤 + @respdoc-401 (ErrorResp) // Token 無效或過期 + @respdoc-422 (ErrorResp) // 平台認證失敗 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler VerifyPlatformAuthResult + post /verify-platform (VerifyAuthResultReq) returns (VerifyAuthResultResp) + + @doc( + summary: "LINE 獲取 Access Token" + description: "使用 LINE 授權碼獲取 Access Token" + ) +/* + @respdoc-200 (LineAccessTokenResp) // 獲取成功 + @respdoc-400 ( + 40004: (ErrorResp) 參數驗證失敗 + 40013: (ErrorResp) 授權碼格式錯誤 + ) // 客戶端錯誤 + @respdoc-401 (ErrorResp) // 授權碼無效或過期 + @respdoc-422 (ErrorResp) // LINE 認證失敗 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler LineCodeToAccessToken + post /line/token (LineGetTokenReq) returns (LineAccessTokenResp) + + @doc( + summary: "LINE 獲取用戶資料" + description: "使用 LINE Access Token 獲取用戶資料" + ) +/* + @respdoc-200 (LineUserProfile) // 獲取成功 + @respdoc-400 ( + 40004: (ErrorResp) 參數驗證失敗 + 40012: (ErrorResp) Token 格式錯誤 + ) // 客戶端錯誤 + @respdoc-401 (ErrorResp) // Token 無效或過期 + @respdoc-422 (ErrorResp) // LINE 用戶資料獲取失敗 + @respdoc-500 (ErrorResp) // 服務器錯誤 +*/ + @handler LineGetProfileByAccessToken + post /line/profile (LineGetUserInfoReq) returns (LineUserProfile) +} diff --git a/generate/api/ping.api b/generate/api/ping.api new file mode 100644 index 0000000..4d41de7 --- /dev/null +++ b/generate/api/ping.api @@ -0,0 +1,21 @@ +syntax = "v1" + +// =========================================== +// 系統健康檢查 API 端點定義 +// =========================================== + +@server( + group: ping + prefix: /api/v1 + schemes: https + timeout: 10s +) + +service gateway { + @doc( + summary: "系統健康檢查" + description: "檢查系統服務狀態,用於監控和負載均衡器健康檢查。返回系統運行狀態信息。" + ) + @handler Ping + get /health () returns () +} \ No newline at end of file diff --git a/generate/database/.DS_Store b/generate/database/.DS_Store new file mode 100644 index 0000000..a51f898 Binary files /dev/null and b/generate/database/.DS_Store differ diff --git a/generate/database/mongo/20240422112701_audo_id.down.txt b/generate/database/mongo/20240422112701_audo_id.down.txt new file mode 100755 index 0000000..80451dc --- /dev/null +++ b/generate/database/mongo/20240422112701_audo_id.down.txt @@ -0,0 +1,2 @@ +use digimon_member +db.count.dropIndex("name_1"); \ No newline at end of file diff --git a/generate/database/mongo/20240422112701_audo_id.up.txt b/generate/database/mongo/20240422112701_audo_id.up.txt new file mode 100755 index 0000000..6d428b9 --- /dev/null +++ b/generate/database/mongo/20240422112701_audo_id.up.txt @@ -0,0 +1,2 @@ +use digimon_member +db.count.createIndex({ "name": 1 }, { unique: true }); \ No newline at end of file diff --git a/generate/database/mongo/2024110500000001_account.up.txt b/generate/database/mongo/2024110500000001_account.up.txt new file mode 100755 index 0000000..3b053e3 --- /dev/null +++ b/generate/database/mongo/2024110500000001_account.up.txt @@ -0,0 +1,3 @@ +use digimon_member; +db.account.createIndex({ "login_id": 1, "platform": 1}, {unique: true}) +db.account.createIndex({"create_at": 1}) diff --git a/generate/database/mongo/2024110500000002_account_uid.up.txt b/generate/database/mongo/2024110500000002_account_uid.up.txt new file mode 100755 index 0000000..e584c35 --- /dev/null +++ b/generate/database/mongo/2024110500000002_account_uid.up.txt @@ -0,0 +1,4 @@ +use digimon_member; +db.account_uid_binding.createIndex({"login_id": 1}, {unique: true}) +db.account_uid_binding.createIndex({"uid": 1}) +db.account_uid_binding.createIndex({"create_at": 1}) diff --git a/generate/database/mongo/2024110500000003_user.up.txt b/generate/database/mongo/2024110500000003_user.up.txt new file mode 100755 index 0000000..ec9f664 --- /dev/null +++ b/generate/database/mongo/2024110500000003_user.up.txt @@ -0,0 +1,3 @@ +use digimon_member; +db.user_info.createIndex({"uid": 1},{unique: true}) +db.user_info.createIndex({"create_at": 1}) diff --git a/generate/database/mongo/2024121000000001_product.up.txt b/generate/database/mongo/2024121000000001_product.up.txt new file mode 100755 index 0000000..aefee8c --- /dev/null +++ b/generate/database/mongo/2024121000000001_product.up.txt @@ -0,0 +1,22 @@ +use digimon_product; + +# 精確查詢索引(針對 owner_uid, is_published, is_visible, created_at) +db.product.createIndex({"owner_uid": 1, "is_published": 1, "is_visible": 1, "created_at": -1}) + +# 範圍查詢索引(針對 start_time, end_time, created_at) +db.product.createIndex({"start_time": 1, "end_time": 1, "created_at": -1}) + +# 金額範圍索引(針對 target_amount, created_at) +db.product.createIndex({"target_amount": 1, "created_at": -1}) + +# 成功排序專用索引(針對 finished, updated_at) +db.product.createIndex({"finished": 1, "updated_at": -1}) + +# URL 精確查詢索引(針對 url_slug) +db.product.createIndex({"url_slug": 1}) + +# 基礎排序索引(針對 created_at 單列) +db.product.createIndex({"created_at": -1}) + +# URL 精確查詢索引(針對 url_slug) +db.product.createIndex({"category": 1}) \ No newline at end of file diff --git a/generate/database/mongo/2024121000000002_product_item.up.txt b/generate/database/mongo/2024121000000002_product_item.up.txt new file mode 100755 index 0000000..02565a4 --- /dev/null +++ b/generate/database/mongo/2024121000000002_product_item.up.txt @@ -0,0 +1,9 @@ +use digimon_product; + +# 精確查詢與範圍條件組合索引 +db.product_item.createIndex({"reference_id": 1}) # 精確匹配 reference_id +db.product_item.createIndex({"sale_start_time": 1, "sale_end_time": 1}) # 範圍查詢索引,提升 SaleStartTime 與 SaleEndTime 查詢效率 +db.product_item.createIndex({"status": 1}) # 精確查詢 status + +# 排序索引 +db.product_item.createIndex({"created_at": -1}) # 用於按 created_at 倒序排序 \ No newline at end of file diff --git a/generate/database/mongo/2024121000000003_supplementary_info.up.txt b/generate/database/mongo/2024121000000003_supplementary_info.up.txt new file mode 100755 index 0000000..5a4ab15 --- /dev/null +++ b/generate/database/mongo/2024121000000003_supplementary_info.up.txt @@ -0,0 +1,8 @@ +use digimon_product; + +# 精確查詢與範圍條件組合索引 +db.supplementary_info.createIndex({"reference_id": 1}) # 精確匹配 reference_id +db.supplementary_info.createIndex({"info_type": 1}) # 精確查詢 status + +# 排序索引 +db.supplementary_info.createIndex({"created_at": -1}) # 用於按 created_at 倒序排序 \ No newline at end of file diff --git a/generate/database/mongo/2024121800000001_cart.up.txt b/generate/database/mongo/2024121800000001_cart.up.txt new file mode 100755 index 0000000..e2bc160 --- /dev/null +++ b/generate/database/mongo/2024121800000001_cart.up.txt @@ -0,0 +1,6 @@ +use digimon_cart; + +# 精確查詢與範圍條件組合索引 +db.cart.createIndex({ "uid": 1, "product_id": 1 }, { unique: true }) +# 排序索引 +db.supplementary_info.createIndex({"created_at": -1}) # 用於按 created_at 倒序排序 \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b5e3786 --- /dev/null +++ b/go.mod @@ -0,0 +1,50 @@ +module backend + +go 1.25.1 + +require github.com/zeromicro/go-zero v1.9.0 + +require ( + 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/fatih/color v1.18.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grafana/pyroscope-go v1.2.4 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 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.1 // 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/spaolacci/murmur3 v1.1.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.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.24.0 // indirect + go.opentelemetry.io/otel/sdk v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b636017 --- /dev/null +++ b/go.sum @@ -0,0 +1,132 @@ +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/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/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/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +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/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grafana/pyroscope-go v1.2.4 h1:B22GMXz+O0nWLatxLuaP7o7L9dvP0clLvIpmeEQQM0Q= +github.com/grafana/pyroscope-go v1.2.4/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU= +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.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +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/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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +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/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/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/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/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.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= +github.com/prometheus/client_golang v1.21.1/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/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +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.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.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/zeromicro/go-zero v1.9.0 h1:hlVtQCSHPszQdcwZTawzGwTej1G2mhHybYzMRLuwCt4= +github.com/zeromicro/go-zero v1.9.0/go.mod h1:TMyCxiaOjLQ3YxyYlJrejaQZF40RlzQ3FVvFu5EbcV4= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +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.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= +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.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +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= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +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/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY= +google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +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-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/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/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..8da153d --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,7 @@ +package config + +import "github.com/zeromicro/go-zero/rest" + +type Config struct { + rest.RestConf +} diff --git a/internal/handler/ping/ping_handler.go b/internal/handler/ping/ping_handler.go new file mode 100644 index 0000000..3ec50e0 --- /dev/null +++ b/internal/handler/ping/ping_handler.go @@ -0,0 +1,22 @@ +package ping + +import ( + "net/http" + + "backend/internal/logic/ping" + "backend/internal/svc" + "github.com/zeromicro/go-zero/rest/httpx" +) + +// 系統健康檢查 +func PingHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + l := ping.NewPingLogic(r.Context(), svcCtx) + err := l.Ping() + if err != nil { + httpx.ErrorCtx(r.Context(), w, err) + } else { + httpx.Ok(w) + } + } +} diff --git a/internal/handler/routes.go b/internal/handler/routes.go new file mode 100644 index 0000000..f7c98f6 --- /dev/null +++ b/internal/handler/routes.go @@ -0,0 +1,29 @@ +// Code generated by goctl. DO NOT EDIT. +// goctl 1.8.1 + +package handler + +import ( + "net/http" + "time" + + ping "backend/internal/handler/ping" + "backend/internal/svc" + + "github.com/zeromicro/go-zero/rest" +) + +func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { + server.AddRoutes( + []rest.Route{ + { + // 系統健康檢查 + Method: http.MethodGet, + Path: "/health", + Handler: ping.PingHandler(serverCtx), + }, + }, + rest.WithPrefix("/api/v1"), + rest.WithTimeout(10000*time.Millisecond), + ) +} diff --git a/internal/logic/ping/ping_logic.go b/internal/logic/ping/ping_logic.go new file mode 100644 index 0000000..402d878 --- /dev/null +++ b/internal/logic/ping/ping_logic.go @@ -0,0 +1,29 @@ +package ping + +import ( + "context" + + "backend/internal/svc" + "github.com/zeromicro/go-zero/core/logx" +) + +type PingLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// 系統健康檢查 +func NewPingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PingLogic { + return &PingLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *PingLogic) Ping() error { + // todo: add your logic here and delete this line + + return nil +} diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go new file mode 100644 index 0000000..7a6bf15 --- /dev/null +++ b/internal/svc/service_context.go @@ -0,0 +1,15 @@ +package svc + +import ( + "backend/internal/config" +) + +type ServiceContext struct { + Config config.Config +} + +func NewServiceContext(c config.Config) *ServiceContext { + return &ServiceContext{ + Config: c, + } +} diff --git a/internal/types/types.go b/internal/types/types.go new file mode 100644 index 0000000..58a3461 --- /dev/null +++ b/internal/types/types.go @@ -0,0 +1,32 @@ +// Code generated by goctl. DO NOT EDIT. +// goctl 1.8.1 + +package types + +type BaseReq struct { +} + +type BaseResponse struct { + Status Status `json:"status"` // 狀態 + Data interface{} `json:"data"` // 資料 +} + +type Pager struct { + Total int64 `json:"total"` + PageSize int64 `json:"page_size"` + PageIndex int64 `json:"page_index"` +} + +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 VerifyHeader struct { + Token string `header:"token" validate:"required"` +}