最新版本

This commit is contained in:
huangsimin@fusen.cn
2023-11-27 17:36:02 +08:00
parent da1f7d67b2
commit c0cbff775f
119 changed files with 19008 additions and 93 deletions

1
goutils/proto_build/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
proto_build

1425
goutils/proto_build/main.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
package main
import (
"testing"
)
func TestMain(t *testing.T) {
ServiceMain()
}
func TestGateway(t *testing.T) {
GatewayMain()
}

View File

@@ -0,0 +1,10 @@
package service
import (
"context"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
)

View File

@@ -0,0 +1,20 @@
package logic
import (
"context"
"{{.ProjectName}}/gen/go/service"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
)
func AutoRegisterHandler(ctx context.Context, mux *runtime.ServeMux, opts ...grpc.DialOption) error {
var err error
{{range .FuncNames}}
err = service.{{.}}(ctx, mux, opts...)
if err != nil {
return err
}
{{end}}
return nil
}

View File

@@ -0,0 +1,90 @@
package {{.PackageName}}
import (
"context"
"fmt"
"log"
"reflect"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
var namingClient naming_client.INamingClient
var groupName string
// AutoGrpcInit auto grpc 必须调用初始化
func AutoGrpcInit(obj any) {
value := reflect.ValueOf(obj)
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
groupName = value.FieldByName("GroupName").String()
for i := 0; i < value.NumField(); i++ {
v := value.Field(i)
if v.IsValid() {
_namingClient, ok := v.Interface().(naming_client.INamingClient)
if ok {
namingClient = _namingClient
}
}
}
}
{{range .ClientParams}}
func Auto{{.ClientName}}Client(ctx context.Context) {{.ClientName}}Client {
if namingClient == nil {
log.Println("nameClient must be init. call")
return nil
}
sel := vo.SelectOneHealthInstanceParam{
ServiceName: "{{.GrpcServiceName}}",
GroupName: groupName,
}
insService, err := namingClient.SelectOneHealthyInstance(sel)
if err != nil {
log.Println(err)
return nil
}
conn, err := grpc.DialContext(ctx, fmt.Sprintf("%s:%d", insService.Ip, insService.Port), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Println(err)
return nil
}
return New{{.ClientName}}Client(conn)
}
func Auto{{.ClientName}}ClientEx(ctx context.Context, opts ...grpc.DialOption) ({{.ClientName}}Client,error) {
if namingClient == nil {
return nil, fmt.Errorf("nameClient must be init. call")
}
sel := vo.SelectOneHealthInstanceParam{
ServiceName: "{{.GrpcServiceName}}",
GroupName: groupName,
}
insService, err := namingClient.SelectOneHealthyInstance(sel)
if err != nil {
return nil, err
}
conn, err := grpc.DialContext(ctx, fmt.Sprintf("%s:%d", insService.Ip, insService.Port), opts...)
if err != nil {
return nil, err
}
return New{{.ClientName}}Client(conn), nil
}
{{end}}

View File

@@ -0,0 +1,7 @@
package config
type Config struct {
Name string `yaml:"Name"`
Host string `yaml:"Host"`
Port uint `yaml:"Port"`
}

View File

@@ -0,0 +1,32 @@
# ---> Go
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
gen
__debug_*
runtime.*
vendor
screenlog.*

View File

@@ -0,0 +1,29 @@
package test
import (
"{{.ProjectName}}/gen/go/service"
"fusen-basic/utils/log"
"testing"
)
// 只需要填写参数
func init() {
{{.RequestVar}} = &service.{{.RequestStruct}}{}
}
func Test{{.MethodName}}HTTP(t *testing.T) {
//
resp, err := {{.MethodName}}Http(nil)
if err != nil {
t.Error(err)
}
log.Println(resp)
}
func Test{{.MethodName}}RPC(t *testing.T) {
resp, err := {{.MethodName}}RPC()
if err != nil {
t.Error(err)
}
log.Println(resp)
}

View File

@@ -0,0 +1,39 @@
package test
import (
"context"
"net/http"
"{{.ProjectName}}/gen/go/service"
"{{.ProjectName}}/server/config"
"fusen-basic/env"
)
{{range .HttpGrpcTestStructs}}
var {{.RequestVar}} *service.{{.RequestStruct}}
func {{.MethodName}}Http(reqHandler func(*http.Request) error) (any, error) {
resp, err := HttpRequest({{.HttpMethod}}, gatewayAddrress+{{.UrlPath}}, {{.RequestVar}}, reqHandler)
if err != nil {
// t.Error(err)
return nil, err
}
return resp, nil
}
func {{.MethodName}}RPC() (any, error) {
if fusen == nil {
fusen := env.NewFusenTest[config.Config]()
fusen.StartNacos(nil)
service.AutoGrpcInit(fusen)
}
resp, err := service.Auto{{.ServiceName}}Client(context.TODO()).{{.MethodName}}(context.TODO(), {{.RequestVar}})
if err != nil {
return nil, err
}
return resp, nil
}
{{end}}

View File

@@ -0,0 +1,103 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"{{.ProjectName}}/server/config"
"fusen-basic/env"
"fusen-basic/utils/log"
"net/http"
"net/url"
"reflect"
"strings"
)
var fusen *env.Fusen[config.Config]
var gatewayAddrress = "http://localhost:9900"
func HttpRequest(method, urlStr string, body interface{}, reqHandler func(*http.Request) error) (map[string]interface{}, error) {
var jsonBody *bytes.Buffer
var queryParams url.Values
if method == "GET" {
queryParams = JsonTagToURLValues(body)
jsonBody = bytes.NewBuffer(nil)
} else {
// JSON格式
jsonData, err := json.Marshal(body)
if err != nil {
log.Printf("Failed to marshal request body: %s\n", err.Error())
return nil, err
}
jsonBody = bytes.NewBuffer(jsonData)
log.Println(string(jsonData))
}
// URL对象并添加查询参数
u, err := url.Parse(urlStr)
if err != nil {
fmt.Printf("Failed to parse URL: %s\n", err.Error())
return nil, err
}
u.RawQuery = queryParams.Encode()
// HTTP请求
req, err := http.NewRequest(method, u.String(), jsonBody)
if err != nil {
fmt.Printf("Failed to create HTTP request: %s\n", err.Error())
return nil, err
}
//
req.Header.Set("Content-Type", "application/json")
if reqHandler != nil {
reqHandler(req)
}
// HTTP请求
client := http.DefaultClient
client.Timeout = time.Second * 60
resp, err := client.Do(req)
if err != nil {
fmt.Printf("HTTP request failed: %s\n", err.Error())
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("%s", resp.Status)
}
defer resp.Body.Close()
//
var response map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
fmt.Printf("Failed to decode response body: %s\n", err.Error())
return nil, err
}
return response, nil
}
func JsonTagToURLValues(body interface{}) url.Values {
values := url.Values{}
v := reflect.ValueOf(body)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
t := v.Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if field.IsExported() {
if jsonTag, ok := field.Tag.Lookup("json"); ok {
jtag := strings.Split(jsonTag, ",")[0]
value := v.Field(i).String()
values.Set(jtag, value)
}
}
}
return values
}

