feat 结构调整

This commit is contained in:
2025-12-20 15:39:25 +08:00
parent 55c5d4cc18
commit ff1bd1d0b6
96 changed files with 4904 additions and 350 deletions

View File

@@ -0,0 +1,37 @@
package http_gateway
import (
"fmt"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"time"
)
func corsConfig() cors.Config {
return cors.Config{
AllowMethods: []string{"GET", "POST", "OPTIONS"},
AllowHeaders: []string{"Content-Type", "Authorization"},
AllowCredentials: false,
AllowAllOrigins: true,
MaxAge: 12 * time.Hour,
}
}
func ginLogger(logger *zap.SugaredLogger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
c.Next()
cost := time.Since(start)
logger.Infof(fmt.Sprintf(
"HTTP Method:%v Code:%v Time:%v IP:%v Path:%v",
c.Request.Method,
c.Writer.Status(),
cost,
c.ClientIP(),
path),
)
}
}

View File

@@ -0,0 +1,54 @@
package http_gateway
import (
"common/log"
"common/net/http/http_resp"
wrapper2 "gateway/internal/net/http_gateway/wrapper"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/protobuf/encoding/protojson"
"net/http"
)
func InitServeMux() *runtime.ServeMux {
baseMarshaler := &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseProtoNames: false,
EmitUnpopulated: true,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
}
unifiedMarshaler := wrapper2.NewWrappedMarshaler(baseMarshaler)
mux := runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, unifiedMarshaler),
runtime.WithErrorHandler(wrapper2.ErrorHandler),
)
return mux
}
func InitRouter(mux *runtime.ServeMux) *gin.Engine {
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(
gin.Recovery(),
ginLogger(log.GetLogger().Named("GIN")),
cors.New(corsConfig()),
)
r.HandleMethodNotAllowed = true
r.NoMethod(func(c *gin.Context) {
c.JSON(http.StatusMethodNotAllowed, http_resp.Error(http_resp.Failed.Code(), "Method Not Allowed"))
})
r.NoRoute(func(c *gin.Context) {
c.JSON(http.StatusNotFound, http_resp.Error(http_resp.Failed.Code(), "Endpoint Not Found"))
})
r.Any("/*any", gin.WrapH(mux))
return r
}

View File

@@ -0,0 +1,53 @@
package wrapper
import (
"common/net/http/http_resp"
"common/proto/ss/ss_common"
"context"
"encoding/json"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"net/http"
)
// ErrorHandler 将 gRPC 错误转为统一 JSON 格式
func ErrorHandler(_ context.Context, _ *runtime.ServeMux, _ runtime.Marshaler, w http.ResponseWriter, _ *http.Request, err error) {
st, ok := status.FromError(err)
if !ok {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(http_resp.Error(http_resp.Failed.Code(), http_resp.Failed.Error()))
return
}
code, msg := 0, ""
for _, detail := range st.Details() {
if errorInfo, ok := detail.(*ss_common.ErrorInfo); ok {
code = int(errorInfo.Code)
msg = errorInfo.Msg
break
}
}
if code == 0 {
code = http_resp.Failed.Code()
msg = http_resp.Failed.Error()
}
if st.Code() == codes.Unknown {
msg = st.Message()
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(grpcCodeToHTTPCode(st.Code()))
_ = json.NewEncoder(w).Encode(http_resp.Error(code, msg))
}
// 这里定义 Internal 属于业务错误,其他的属于 500 报错
func grpcCodeToHTTPCode(c codes.Code) int {
switch c {
case codes.OK, codes.Unknown:
return http.StatusOK
default:
return http.StatusInternalServerError
}
}

View File

@@ -0,0 +1,42 @@
package wrapper
import (
"common/net/http/http_resp"
"encoding/json"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"io"
)
// WrappedMarshaler 自动包装响应为 { code, message, data }
type WrappedMarshaler struct {
inner runtime.Marshaler
}
func NewWrappedMarshaler(inner runtime.Marshaler) *WrappedMarshaler {
return &WrappedMarshaler{inner: inner}
}
// Marshal 将 gRPC 响应包装成统一格式
func (w *WrappedMarshaler) Marshal(v interface{}) ([]byte, error) {
dataBytes, err := w.inner.Marshal(v)
if err != nil {
return json.Marshal(http_resp.Error(http_resp.Failed.Code(), http_resp.Failed.Error()))
}
return json.Marshal(http_resp.Success(json.RawMessage(dataBytes)))
}
func (w *WrappedMarshaler) Unmarshal(data []byte, v interface{}) error {
return w.inner.Unmarshal(data, v)
}
func (w *WrappedMarshaler) NewDecoder(r io.Reader) runtime.Decoder {
return w.inner.NewDecoder(r)
}
func (w *WrappedMarshaler) NewEncoder(wr io.Writer) runtime.Encoder {
return w.inner.NewEncoder(wr)
}
func (w *WrappedMarshaler) ContentType(v interface{}) string {
return "application/json"
}

View File

@@ -0,0 +1,64 @@
package ws_gateway
import (
"common/log"
"common/net/socket"
"fmt"
ws_handler2 "gateway/internal/handler/ws_handler"
"go.uber.org/zap"
"strconv"
"time"
)
type GatewayWsServer struct {
logger *zap.SugaredLogger
}
func (g *GatewayWsServer) OnOpen(conn socket.ISocketConn) ([]byte, socket.Action) {
g.logger = log.GetLogger().Named(fmt.Sprintf("addr:%v", conn.RemoteAddr()))
return nil, socket.None
}
func (g *GatewayWsServer) OnHandShake(conn socket.ISocketConn) {
token, ok := conn.GetParam("token").(string)
if !ok || len(token) == 0 {
g.logger.Warnf("token is not string")
_ = conn.Close()
return
}
t, err := strconv.Atoi(token)
if err != nil {
_ = conn.Close()
}
if oldClient := ws_handler2.UserMgr.GetByUID(int32(t)); oldClient != nil {
oldClient.CloseClient()
}
client := ws_handler2.NewClient(int32(t), conn)
ws_handler2.UserMgr.Add(int32(t), client)
conn.SetParam("client", client)
}
func (g *GatewayWsServer) OnMessage(conn socket.ISocketConn, bytes []byte) socket.Action {
client, ok := conn.GetParam("client").(*ws_handler2.Client)
if !ok || client.UID == 0 {
return socket.Close
}
client.OnEvent(&ws_handler2.ClientEvent{Msg: bytes})
return socket.None
}
func (g *GatewayWsServer) OnPong(conn socket.ISocketConn) {
client, ok := conn.GetParam("client").(*ws_handler2.Client)
if !ok || client.UID == 0 {
return
}
client.OnEvent(&ws_handler2.PongEvent{})
}
func (g *GatewayWsServer) OnClose(_ socket.ISocketConn, _ error) socket.Action {
return socket.Close
}
func (g *GatewayWsServer) OnTick() (time.Duration, socket.Action) {
return 5 * time.Second, socket.None
}