chore: initial import

This commit is contained in:
phamnazage-jpg
2026-05-12 17:47:32 +08:00
commit fc54ba84b2
104 changed files with 11575 additions and 0 deletions

59
pkg/response/response.go Normal file
View File

@@ -0,0 +1,59 @@
package response
import (
"encoding/json"
"net/http"
"github.com/company/ai-ops/pkg/errors"
)
// Response 是统一响应结构
type Response struct {
Code string `json:"code,omitempty"`
Message string `json:"message,omitempty"`
Data any `json:"data,omitempty"`
}
// JSON 返回 JSON 响应
func JSON(w http.ResponseWriter, status int, data any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
// Success 返回成功响应
func Success(w http.ResponseWriter, data any) {
JSON(w, http.StatusOK, Response{Data: data})
}
// Error 返回错误响应
func Error(w http.ResponseWriter, err *errors.AppError) {
JSON(w, err.HTTPStatus, Response{
Code: err.Code,
Message: err.Message,
})
}
// Paginated 是分页响应结构
type Paginated struct {
Items any `json:"items"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalPages int `json:"total_pages"`
}
// PaginatedResponse 返回分页响应
func PaginatedResponse(w http.ResponseWriter, items any, total, page, pageSize int) {
totalPages := total / pageSize
if total%pageSize > 0 {
totalPages++
}
Success(w, Paginated{
Items: items,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
})
}

View File

@@ -0,0 +1,66 @@
package response
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
errorspkg "github.com/company/ai-ops/pkg/errors"
)
func TestJSONWritesStatusContentTypeAndBody(t *testing.T) {
w := httptest.NewRecorder()
JSON(w, http.StatusCreated, map[string]string{"ok": "true"})
if w.Code != http.StatusCreated {
t.Fatalf("status = %d", w.Code)
}
if got := w.Header().Get("Content-Type"); got != "application/json" {
t.Fatalf("content-type = %s", got)
}
if got := w.Body.String(); got != "{\"ok\":\"true\"}\n" {
t.Fatalf("body = %s", got)
}
}
func TestSuccessAndErrorResponses(t *testing.T) {
t.Run("success", func(t *testing.T) {
w := httptest.NewRecorder()
Success(w, map[string]any{"id": "1"})
var out Response
if err := json.Unmarshal(w.Body.Bytes(), &out); err != nil {
t.Fatal(err)
}
data := out.Data.(map[string]any)
if data["id"] != "1" {
t.Fatalf("unexpected data: %+v", out.Data)
}
})
t.Run("error", func(t *testing.T) {
w := httptest.NewRecorder()
Error(w, errorspkg.ErrForbidden)
if w.Code != http.StatusForbidden {
t.Fatalf("status = %d", w.Code)
}
if got := w.Body.String(); got == "" || !json.Valid(w.Body.Bytes()) {
t.Fatalf("invalid json body: %q", got)
}
})
}
func TestPaginatedResponseComputesTotalPages(t *testing.T) {
w := httptest.NewRecorder()
PaginatedResponse(w, []string{"a", "b"}, 21, 2, 10)
var out Response
if err := json.Unmarshal(w.Body.Bytes(), &out); err != nil {
t.Fatal(err)
}
payload := out.Data.(map[string]any)
if payload["total_pages"].(float64) != 3 {
t.Fatalf("total_pages = %+v", payload["total_pages"])
}
}