From 5b63fd83a9069647194c111df56ed1bf81695e79 Mon Sep 17 00:00:00 2001 From: AI Engineer Date: Fri, 8 May 2026 22:41:01 +0800 Subject: [PATCH] Slim down service module: remove redundant features and optimize ID generation (by AI) --- README.md | 54 +++++++------- document.go | 29 +------- handler.go | 7 +- service.go | 9 ++- starter.go | 49 ------------- types.go | 68 ------------------ types_test.go | 41 ----------- utility.go | 47 ++++++++++++- websocket.go | 176 +++++++++------------------------------------- websocket_test.go | 13 ++-- 10 files changed, 129 insertions(+), 364 deletions(-) delete mode 100644 starter.go delete mode 100644 types.go delete mode 100644 types_test.go diff --git a/README.md b/README.md index bf588e2..b679fa9 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,11 @@ 极简、自动化的 Web 与 WebSocket 服务框架,实现极致的依赖注入与路由映射。 ## 核心特性 +- **极致精简**: 剥离非核心组件(如 Starter, Task, 业务 Result 定义),保持底座纯净。 - **路由反射**: 自动解析函数参数,支持 `*Request`, `*Response`, `*log.Logger` 及自定义结构体自动注入。 - **自动校验**: 集成 `verify` 引擎,通过 Struct Tag 实现入参合法性自动检查。 -- **功能闭环**: 内置静态文件服务、WebSocket (带 Action 路由)、URL 重写、反向代理(对接 Discover)。 -- **零摩擦启动**: 支持命令行指令管理 (start/stop/help) 及异步平滑启停。 +- **功能闭环**: 内置静态文件服务、基础 WebSocket 注册、URL 重写、反向代理(对接 Discover)。 +- **统一 ID 体系**: 整合 Redis 集群版 ID 生成器,全局统一生成 `RequestId` 与 `LogTraceID`。 ## API 指南 @@ -14,39 +15,42 @@ ```go import "apigo.cc/go/service" -// 注册标准 Web 服务 -service.Register(0, "/hello", func(in struct{ Name string }) string { +// 注册标准 Web 服务,自动注入 Struct 参数并执行校验 +service.Register(0, "/hello", func(in struct{ Name string `verify:"length:2+"` }) string { return "Hello " + in.Name }, "打招呼接口") - -// 注册 Restful 服务 -service.Restful(0, "POST", "/user/{id}", func(args map[string]any) service.Result { - res := service.Result{} - res.OK() - return res -}, "更新用户") ``` -### 2. WebSocket 支持 +### 2. WebSocket 支持 (极简模式) ```go -ar := service.RegisterWebsocket(0, "/ws", onOpen, onClose, "聊天室") -ar.RegisterAction(0, "chat", func(in ChatMessage, sess *MySession) { - // 处理消息 -}, "发送消息") +// 业务自行处理消息循环与逻辑 +service.RegisterWebsocket(0, "/ws", func(conn *websocket.Conn, logger *log.Logger) { + defer conn.Close() + for { + _, msg, err := conn.ReadMessage() + if err != nil { + break + } + logger.Info("received", "msg", string(msg)) + } +}, "聊天室") ``` -### 3. 增强插件 +### 3. 生命周期管理 +```go +func main() { + // 异步启动 + as := service.AsyncStart() + // 执行其他初始化... + as.Wait() // 阻塞并监听信号优雅退出 +} +``` + +### 4. 增强插件 - **静态文件**: `service.Static("/ui", "./static_dir")` - **URL 重写**: `service.Rewrite("/old", "/new")` - **反向代理**: `service.Proxy(0, "/api", "other_app", "/api")` - -### 4. 生命周期管理 -```go -func main() { - service.CheckCmd() // 处理 start/stop/help 指令 - service.Start() // 阻塞启动 -} -``` +- **文档生成**: `service.MakeDocument()` 返回全量接口描述 ## 基础设施对齐 - **类型转换**: `apigo.cc/go/cast` diff --git a/document.go b/document.go index 428148c..791b3b2 100644 --- a/document.go +++ b/document.go @@ -77,10 +77,10 @@ func MakeDocument() []Api { AuthLevel: a.authLevel, Memo: a.memo, } - if a.openFuncType != nil && a.openFuncType.NumIn() > 0 { + if a.handlerType != nil && a.handlerType.NumIn() > 0 { // Find struct in - for i := 0; i < a.openFuncType.NumIn(); i++ { - t := a.openFuncType.In(i) + for i := 0; i < a.handlerType.NumIn(); i++ { + t := a.handlerType.In(i) if t.Kind() == reflect.Struct { api.In = getType(t) break @@ -88,22 +88,6 @@ func MakeDocument() []Api { } } out = append(out, api) - - for name, act := range a.actions { - actionApi := Api{ - Type: "Action", - Path: name, - AuthLevel: act.authLevel, - Memo: act.memo, - } - if act.inType != nil { - actionApi.In = getType(act.inType) - } - if act.funcType.NumOut() > 0 { - actionApi.Out = getType(act.funcType.Out(0)) - } - out = append(out, actionApi) - } } websocketServicesLock.RUnlock() @@ -153,10 +137,3 @@ func getType(t reflect.Type) any { return t.String() } } - -// 自动注册文档服务 -func init() { - Register(0, "/__DOC__", func() string { - return MakeJsonDocument() - }, "API Document") -} diff --git a/handler.go b/handler.go index 181fa08..b178855 100644 --- a/handler.go +++ b/handler.go @@ -2,7 +2,6 @@ package service import ( "apigo.cc/go/cast" - "apigo.cc/go/id" "apigo.cc/go/log" "apigo.cc/go/standard" "encoding/json" @@ -25,7 +24,7 @@ func (rh *routeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { startTime := time.Now() requestId := r.Header.Get(standard.DiscoverHeaderRequestId) if requestId == "" { - requestId = id.MakeID(12) + requestId = MakeId(12) r.Header.Set(standard.DiscoverHeaderRequestId, requestId) } @@ -278,7 +277,7 @@ func handleClientKeys(request *Request, response *Response) { if sessionIdMaker != nil { sessionId = sessionIdMaker() } else { - sessionId = id.MakeID(14) + sessionId = MakeId(14) } if !Config.SessionWithoutCookie { http.SetCookie(response.Writer, &http.Cookie{ @@ -302,7 +301,7 @@ func handleClientKeys(request *Request, response *Response) { } } if deviceId == "" { - deviceId = id.MakeID(14) + deviceId = MakeId(14) if !Config.DeviceWithoutCookie { http.SetCookie(response.Writer, &http.Cookie{ Name: usedDeviceIdKey, diff --git a/service.go b/service.go index 5dea62c..bbe48fe 100644 --- a/service.go +++ b/service.go @@ -9,6 +9,12 @@ import ( "sync" ) +// Map 通用 Map 类型 +type Map = map[string]any + +// Arr 通用切片类型 +type Arr = []any + // WebServiceOptions 服务注册选项 type WebServiceOptions struct { Priority int @@ -58,9 +64,8 @@ var ( webServicesList = make([]*webServiceType, 0) websocketServices = make(map[string]*websocketServiceType) - regexWebsocketServices = make([]*websocketServiceType, 0) websocketServicesLock = sync.RWMutex{} - websocketServicesList = make([]*websocketServiceType, 0) + websocketServicesList = make([]*webServiceType, 0) // 过滤器与拦截器 inFilters = make([]func(*map[string]any, *Request, *Response, *log.Logger) any, 0) diff --git a/starter.go b/starter.go deleted file mode 100644 index 970005f..0000000 --- a/starter.go +++ /dev/null @@ -1,49 +0,0 @@ -package service - -import ( - "fmt" - "os" - "path/filepath" -) - -// StartCmd 命令行命令定义 -type StartCmd struct { - Name string - Comment string - Func func() -} - -var startCmds = []StartCmd{ - {"start", "Start server", Start}, -} - -// AddCmd 添加自定义命令行命令 -func AddCmd(name, comment string, function func()) { - startCmds = append(startCmds, StartCmd{name, comment, function}) -} - -// CheckCmd 检查并执行命令行命令 -func CheckCmd() { - if len(os.Args) > 1 { - cmd := os.Args[1] - if cmd == "help" || cmd == "--help" { - showHelp() - os.Exit(0) - } - - for _, cmdInfo := range startCmds { - if cmd == cmdInfo.Name { - cmdInfo.Func() - os.Exit(0) - } - } - } -} - -func showHelp() { - fmt.Printf("Usage: %s [command]\n\n", filepath.Base(os.Args[0])) - fmt.Println("Available commands:") - for _, cmdInfo := range startCmds { - fmt.Printf(" %-10s %s\n", cmdInfo.Name, cmdInfo.Comment) - } -} diff --git a/types.go b/types.go deleted file mode 100644 index 5ce805f..0000000 --- a/types.go +++ /dev/null @@ -1,68 +0,0 @@ -package service - -// Map 通用 Map 类型 -type Map = map[string]any - -// Arr 通用切片类型 -type Arr = []any - -// Argot 错误码/标识符类型 -type Argot string - -// Result 通用返回结构 -type Result struct { - Ok bool `json:"ok"` - Argot Argot `json:"argot,omitempty"` - Message string `json:"message,omitempty"` -} - -// CodeResult 带状态码的返回结构 -type CodeResult struct { - Code int `json:"code"` - Message string `json:"message,omitempty"` -} - -// ArgotInfo 标识符信息(用于文档生成) -type ArgotInfo struct { - Name Argot - Memo string -} - -// OK 设置成功状态 -func (r *Result) OK(argots ...Argot) { - r.Ok = true - if len(argots) > 0 { - r.Argot = argots[0] - } -} - -// Failed 设置失败状态 -func (r *Result) Failed(message string, argots ...Argot) { - r.Ok = false - r.Message = message - if len(argots) > 0 { - r.Argot = argots[0] - } -} - -// Done 根据布尔值设置状态 -func (r *Result) Done(ok bool, failedMessage string, argots ...Argot) { - r.Ok = ok - if !ok { - r.Message = failedMessage - if len(argots) > 0 { - r.Argot = argots[0] - } - } -} - -// OK 设置成功状态 (Code=1) -func (r *CodeResult) OK() { - r.Code = 1 -} - -// Failed 设置失败状态与错误码 -func (r *CodeResult) Failed(code int, message string) { - r.Code = code - r.Message = message -} diff --git a/types_test.go b/types_test.go deleted file mode 100644 index 65ba6ba..0000000 --- a/types_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package service - -import ( - "testing" -) - -func TestResult(t *testing.T) { - r := &Result{} - r.OK() - if !r.Ok { - t.Error("Result.OK() failed") - } - - r.Failed("error", Argot("ERR_CODE")) - if r.Ok || r.Message != "error" || r.Argot != "ERR_CODE" { - t.Error("Result.Failed() failed") - } - - r.Done(true, "never") - if !r.Ok { - t.Error("Result.Done(true) failed") - } - - r.Done(false, "failed", Argot("FAIL")) - if r.Ok || r.Message != "failed" || r.Argot != "FAIL" { - t.Error("Result.Done(false) failed") - } -} - -func TestCodeResult(t *testing.T) { - cr := &CodeResult{} - cr.OK() - if cr.Code != 1 { - t.Error("CodeResult.OK() failed") - } - - cr.Failed(500, "internal error") - if cr.Code != 500 || cr.Message != "internal error" { - t.Error("CodeResult.Failed() failed") - } -} diff --git a/utility.go b/utility.go index 293dcb0..7929e39 100644 --- a/utility.go +++ b/utility.go @@ -2,19 +2,60 @@ package service import ( "apigo.cc/go/id" + "apigo.cc/go/log" + "apigo.cc/go/redis" + "sync" ) +var ( + idMaker IDMakerInterface + idMakerLock sync.Mutex +) + +// IDMakerInterface ID 生成器接口 +type IDMakerInterface interface { + Get(size int) string + GetForMysql(size int) string + GetForPostgreSQL(size int) string +} + +func getIDMaker() IDMakerInterface { + if idMaker != nil { + return idMaker + } + + idMakerLock.Lock() + defer idMakerLock.Unlock() + + if idMaker != nil { + return idMaker + } + + if Config.IdServer != "" { + rd := redis.GetRedis(Config.IdServer, log.DefaultLogger) + if rd.Error == nil { + idMaker = redis.NewIDMaker(rd) + } + } + + if idMaker == nil { + idMaker = id.DefaultIDMaker + } + + return idMaker +} + // MakeId 生成指定长度的 ID func MakeId(size int) string { - return id.MakeID(size) + return getIDMaker().Get(size) } // MakeIdForMysql 生成适用于 MySQL 的有序 ID func MakeIdForMysql(size int) string { - return id.DefaultIDMaker.GetForMysql(size) + return getIDMaker().GetForMysql(size) } // MakeIdForPostgreSQL 生成适用于 PostgreSQL 的有序 ID func MakeIdForPostgreSQL(size int) string { - return id.DefaultIDMaker.GetForPostgreSQL(size) + return getIDMaker().GetForPostgreSQL(size) } diff --git a/websocket.go b/websocket.go index 323eb5d..4b8b770 100644 --- a/websocket.go +++ b/websocket.go @@ -1,97 +1,42 @@ package service import ( - "apigo.cc/go/cast" "apigo.cc/go/log" "github.com/gorilla/websocket" "net/http" "reflect" - "regexp" ) // websocketServiceType WebSocket 服务元数据 type websocketServiceType struct { - authLevel int - path string - pathMatcher *regexp.Regexp - pathArgs []string - updater *websocket.Upgrader - openFuncValue reflect.Value - openFuncType reflect.Type - closeFuncValue reflect.Value - closeFuncType reflect.Type - sessionType reflect.Type - actions map[string]*websocketActionType - isSimple bool - options WebServiceOptions - memo string -} - -// websocketActionType WebSocket Action 元数据 -type websocketActionType struct { - authLevel int - funcValue reflect.Value - funcType reflect.Type - inType reflect.Type - memo string -} - -// ActionRegister WebSocket Action 注册器 -type ActionRegister struct { - ws *websocketServiceType + authLevel int + path string + updater *websocket.Upgrader + handlerValue reflect.Value + handlerType reflect.Type + memo string } // RegisterWebsocket 注册 WebSocket 服务 -func RegisterWebsocket(authLevel int, path string, onOpen, onClose any, memo string) *ActionRegister { +func RegisterWebsocket(authLevel int, path string, handler any, memo string) { + v := reflect.ValueOf(handler) + t := v.Type() + if t.Kind() != reflect.Func { + return + } + s := &websocketServiceType{ - authLevel: authLevel, - path: path, - memo: memo, - updater: &websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}, - actions: make(map[string]*websocketActionType), - } - - if onOpen != nil { - s.openFuncValue = reflect.ValueOf(onOpen) - s.openFuncType = s.openFuncValue.Type() - if s.openFuncType.NumOut() > 0 { - s.sessionType = s.openFuncType.Out(0) - } - } - - if onClose != nil { - s.closeFuncValue = reflect.ValueOf(onClose) - s.closeFuncType = s.closeFuncValue.Type() + authLevel: authLevel, + path: path, + memo: memo, + updater: &websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}, + handlerValue: v, + handlerType: t, } websocketServicesLock.Lock() websocketServices[path] = s websocketServicesLock.Unlock() - - return &ActionRegister{ws: s} -} - -// RegisterAction 注册 WebSocket Action -func (ar *ActionRegister) RegisterAction(authLevel int, name string, action any, memo string) { - v := reflect.ValueOf(action) - t := v.Type() - a := &websocketActionType{ - authLevel: authLevel, - funcValue: v, - funcType: t, - memo: memo, - } - - // 查找输入参数类型 - for i := 0; i < t.NumIn(); i++ { - inT := t.In(i) - if inT.Kind() == reflect.Struct { - a.inType = inT - break - } - } - - ar.ws.actions[name] = a } func doWebsocketService(ws *websocketServiceType, request *Request, response *Response, logger *log.Logger) { @@ -102,74 +47,21 @@ func doWebsocketService(ws *websocketServiceType, request *Request, response *Re } defer conn.Close() - var session any - if ws.openFuncValue.IsValid() { - // 简化版:仅支持基础参数注入 - params := make([]reflect.Value, ws.openFuncType.NumIn()) - for i := 0; i < len(params); i++ { - t := ws.openFuncType.In(i) - if t == reflect.TypeOf(request) { - params[i] = reflect.ValueOf(request) - } else if t == reflect.TypeOf(logger) { - params[i] = reflect.ValueOf(logger) - } else { - params[i] = reflect.New(t).Elem() - } - } - outs := ws.openFuncValue.Call(params) - if len(outs) > 0 { - session = outs[0].Interface() + // 调用业务处理函数,注入依赖 + params := make([]reflect.Value, ws.handlerType.NumIn()) + for i := 0; i < len(params); i++ { + t := ws.handlerType.In(i) + if t == reflect.TypeOf(request) { + params[i] = reflect.ValueOf(request) + } else if t == reflect.TypeOf(logger) { + params[i] = reflect.ValueOf(logger) + } else if t == reflect.TypeOf(conn) { + params[i] = reflect.ValueOf(conn) + } else if obj := GetInject(t); obj != nil { + params[i] = reflect.ValueOf(obj) + } else { + params[i] = reflect.New(t).Elem() } } - - for { - var msg Map - if err := conn.ReadJSON(&msg); err != nil { - break - } - - actionName := cast.String(msg["action"]) - action := ws.actions[actionName] - if action == nil { - action = ws.actions[""] // 默认 action - } - - if action != nil { - params := make([]reflect.Value, action.funcType.NumIn()) - for i := 0; i < len(params); i++ { - t := action.funcType.In(i) - if t == ws.sessionType { - params[i] = reflect.ValueOf(session) - } else if t == reflect.TypeOf(conn) { - params[i] = reflect.ValueOf(conn) - } else if t.Kind() == reflect.Struct { - in := reflect.New(t).Interface() - cast.Convert(in, msg) - params[i] = reflect.ValueOf(in).Elem() - } else { - params[i] = reflect.New(t).Elem() - } - } - outs := action.funcValue.Call(params) - if len(outs) > 0 { - result := outs[0].Interface() - if result != nil { - _ = conn.WriteJSON(result) - } - } - } - } - - if ws.closeFuncValue.IsValid() { - params := make([]reflect.Value, ws.closeFuncType.NumIn()) - for i := 0; i < len(params); i++ { - t := ws.closeFuncType.In(i) - if t == ws.sessionType { - params[i] = reflect.ValueOf(session) - } else { - params[i] = reflect.New(t).Elem() - } - } - ws.closeFuncValue.Call(params) - } + ws.handlerValue.Call(params) } diff --git a/websocket_test.go b/websocket_test.go index 8689ff0..2456702 100644 --- a/websocket_test.go +++ b/websocket_test.go @@ -9,10 +9,15 @@ import ( func TestWebSocketService(t *testing.T) { // 注册 WebSocket 服务 - ar := RegisterWebsocket(0, "/ws", nil, nil, "test websocket") - ar.RegisterAction(0, "echo", func(in struct{ Msg string }) Map { - return Map{"action": "echo", "reply": in.Msg} - }, "echo action") + RegisterWebsocket(0, "/ws", func(conn *websocket.Conn) { + for { + var msg Map + if err := conn.ReadJSON(&msg); err != nil { + break + } + _ = conn.WriteJSON(Map{"reply": msg["msg"]}) + } + }, "test websocket") // 启动测试服务器 server := httptest.NewServer(&routeHandler{})