feat: add login page

This commit is contained in:
王性驊 2025-10-01 00:53:31 +08:00
commit 513a290031
33 changed files with 4144 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -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

9
.idea/backend.iml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="-31530129:19584ea2536:-7ff9" />
</MTProjectMetadataState>
</option>
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/backend.iml" filepath="$PROJECT_DIR$/.idea/backend.iml" />
</modules>
</component>
</project>

4
.idea/vcs.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" />
</project>

76
Makefile Normal file
View File

@ -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 - 顯示幫助信息"

59
cursor.md Normal file
View File

@ -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** 版本控制的嚴格性與相容性。 |
---

3
etc/gateway.yaml Normal file
View File

@ -0,0 +1,3 @@
Name: gateway
Host: 0.0.0.0
Port: 8888

31
gateway.go Normal file
View File

@ -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()
}

2931
gateway.json Normal file

File diff suppressed because it is too large Load Diff

BIN
generate/.DS_Store vendored Normal file

Binary file not shown.

33
generate/api/common.api Executable file
View File

@ -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"`
}
)

20
generate/api/gateway.api Executable file
View File

@ -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"
)

554
generate/api/member.api Normal file
View File

@ -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)
}

21
generate/api/ping.api Normal file
View File

@ -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 ()
}

BIN
generate/database/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,2 @@
use digimon_member
db.count.dropIndex("name_1");

View File

@ -0,0 +1,2 @@
use digimon_member
db.count.createIndex({ "name": 1 }, { unique: true });

View File

@ -0,0 +1,3 @@
use digimon_member;
db.account.createIndex({ "login_id": 1, "platform": 1}, {unique: true})
db.account.createIndex({"create_at": 1})

View File

@ -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})

View File

@ -0,0 +1,3 @@
use digimon_member;
db.user_info.createIndex({"uid": 1},{unique: true})
db.user_info.createIndex({"create_at": 1})

View File

@ -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})

View File

@ -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 倒序排序

View File

@ -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 倒序排序

View File

@ -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 倒序排序

50
go.mod Normal file
View File

@ -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
)

132
go.sum Normal file
View File

@ -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=

View File

@ -0,0 +1,7 @@
package config
import "github.com/zeromicro/go-zero/rest"
type Config struct {
rest.RestConf
}

View File

@ -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)
}
}
}

View File

@ -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),
)
}

View File

@ -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
}

View File

@ -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,
}
}

32
internal/types/types.go Normal file
View File

@ -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"`
}