108 lines
4.8 KiB
Go
108 lines
4.8 KiB
Go
|
|
// Package http 提供平台总后台的 HTTP 路由和统一响应。
|
||
|
|
package http
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"log/slog"
|
||
|
|
"net/http"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"git.apinb.com/heqiapp/platforms/backend/internal/platform"
|
||
|
|
"github.com/google/uuid"
|
||
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
||
|
|
)
|
||
|
|
|
||
|
|
// NewRouter 注册首期平台总后台所需的只读列表、仪表盘与气站创建接口。
|
||
|
|
func NewRouter(pool *pgxpool.Pool, logger *slog.Logger) http.Handler {
|
||
|
|
repository := platform.NewRepository(pool)
|
||
|
|
mux := http.NewServeMux()
|
||
|
|
|
||
|
|
mux.HandleFunc("GET /healthz", func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
writeJSON(writer, http.StatusOK, map[string]any{"status": "ok"})
|
||
|
|
})
|
||
|
|
mux.HandleFunc("GET /api/v1/dashboard/overview", func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
respondRepository(writer, request, logger, func() (any, error) { return repository.DashboardOverview(request.Context()) })
|
||
|
|
})
|
||
|
|
mux.HandleFunc("GET /api/v1/org/gas-station", func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
respondRepository(writer, request, logger, func() (any, error) { return repository.ListOrgGasStation(request.Context()) })
|
||
|
|
})
|
||
|
|
mux.HandleFunc("POST /api/v1/org/gas-station", func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
var body struct {
|
||
|
|
StationCode string `json:"stationCode"`
|
||
|
|
Name string `json:"name"`
|
||
|
|
Principal string `json:"principal"`
|
||
|
|
ServiceArea string `json:"serviceArea"`
|
||
|
|
}
|
||
|
|
if err := json.NewDecoder(request.Body).Decode(&body); err != nil || body.StationCode == "" || body.Name == "" || body.Principal == "" || body.ServiceArea == "" {
|
||
|
|
writeError(writer, http.StatusBadRequest, "参数不完整:站点编码、名称、负责人和服务区域均为必填")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
respondRepositoryWithStatus(writer, request, logger, http.StatusCreated, func() (any, error) {
|
||
|
|
return repository.CreateOrgGasStation(request.Context(), body.StationCode, body.Name, body.Principal, body.ServiceArea)
|
||
|
|
})
|
||
|
|
})
|
||
|
|
mux.HandleFunc("GET /api/v1/org/delivery-point", func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
respondRepository(writer, request, logger, func() (any, error) { return repository.ListOrgDeliveryPoint(request.Context()) })
|
||
|
|
})
|
||
|
|
mux.HandleFunc("GET /api/v1/org/service-person", func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
respondRepository(writer, request, logger, func() (any, error) { return repository.ListOrgServicePerson(request.Context()) })
|
||
|
|
})
|
||
|
|
mux.HandleFunc("GET /api/v1/idn/account", func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
respondRepository(writer, request, logger, func() (any, error) { return repository.ListIdnAccount(request.Context()) })
|
||
|
|
})
|
||
|
|
mux.HandleFunc("GET /api/v1/saf/event", func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
respondRepository(writer, request, logger, func() (any, error) { return repository.ListSafEvent(request.Context()) })
|
||
|
|
})
|
||
|
|
|
||
|
|
return requestLogger(cors(mux), logger)
|
||
|
|
}
|
||
|
|
|
||
|
|
func respondRepository(writer http.ResponseWriter, request *http.Request, logger *slog.Logger, query func() (any, error)) {
|
||
|
|
respondRepositoryWithStatus(writer, request, logger, http.StatusOK, query)
|
||
|
|
}
|
||
|
|
|
||
|
|
func respondRepositoryWithStatus(writer http.ResponseWriter, request *http.Request, logger *slog.Logger, status int, query func() (any, error)) {
|
||
|
|
result, err := query()
|
||
|
|
if err != nil {
|
||
|
|
logger.Error("处理平台 API 请求失败", "method", request.Method, "path", request.URL.Path, "error", err)
|
||
|
|
writeError(writer, http.StatusInternalServerError, "服务暂不可用,请稍后重试")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
writeJSON(writer, status, map[string]any{"data": result})
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeJSON(writer http.ResponseWriter, status int, data any) {
|
||
|
|
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||
|
|
writer.WriteHeader(status)
|
||
|
|
_ = json.NewEncoder(writer).Encode(data)
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeError(writer http.ResponseWriter, status int, message string) {
|
||
|
|
writeJSON(writer, status, map[string]any{"error": message})
|
||
|
|
}
|
||
|
|
|
||
|
|
func cors(next http.Handler) http.Handler {
|
||
|
|
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173")
|
||
|
|
writer.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
|
||
|
|
writer.Header().Set("Access-Control-Allow-Headers", "Content-Type,Idempotency-Key")
|
||
|
|
if request.Method == http.MethodOptions {
|
||
|
|
writer.WriteHeader(http.StatusNoContent)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
next.ServeHTTP(writer, request)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func requestLogger(next http.Handler, logger *slog.Logger) http.Handler {
|
||
|
|
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
requestID, err := uuid.NewV7()
|
||
|
|
if err == nil {
|
||
|
|
writer.Header().Set("X-Request-Id", requestID.String())
|
||
|
|
}
|
||
|
|
startedAt := time.Now()
|
||
|
|
next.ServeHTTP(writer, request)
|
||
|
|
logger.Info("平台 API 请求完成", "method", request.Method, "path", request.URL.Path, "duration", time.Since(startedAt).String())
|
||
|
|
})
|
||
|
|
}
|