View File

@@ -0,0 +1,16 @@
package {{.PackageName}}
import (
"fusen-basic/basic"
"{{.ProjectName}}/gen/go/service"
)
func (l *{{.StructName}}) {{.MethodName}}Logic(req {{.ParamReq}}, userinfo *basic.UserInfo) (resp *basic.Response[{{.MethodResponse}}]) {
return resp.Set(basic.CodeOK)
}
// 自定义验证校验方法 VaildUserInfoHandler
// func (h *{{.MethodName}}Handler) VaildUserInfoHandler(ctx context.Context) *basic.UserInfo {
// return basic.ValidUserInfo(ctx)
// }

View File

@@ -0,0 +1,126 @@
// 生成代码, 不要在这里添加任何逻辑
package {{.PackageName}}
import (
"context"
"strings"
"sync"
"fusen-basic/basic"
"fusen-basic/env"
"fusen-basic/utils/log"
"{{.ProjectName}}/gen/go/service"
"{{.ProjectName}}/server/config"
"google.golang.org/grpc/metadata"
)
type {{.StructName}} struct {
ctx context.Context
cfg *config.Config
svcCtx *SharedContext
}
func New{{.StructName}}(ctx context.Context) *{{.StructName}} {
l := &{{.StructName}}{}
l.ctx = ctx //
l.cfg = ctx.Value(env.CtxServiceConfig{}).(*config.Config) //
l.svcCtx = svcCtx
onceSharedContext.Do(func() {
svcCtx.Init(ctx, l.cfg)
})
l.svcCtx.Handler(ctx, l.cfg)
return l
}
var onceSharedContext sync.Once
var svcCtx = &SharedContext{}
type {{.StructName}}Grpc struct {
{{.UnimplementedStructName}}
}
{{range .Methods}}
type {{.MethodName}}Handler struct {
{{.MethodName}}HandlerMust
}
type {{.MethodName}}HandlerMust struct{}
func (must *{{.MethodName}}HandlerMust) ValidUserInfoHandler(ctx context.Context) *basic.UserInfo {
return defaultValidUserInfoHandler(ctx)
}
var __hide_handler{{.MethodName}} *{{.MethodName}}Handler = &{{.MethodName}}Handler{}
func (lgrpc *{{.StructName}}Grpc) {{.MethodName}}(ctx {{.ParamCtx}}, req {{.ParamReq}}) ({{.MethodReturn}}, error) {
return New{{.StructName}}(ctx).{{.MethodName}}Logic(req, __hide_handler{{.MethodName}}.ValidUserInfoHandler(ctx)).PassMetaResponse(ctx)
}
{{end}}
func DefaultValidToken(ctx context.Context) *basic.UserInfo {
var (
err error
ui = &basic.UserInfo{}
)
// log.Println(ctx)
if md, ok := metadata.FromIncomingContext(ctx); ok {
var authtoken, debugtoken *string
var vresult *service.ValidTokenResponse
if a, ok := md["authorization"]; ok {
token := a[0]
if len(token) > 15 {
if strings.HasPrefix(token[1:], "earer ") {
token = token[7:]
}
authtoken = &token
defer func() {
if vresult != nil {
userinfo := vresult.UserInfo.AsMap()
ui.UserId = int64(userinfo["user_id"].(float64))
ui.GuestId = int64(userinfo["guest_id"].(float64))
ui.Exp = int64(userinfo["exp"].(float64))
}
}()
}
}
if a, ok := md["debug-token"]; ok {
token := a[0]
if len(token) > 15 {
if strings.HasPrefix(token[1:], "earer ") {
token = token[7:]
}
debugtoken = &token
defer func() {
if vresult != nil {
debug := vresult.DebugInfo.AsMap()
ui.Debug = &basic.Debug{}
if dexp, ok := debug["exp"]; ok {
dexpint64 := int64(dexp.(float64))
ui.Debug.Exp = &dexpint64
}
ui.Debug.IsAllTemplateTag = int64(debug["is_all_template_tag"].(float64))
ui.Debug.IsCache = int64(debug["is_cache"].(float64))
}
}()
}
}
vresult, err = service.AutoAuthClient(ctx).ValidToken(context.TODO(), &service.ValidTokenRequest{
UserToken: authtoken,
DebugToken: debugtoken,
})
if err != nil {
log.Println(err.Error())
}
}
return ui
}

