Slim down service module: remove redundant features and optimize ID generation (by AI)
This commit is contained in:
parent
bdb104aa2f
commit
5b63fd83a9
54
README.md
54
README.md
@ -3,10 +3,11 @@
|
|||||||
极简、自动化的 Web 与 WebSocket 服务框架,实现极致的依赖注入与路由映射。
|
极简、自动化的 Web 与 WebSocket 服务框架,实现极致的依赖注入与路由映射。
|
||||||
|
|
||||||
## 核心特性
|
## 核心特性
|
||||||
|
- **极致精简**: 剥离非核心组件(如 Starter, Task, 业务 Result 定义),保持底座纯净。
|
||||||
- **路由反射**: 自动解析函数参数,支持 `*Request`, `*Response`, `*log.Logger` 及自定义结构体自动注入。
|
- **路由反射**: 自动解析函数参数,支持 `*Request`, `*Response`, `*log.Logger` 及自定义结构体自动注入。
|
||||||
- **自动校验**: 集成 `verify` 引擎,通过 Struct Tag 实现入参合法性自动检查。
|
- **自动校验**: 集成 `verify` 引擎,通过 Struct Tag 实现入参合法性自动检查。
|
||||||
- **功能闭环**: 内置静态文件服务、WebSocket (带 Action 路由)、URL 重写、反向代理(对接 Discover)。
|
- **功能闭环**: 内置静态文件服务、基础 WebSocket 注册、URL 重写、反向代理(对接 Discover)。
|
||||||
- **零摩擦启动**: 支持命令行指令管理 (start/stop/help) 及异步平滑启停。
|
- **统一 ID 体系**: 整合 Redis 集群版 ID 生成器,全局统一生成 `RequestId` 与 `LogTraceID`。
|
||||||
|
|
||||||
## API 指南
|
## API 指南
|
||||||
|
|
||||||
@ -14,39 +15,42 @@
|
|||||||
```go
|
```go
|
||||||
import "apigo.cc/go/service"
|
import "apigo.cc/go/service"
|
||||||
|
|
||||||
// 注册标准 Web 服务
|
// 注册标准 Web 服务,自动注入 Struct 参数并执行校验
|
||||||
service.Register(0, "/hello", func(in struct{ Name string }) string {
|
service.Register(0, "/hello", func(in struct{ Name string `verify:"length:2+"` }) string {
|
||||||
return "Hello " + in.Name
|
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
|
```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")`
|
- **静态文件**: `service.Static("/ui", "./static_dir")`
|
||||||
- **URL 重写**: `service.Rewrite("/old", "/new")`
|
- **URL 重写**: `service.Rewrite("/old", "/new")`
|
||||||
- **反向代理**: `service.Proxy(0, "/api", "other_app", "/api")`
|
- **反向代理**: `service.Proxy(0, "/api", "other_app", "/api")`
|
||||||
|
- **文档生成**: `service.MakeDocument()` 返回全量接口描述
|
||||||
### 4. 生命周期管理
|
|
||||||
```go
|
|
||||||
func main() {
|
|
||||||
service.CheckCmd() // 处理 start/stop/help 指令
|
|
||||||
service.Start() // 阻塞启动
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 基础设施对齐
|
## 基础设施对齐
|
||||||
- **类型转换**: `apigo.cc/go/cast`
|
- **类型转换**: `apigo.cc/go/cast`
|
||||||
|
|||||||
29
document.go
29
document.go
@ -77,10 +77,10 @@ func MakeDocument() []Api {
|
|||||||
AuthLevel: a.authLevel,
|
AuthLevel: a.authLevel,
|
||||||
Memo: a.memo,
|
Memo: a.memo,
|
||||||
}
|
}
|
||||||
if a.openFuncType != nil && a.openFuncType.NumIn() > 0 {
|
if a.handlerType != nil && a.handlerType.NumIn() > 0 {
|
||||||
// Find struct in
|
// Find struct in
|
||||||
for i := 0; i < a.openFuncType.NumIn(); i++ {
|
for i := 0; i < a.handlerType.NumIn(); i++ {
|
||||||
t := a.openFuncType.In(i)
|
t := a.handlerType.In(i)
|
||||||
if t.Kind() == reflect.Struct {
|
if t.Kind() == reflect.Struct {
|
||||||
api.In = getType(t)
|
api.In = getType(t)
|
||||||
break
|
break
|
||||||
@ -88,22 +88,6 @@ func MakeDocument() []Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out = append(out, 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()
|
websocketServicesLock.RUnlock()
|
||||||
|
|
||||||
@ -153,10 +137,3 @@ func getType(t reflect.Type) any {
|
|||||||
return t.String()
|
return t.String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动注册文档服务
|
|
||||||
func init() {
|
|
||||||
Register(0, "/__DOC__", func() string {
|
|
||||||
return MakeJsonDocument()
|
|
||||||
}, "API Document")
|
|
||||||
}
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"apigo.cc/go/cast"
|
"apigo.cc/go/cast"
|
||||||
"apigo.cc/go/id"
|
|
||||||
"apigo.cc/go/log"
|
"apigo.cc/go/log"
|
||||||
"apigo.cc/go/standard"
|
"apigo.cc/go/standard"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -25,7 +24,7 @@ func (rh *routeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
requestId := r.Header.Get(standard.DiscoverHeaderRequestId)
|
requestId := r.Header.Get(standard.DiscoverHeaderRequestId)
|
||||||
if requestId == "" {
|
if requestId == "" {
|
||||||
requestId = id.MakeID(12)
|
requestId = MakeId(12)
|
||||||
r.Header.Set(standard.DiscoverHeaderRequestId, requestId)
|
r.Header.Set(standard.DiscoverHeaderRequestId, requestId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +277,7 @@ func handleClientKeys(request *Request, response *Response) {
|
|||||||
if sessionIdMaker != nil {
|
if sessionIdMaker != nil {
|
||||||
sessionId = sessionIdMaker()
|
sessionId = sessionIdMaker()
|
||||||
} else {
|
} else {
|
||||||
sessionId = id.MakeID(14)
|
sessionId = MakeId(14)
|
||||||
}
|
}
|
||||||
if !Config.SessionWithoutCookie {
|
if !Config.SessionWithoutCookie {
|
||||||
http.SetCookie(response.Writer, &http.Cookie{
|
http.SetCookie(response.Writer, &http.Cookie{
|
||||||
@ -302,7 +301,7 @@ func handleClientKeys(request *Request, response *Response) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if deviceId == "" {
|
if deviceId == "" {
|
||||||
deviceId = id.MakeID(14)
|
deviceId = MakeId(14)
|
||||||
if !Config.DeviceWithoutCookie {
|
if !Config.DeviceWithoutCookie {
|
||||||
http.SetCookie(response.Writer, &http.Cookie{
|
http.SetCookie(response.Writer, &http.Cookie{
|
||||||
Name: usedDeviceIdKey,
|
Name: usedDeviceIdKey,
|
||||||
|
|||||||
@ -9,6 +9,12 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Map 通用 Map 类型
|
||||||
|
type Map = map[string]any
|
||||||
|
|
||||||
|
// Arr 通用切片类型
|
||||||
|
type Arr = []any
|
||||||
|
|
||||||
// WebServiceOptions 服务注册选项
|
// WebServiceOptions 服务注册选项
|
||||||
type WebServiceOptions struct {
|
type WebServiceOptions struct {
|
||||||
Priority int
|
Priority int
|
||||||
@ -58,9 +64,8 @@ var (
|
|||||||
webServicesList = make([]*webServiceType, 0)
|
webServicesList = make([]*webServiceType, 0)
|
||||||
|
|
||||||
websocketServices = make(map[string]*websocketServiceType)
|
websocketServices = make(map[string]*websocketServiceType)
|
||||||
regexWebsocketServices = make([]*websocketServiceType, 0)
|
|
||||||
websocketServicesLock = sync.RWMutex{}
|
websocketServicesLock = sync.RWMutex{}
|
||||||
websocketServicesList = make([]*websocketServiceType, 0)
|
websocketServicesList = make([]*webServiceType, 0)
|
||||||
|
|
||||||
// 过滤器与拦截器
|
// 过滤器与拦截器
|
||||||
inFilters = make([]func(*map[string]any, *Request, *Response, *log.Logger) any, 0)
|
inFilters = make([]func(*map[string]any, *Request, *Response, *log.Logger) any, 0)
|
||||||
|
|||||||
49
starter.go
49
starter.go
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
68
types.go
68
types.go
@ -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
|
|
||||||
}
|
|
||||||
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
47
utility.go
47
utility.go
@ -2,19 +2,60 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"apigo.cc/go/id"
|
"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
|
// MakeId 生成指定长度的 ID
|
||||||
func MakeId(size int) string {
|
func MakeId(size int) string {
|
||||||
return id.MakeID(size)
|
return getIDMaker().Get(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeIdForMysql 生成适用于 MySQL 的有序 ID
|
// MakeIdForMysql 生成适用于 MySQL 的有序 ID
|
||||||
func MakeIdForMysql(size int) string {
|
func MakeIdForMysql(size int) string {
|
||||||
return id.DefaultIDMaker.GetForMysql(size)
|
return getIDMaker().GetForMysql(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeIdForPostgreSQL 生成适用于 PostgreSQL 的有序 ID
|
// MakeIdForPostgreSQL 生成适用于 PostgreSQL 的有序 ID
|
||||||
func MakeIdForPostgreSQL(size int) string {
|
func MakeIdForPostgreSQL(size int) string {
|
||||||
return id.DefaultIDMaker.GetForPostgreSQL(size)
|
return getIDMaker().GetForPostgreSQL(size)
|
||||||
}
|
}
|
||||||
|
|||||||
142
websocket.go
142
websocket.go
@ -1,97 +1,42 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"apigo.cc/go/cast"
|
|
||||||
"apigo.cc/go/log"
|
"apigo.cc/go/log"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// websocketServiceType WebSocket 服务元数据
|
// websocketServiceType WebSocket 服务元数据
|
||||||
type websocketServiceType struct {
|
type websocketServiceType struct {
|
||||||
authLevel int
|
authLevel int
|
||||||
path string
|
path string
|
||||||
pathMatcher *regexp.Regexp
|
|
||||||
pathArgs []string
|
|
||||||
updater *websocket.Upgrader
|
updater *websocket.Upgrader
|
||||||
openFuncValue reflect.Value
|
handlerValue reflect.Value
|
||||||
openFuncType reflect.Type
|
handlerType reflect.Type
|
||||||
closeFuncValue reflect.Value
|
|
||||||
closeFuncType reflect.Type
|
|
||||||
sessionType reflect.Type
|
|
||||||
actions map[string]*websocketActionType
|
|
||||||
isSimple bool
|
|
||||||
options WebServiceOptions
|
|
||||||
memo string
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterWebsocket 注册 WebSocket 服务
|
// 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{
|
s := &websocketServiceType{
|
||||||
authLevel: authLevel,
|
authLevel: authLevel,
|
||||||
path: path,
|
path: path,
|
||||||
memo: memo,
|
memo: memo,
|
||||||
updater: &websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }},
|
updater: &websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }},
|
||||||
actions: make(map[string]*websocketActionType),
|
handlerValue: v,
|
||||||
}
|
handlerType: t,
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
websocketServicesLock.Lock()
|
websocketServicesLock.Lock()
|
||||||
websocketServices[path] = s
|
websocketServices[path] = s
|
||||||
websocketServicesLock.Unlock()
|
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) {
|
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()
|
defer conn.Close()
|
||||||
|
|
||||||
var session any
|
// 调用业务处理函数,注入依赖
|
||||||
if ws.openFuncValue.IsValid() {
|
params := make([]reflect.Value, ws.handlerType.NumIn())
|
||||||
// 简化版:仅支持基础参数注入
|
|
||||||
params := make([]reflect.Value, ws.openFuncType.NumIn())
|
|
||||||
for i := 0; i < len(params); i++ {
|
for i := 0; i < len(params); i++ {
|
||||||
t := ws.openFuncType.In(i)
|
t := ws.handlerType.In(i)
|
||||||
if t == reflect.TypeOf(request) {
|
if t == reflect.TypeOf(request) {
|
||||||
params[i] = reflect.ValueOf(request)
|
params[i] = reflect.ValueOf(request)
|
||||||
} else if t == reflect.TypeOf(logger) {
|
} else if t == reflect.TypeOf(logger) {
|
||||||
params[i] = reflect.ValueOf(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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
} else if t == reflect.TypeOf(conn) {
|
||||||
params[i] = reflect.ValueOf(conn)
|
params[i] = reflect.ValueOf(conn)
|
||||||
} else if t.Kind() == reflect.Struct {
|
} else if obj := GetInject(t); obj != nil {
|
||||||
in := reflect.New(t).Interface()
|
params[i] = reflect.ValueOf(obj)
|
||||||
cast.Convert(in, msg)
|
|
||||||
params[i] = reflect.ValueOf(in).Elem()
|
|
||||||
} else {
|
} else {
|
||||||
params[i] = reflect.New(t).Elem()
|
params[i] = reflect.New(t).Elem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
outs := action.funcValue.Call(params)
|
ws.handlerValue.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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,10 +9,15 @@ import (
|
|||||||
|
|
||||||
func TestWebSocketService(t *testing.T) {
|
func TestWebSocketService(t *testing.T) {
|
||||||
// 注册 WebSocket 服务
|
// 注册 WebSocket 服务
|
||||||
ar := RegisterWebsocket(0, "/ws", nil, nil, "test websocket")
|
RegisterWebsocket(0, "/ws", func(conn *websocket.Conn) {
|
||||||
ar.RegisterAction(0, "echo", func(in struct{ Msg string }) Map {
|
for {
|
||||||
return Map{"action": "echo", "reply": in.Msg}
|
var msg Map
|
||||||
}, "echo action")
|
if err := conn.ReadJSON(&msg); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
_ = conn.WriteJSON(Map{"reply": msg["msg"]})
|
||||||
|
}
|
||||||
|
}, "test websocket")
|
||||||
|
|
||||||
// 启动测试服务器
|
// 启动测试服务器
|
||||||
server := httptest.NewServer(&routeHandler{})
|
server := httptest.NewServer(&routeHandler{})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user