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