View File

@@ -0,0 +1,28 @@
// 当文件不存在的时候生成代码, 在这里添加逻辑
package {{.PackageName}}
import (
"context"
"fusen-basic/basic"
"fusen-model/dbutils"
"fusen-model/gmodel"
"{{.ProjectName}}/server/config"
)
// 统一的验证入口
var defaultValidUserInfoHandler func(context.Context) *basic.UserInfo = DefaultValidToken
// 处理共享数据的上下文
type SharedContext struct {
models *gmodel.Models
}
// 初始化
func (svcCtx *SharedContext) Init(ctx context.Context, cfg *config.Config) {
svcCtx.models = gmodel.NewModels(dbutils.InitMysql(cfg.SourceMysql)) // gmodel
}
// 每次请求需要处理的上下文.
func (svcCtx *SharedContext) Handler(ctx context.Context, cfg *config.Config) {
}

View File

@@ -0,0 +1,40 @@
package main
import (
"fmt"
"net"
"fusen-basic/env"
"fusen-basic/utils/log"
"{{.ProjectName}}/gen/go/service"
"{{.ProjectName}}/server/config"
{{range .LogicDirNames}}
"{{$.ProjectName}}/{{.}}"
{{- end}}
"google.golang.org/grpc"
)
func main() {
fusen := env.NewFusen[config.Config]()
fusen.StartNacos(nil)
service.AutoGrpcInit(fusen)
sopt := grpc.UnaryInterceptor(fusen.Interceptor())
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", fusen.ServiceConfig.Port)) //
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer(sopt) //grpc服务
{{range .StructServiceNames}}
service.Register{{.StructServiceName}}Server(s, &{{.LogicPackageName}}.{{.StructServiceName}}LogicGrpc{}) // {{.LogicPackageName}}
{{- end}}
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

View File

@@ -0,0 +1,9 @@
package main
import (
"testing"
)
func TestMain(t *testing.T) {
main()
}

View File

@@ -0,0 +1,58 @@
#! /bin/bash
# 获取当前脚本的全局路径
script_path=$(realpath "$0")
# 获取当前脚本的目录
script_dir=$(dirname "$script_path")
# 执行更新最新的包
$script_dir/update_fspkg_master.sh
# 拼接service_config.ini的路径
config_file="$script_dir/server/service_config.ini"
# 检查文件是否存在
if [[ ! -f "$config_file" ]]; then
echo "Error: service_config.ini 文件不存在"
exit 1
fi
echo "read $config_file"
service_name=$(grep "SERVICE_NAME=" $config_file | cut -d'=' -f2 | cut -d' ' -f1)
# 输出变量值
echo "service name: $service_name"
cd $script_dir/proto
git pull origin master
cd $script_dir
go run -gcflags="-N" proto/goutils/proto_build/main.go
run_server() {
#
cd $script_dir/server
echo "build $service_name"
go build -o $service_name
# screen
# screen会话
existing_session=$(screen -ls | grep -w "$service_name")
if [ -n "$existing_session" ]; then
echo "Terminating existing screen session for $service_name"
screen -S "$service_name" -X quit
while [[ $(screen -ls | grep "\.$service_name\s") ]]; do
sleep 0.1s # 0.1
echo "wait for $service_name"
done
fi
# screen进程是否存在
[ -f .gitignore ] || (echo "server" > .gitignore && echo "$service_name" >> .gitignore && echo "main" >> .gitignore)
echo "Running $service_name"
screen -dmS $service_name -L ./$service_name
}
run_server

View File

@@ -0,0 +1,2 @@
SERVICE_NAME= #需要修改当前项目的名字
PROJECT_NAME=gitee.com/fusenpack/ #需要修改项目的仓库

View File

@@ -0,0 +1,25 @@
#! /bin/bash
input_string=$(cat go.mod)
# 使用sed命令提取参数并替换输出格式
output=$(echo "$input_string" | sed -nE 's/[[:space:]]{0,}(replace[[:space:]]+)?([[:alnum:]-]+)[[:space:]]+([[:alnum:].-]+)[[:space:]]+=>[[:space:]]+([[:alnum:]/.-]+)[[:space:]]+([[:alnum:].-]+)/\2 \3 \4 \5/p')
IFS=$'\n' # 设置分隔符为换行符
for out in $output; do
IFS=$' '
read -r fsname fsversion fsfullname fslatest_master <<< "$out"
laster_master=`echo $(GOPROXY=direct go get $fsfullname@master 2>&1) | grep -oE '@v[^:"]+'`
echo "go mod edit -replace $fsname@$fsversion=$fsfullname$laster_master"
go mod edit -replace $fsname@$fsversion=$fsfullname$laster_master
done
IFS=$'\n' # 设置分隔符为换行符
for out in $output; do
IFS=$' '
read -r fsname fsversion fsfullname fslatest_master <<< "$out"
go get $fsname@$fsversion
done
go mod tidy
go mod vendor