Browse Source

添加mono项目

yjp 1 year ago
parent
commit
68acee3e20

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module baize-demo
 go 1.22.3
 
 require (
-	git.sxidc.com/go-framework/baize v0.8.0
+	git.sxidc.com/go-framework/baize v0.8.1
 	git.sxidc.com/go-tools/utils v1.5.15
 	git.sxidc.com/service-supports/fserr v0.3.5
 	git.sxidc.com/service-supports/fslog v0.5.9

+ 2 - 2
go.sum

@@ -1,5 +1,5 @@
-git.sxidc.com/go-framework/baize v0.8.0 h1:KqJs+GKIjkdwHXNaxF2HoxOC7sBI5YZ7/E+EUlfUFuI=
-git.sxidc.com/go-framework/baize v0.8.0/go.mod h1:xE4XwsvwSO8tR5j8BaVZUTngU+HLNICHwdl5x1LULa0=
+git.sxidc.com/go-framework/baize v0.8.1 h1:99FEhI66oQwR/hxrhMl7BnTE0mCNBJ2mbDC/mX9aivk=
+git.sxidc.com/go-framework/baize v0.8.1/go.mod h1:xE4XwsvwSO8tR5j8BaVZUTngU+HLNICHwdl5x1LULa0=
 git.sxidc.com/go-tools/utils v1.5.15 h1:7xs/EM8XZyKycrSSHcPZ6wvyYs+v8uWQ7ZmPP/fHyFI=
 git.sxidc.com/go-tools/utils v1.5.15/go.mod h1:fkobAXFpOMTvkZ82TQXWcpsayePcyk/MS5TN6GTlRDg=
 git.sxidc.com/service-supports/fserr v0.3.5 h1:1SDC60r3FIDd2iRq/oHRLK4OMa1gf67h9B7kierKTUE=

+ 70 - 0
project/mono/application/application.go

@@ -0,0 +1,70 @@
+package application
+
+import (
+	"baize-demo/project/mono/application/service"
+	"baize-demo/project/mono/config"
+	"git.sxidc.com/go-framework/baize"
+	"git.sxidc.com/go-framework/baize/framework/core/api"
+	"git.sxidc.com/go-framework/baize/framework/core/application"
+	"net/http"
+)
+
+var appInstance *application.App
+
+func NewApp() {
+	if appInstance != nil {
+		return
+	}
+
+	appInstance = baize.NewApplication(config.GetConfig().ApplicationConfig)
+
+	// 注册Router
+	appInstance.Api().PrefixRouter().RegisterVersionedRouter("v1")
+}
+
+func DestroyApp() {
+	if appInstance == nil {
+		return
+	}
+
+	baize.DestroyApplication(appInstance)
+	appInstance = nil
+}
+
+func Start() error {
+	// 初始化服务
+	for _, svc := range service.RegisteredServices {
+		err := svc.Init(appInstance)
+		if err != nil {
+			return err
+		}
+	}
+
+	err := appInstance.Start()
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func Finish() error {
+	err := appInstance.Finish()
+	if err != nil {
+		return err
+	}
+
+	// 销毁服务
+	for _, svc := range service.RegisteredServices {
+		err := svc.Destroy()
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func ServerHttpForTest(w http.ResponseWriter, req *http.Request) {
+	appInstance.Api().RootRouter().(*api.RootRouter).ServerHttp(w, req)
+}

+ 98 - 0
project/mono/application/domain/class/entity.go

@@ -0,0 +1,98 @@
+package class
+
+import (
+	"git.sxidc.com/go-framework/baize/framework/core/domain"
+	"git.sxidc.com/go-framework/baize/framework/core/domain/entity"
+	"git.sxidc.com/go-framework/baize/framework/core/tag/check"
+	"git.sxidc.com/go-tools/utils/strutils"
+	"git.sxidc.com/service-supports/fserr"
+)
+
+const (
+	FieldName       = "Name"
+	FieldStudentNum = "StudentNum"
+)
+
+var (
+	ColumnName       = domain.ColumnName(FieldName)
+	ColumnStudentNum = domain.ColumnName(FieldStudentNum)
+)
+
+var fieldMap = map[string]string{
+	FieldName:       "班名",
+	FieldStudentNum: "学生数量",
+}
+
+type Entity struct {
+	entity.Base
+	Name       string   `sqlmapping:"column:name" sqlresult:"column:name" check:"required,lte=128"`
+	StudentNum int      `sqlmapping:"column:student_num;updateClear;" sqlresult:"column:student_num"`
+	StudentIDs []string `sqlmapping:"-" sqlresult:"-"`
+	entity.TimeFields
+}
+
+func (e *Entity) DomainCNName() string {
+	return "班级"
+}
+
+func (e *Entity) DomainCamelName() string {
+	return "Class"
+}
+
+func (e *Entity) ForCreate() error {
+	checkResult := check.Struct(e, fieldMap)
+
+	err := entity.CheckFieldIDResult(checkResult)
+	if err != nil {
+		return err
+	}
+
+	err = domain.CheckField(checkResult, e.DomainCNName(), FieldName)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (e *Entity) ForDelete() error {
+	checkResult := check.Struct(e, fieldMap)
+
+	err := entity.CheckFieldIDResult(checkResult)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (e *Entity) ForUpdate() error {
+	checkResult := check.Struct(e, fieldMap)
+
+	err := entity.CheckFieldIDResult(checkResult)
+	if err != nil {
+		return err
+	}
+
+	err = e.checkUpdateFields(checkResult)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (e *Entity) checkUpdateFields(checkResult check.Result) error {
+	if strutils.AllBlank(e.Name) {
+		return fserr.New(e.DomainCNName() + "没有传递需要更新的字段")
+	}
+
+	if strutils.IsStringNotEmpty(e.Name) {
+		err := domain.CheckField(checkResult, e.DomainCNName(), FieldName)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 12 - 0
project/mono/application/domain/class/info.go

@@ -0,0 +1,12 @@
+package class
+
+import (
+	"git.sxidc.com/go-framework/baize/framework/core/application"
+)
+
+type Info struct {
+	application.InfoIDField
+	Name       string `json:"name" sqlresult:"column:name"`
+	StudentNum int    `json:"studentNum" sqlresult:"column:student_num"`
+	application.InfoTimeFields
+}

+ 41 - 0
project/mono/application/domain/class/request_params.go

@@ -0,0 +1,41 @@
+package class
+
+import (
+	"git.sxidc.com/go-framework/baize/framework/core/api/request"
+)
+
+type (
+	CreateJsonBody struct {
+		Name       string `json:"name" binding:"required" assign:"toField:Name"`
+		StudentNum int    `json:"studentNum" binding:"required" assign:"toField:StudentNum"`
+	}
+
+	DeleteQueryParams struct {
+		request.IDQueryParam
+	}
+
+	UpdateJsonBody struct {
+		request.IDJsonBody
+		Name       string `json:"name" assign:"toField:Name"`
+		StudentNum int    `json:"studentNum" assign:"toField:StudentNum"`
+	}
+
+	QueryQueryParams struct {
+		request.BaseQueryParams
+		Name       string `form:"name" assign:"toField:Name"`
+		StudentNum int    `form:"studentNum" assign:"toField:StudentNum"`
+	}
+
+	GetByIDQueryParams struct {
+		request.IDQueryParam
+	}
+
+	UpdateStudentsOfClassJsonBody struct {
+		request.IDJsonBody
+		StudentIDs []string `json:"studentIds" assign:"toField:StudentIDs"`
+	}
+
+	QueryStudentsOfClassQueryParams struct {
+		request.BaseQueryWithIDParams
+	}
+)

+ 36 - 0
project/mono/application/service/class.go

@@ -0,0 +1,36 @@
+package service
+
+import (
+	"baize-demo/project/mono/application/domain/class"
+	"git.sxidc.com/go-framework/baize/convenient/entity_crud"
+	"git.sxidc.com/go-framework/baize/framework/binding"
+	"git.sxidc.com/go-framework/baize/framework/core/api"
+	"git.sxidc.com/go-framework/baize/framework/core/application"
+)
+
+var classService = &ClassService{}
+
+type ClassService struct{}
+
+func (svc *ClassService) Init(appInstance *application.App) error {
+	svc.v1(appInstance)
+	return nil
+}
+
+func (svc *ClassService) Destroy() error {
+	return nil
+}
+
+func (svc *ClassService) v1(appInstance *application.App) {
+	v1Binder := binding.NewBinder(appInstance.ChooseRouter(api.RouterPrefix, "v1"), appInstance.Infrastructure())
+
+	entity_crud.BindSimple[class.Info](v1Binder, &entity_crud.Simple[class.Info]{
+		Entity:             &class.Entity{},
+		Schema:             dbSchema,
+		CreateJsonBody:     &class.CreateJsonBody{},
+		DeleteQueryParams:  &class.DeleteQueryParams{},
+		UpdateJsonBody:     &class.UpdateJsonBody{},
+		QueryQueryParams:   &class.QueryQueryParams{},
+		GetByIDQueryParams: &class.GetByIDQueryParams{},
+	})
+}

+ 17 - 0
project/mono/application/service/service.go

@@ -0,0 +1,17 @@
+package service
+
+import "git.sxidc.com/go-framework/baize/framework/core/application"
+
+const (
+	dbSchema = "test"
+)
+
+type Service interface {
+	Init(appInstance *application.App) error
+	Destroy() error
+}
+
+var RegisteredServices = []Service{
+	versionService,
+	classService,
+}

+ 58 - 0
project/mono/application/service/version.go

@@ -0,0 +1,58 @@
+package service
+
+import (
+	"git.sxidc.com/go-framework/baize/framework/binding"
+	"git.sxidc.com/go-framework/baize/framework/core/api"
+	"git.sxidc.com/go-framework/baize/framework/core/api/request"
+	"git.sxidc.com/go-framework/baize/framework/core/api/response"
+	"git.sxidc.com/go-framework/baize/framework/core/application"
+	"git.sxidc.com/go-framework/baize/framework/core/domain"
+	"git.sxidc.com/go-framework/baize/framework/core/infrastructure"
+	"git.sxidc.com/go-tools/utils/strutils"
+	"git.sxidc.com/service-supports/fserr"
+)
+
+var versionService = &VersionService{}
+
+type VersionService struct{}
+
+func (svc *VersionService) Init(appInstance *application.App) error {
+	svc.prefixRoot(appInstance)
+	return nil
+}
+
+func (svc *VersionService) Destroy() error {
+	return nil
+}
+
+func (svc *VersionService) prefixRoot(appInstance *application.App) {
+	prefixRootBinder := binding.NewBinder(appInstance.ChooseRouter(api.RouterPrefix, ""), appInstance.Infrastructure())
+
+	binding.GetBind(prefixRootBinder, &binding.SimpleBindItem[map[string]any]{
+		Path:             "/version",
+		SendResponseFunc: response.SendMapResponse,
+		ServiceFunc: func(c *api.Context, params request.Params, objects []domain.Object, i *infrastructure.Infrastructure) (map[string]any, error) {
+			localCacheVersion, err := i.LocalCache().Get("version")
+			if err != nil {
+				return nil, err
+			}
+
+			redisCacheVersion, err := i.RedisCache().Get("version")
+			if err != nil {
+				return nil, err
+			}
+
+			if localCacheVersion != redisCacheVersion {
+				return nil, fserr.New("版本不一致")
+			}
+
+			if strutils.IsStringEmpty(localCacheVersion) {
+				localCacheVersion = "cache-empty-v1.0.0"
+			}
+
+			return map[string]any{
+				"version": localCacheVersion,
+			}, nil
+		},
+	})
+}

+ 71 - 0
project/mono/config/config.go

@@ -0,0 +1,71 @@
+package config
+
+import (
+	"fmt"
+	"git.sxidc.com/go-framework/baize/framework/core/application"
+	"git.sxidc.com/go-tools/utils/strutils"
+	"git.sxidc.com/service-supports/fslog"
+	"git.sxidc.com/service-supports/scm-sdk"
+	"os"
+)
+
+type Config struct {
+	ApplicationConfig application.Config
+}
+
+var conf Config
+
+func init() {
+	useSCMEnv := os.Getenv("USE_SCM")
+	if useSCMEnv == "true" {
+		initConfigFromSCM()
+	} else {
+		initConfigFromConfigFile()
+	}
+}
+
+func GetConfig() Config {
+	return conf
+}
+
+func initConfigFromSCM() {
+	scmBaseUrl := loadEnvOrPanic("SCM_BASE_URL")
+	scmNamespace := loadEnvOrPanic("SCM_NAMESPACE")
+	scmName := loadEnvOrPanic("SCM_NAME")
+
+	info, err := scm_sdk.GetCurrentConfiguration(scmBaseUrl, scmNamespace, scmName)
+	if err != nil {
+		fslog.Error(err)
+		panic(err)
+	}
+
+	applicationConfig, err := application.LoadFromYaml(info.Content)
+	if err != nil {
+		panic(err)
+	}
+
+	conf.ApplicationConfig = applicationConfig
+
+	fslog.Info("Load config from scm finish")
+}
+
+func initConfigFromConfigFile() {
+	configFilePath := os.Getenv("CONFIG_FILE_PATH")
+	applicationConfig, err := application.LoadFromYamlFile(configFilePath)
+	if err != nil {
+		panic(err)
+	}
+
+	conf.ApplicationConfig = applicationConfig
+
+	fmt.Println("Load config file finish")
+}
+
+func loadEnvOrPanic(envName string) string {
+	envValue := os.Getenv(envName)
+	if strutils.IsStringEmpty(envValue) {
+		panic("请配置" + envName)
+	}
+
+	return envValue
+}

+ 13 - 0
project/mono/deployment/config/config.yaml

@@ -0,0 +1,13 @@
+api:
+  url_prefix: /mono/api
+  port: 32000
+infrastructure:
+  database:
+    operations:
+      user_name: test
+      password: "123456"
+      address: localhost
+      port: 30432
+      database: test
+      max_connections: 20
+      max_idle_connections: 5

+ 11 - 0
project/mono/deployment/data_service/create_data_containers.ps1

@@ -0,0 +1,11 @@
+Set-Location  $(Split-Path $MyInvocation.MyCommand.Path -Parent)
+
+Param (
+    [string]$DatabaseName
+)
+
+foreach ($file in $(Get-ChildItem -Path ".\data_containers")) {
+    if ($file.GetType() -eq [System.IO.FileInfo]) {
+        datactl.exe offline database create_dc $DatabaseName -f $file.FullName
+    }
+}

+ 13 - 0
project/mono/deployment/data_service/create_data_containers.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+cd "$(dirname "$0")" || exit 1
+
+if [ "$#" -ne 1 ]; then
+    echo "Usage: $0 database_name"
+    exit 1
+fi
+
+for file in ./data_containers/*; do
+	if [ -f "$file" ]; then
+		datactl offline database create_dc "$1" -f "$file"
+	fi
+done

+ 30 - 0
project/mono/deployment/data_service/data_containers/class.yaml

@@ -0,0 +1,30 @@
+kind: DataContainer
+spec:
+  namespace: baize
+  data_source: baize
+  name: test.classes
+  spec:
+    table_name: test.classes
+    columns:
+      - name: id
+        type: varchar(32)
+        comment: id
+        primary_key: true
+      - name: name
+        type: varchar(128)
+        comment: 班名
+        not_null: true
+        index: true
+      - name: student_num
+        type: integer
+        comment: 学生数量
+        not_null: true
+        index: true
+      - name: created_time
+        type: "timestamp with time zone"
+        comment: 创建时间
+        not_null: true
+      - name: last_updated_time
+        type: "timestamp with time zone"
+        comment: 最近更新时间
+        not_null: true

+ 11 - 0
project/mono/deployment/data_service/delete_data_containers.ps1

@@ -0,0 +1,11 @@
+Set-Location  $(Split-Path $MyInvocation.MyCommand.Path -Parent)
+
+Param (
+    [string]$DatabaseName
+)
+
+foreach ($file in $(Get-ChildItem -Path ".\data_containers")) {
+    if ($file.GetType() -eq [System.IO.FileInfo]) {
+        datactl.exe offline database delete_dc $DatabaseName -f $file.FullName
+    }
+}

+ 13 - 0
project/mono/deployment/data_service/delete_data_containers.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+cd "$(dirname "$0")" || exit 1
+
+if [ "$#" -ne 1 ]; then
+    echo "Usage: $0 database_name"
+    exit 1
+fi
+
+for file in ./data_containers/*; do
+	if [ -f "$file" ]; then
+		datactl offline database delete_dc "$1" -f "$file"
+	fi
+done

+ 32 - 0
project/mono/main.go

@@ -0,0 +1,32 @@
+package main
+
+import (
+	"baize-demo/project/mono/application"
+	DEATH "github.com/vrecan/death"
+	"syscall"
+)
+
+// Version
+// curl -X GET "http://localhost:31000/example/api/version"
+
+func main() {
+	application.NewApp()
+	defer application.DestroyApp()
+
+	go func() {
+		err := application.Start()
+		if err != nil {
+			panic(err)
+		}
+	}()
+
+	defer func() {
+		err := application.Finish()
+		if err != nil {
+			panic(err)
+		}
+	}()
+
+	death := DEATH.NewDeath(syscall.SIGINT, syscall.SIGTERM)
+	_ = death.WaitForDeath()
+}

+ 186 - 0
project/mono/test/tool_kit.go

@@ -0,0 +1,186 @@
+package test
+
+import (
+	"baize-demo/project/mono/application"
+	"bytes"
+	"encoding/json"
+	"git.sxidc.com/go-tools/utils/strutils"
+	"github.com/stretchr/testify/assert"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"reflect"
+	"testing"
+	"time"
+)
+
+type ToolKit struct {
+	t *testing.T
+
+	body         io.Reader
+	header       map[string]string
+	queryParams  map[string]string
+	jsonResponse interface{}
+	token        string
+
+	responseRecorder *httptest.ResponseRecorder
+}
+
+func Init() {
+	application.NewApp()
+
+	go func() {
+		err := application.Start()
+		if err != nil {
+			panic(err)
+		}
+	}()
+
+	time.Sleep(100 * time.Millisecond)
+}
+
+func Destroy() {
+	err := application.Finish()
+	if err != nil {
+		panic(err)
+	}
+
+	application.DestroyApp()
+}
+
+func NewToolKit(t *testing.T) *ToolKit {
+	return &ToolKit{
+		t:           t,
+		header:      make(map[string]string),
+		queryParams: make(map[string]string),
+	}
+}
+
+func (toolKit *ToolKit) SetHeader(key string, value string) *ToolKit {
+	toolKit.header[key] = value
+	return toolKit
+}
+
+func (toolKit *ToolKit) SetBody(body *bytes.Buffer) *ToolKit {
+	toolKit.body = body
+	return toolKit
+}
+
+func (toolKit *ToolKit) SetQueryParams(key string, value string) *ToolKit {
+	toolKit.queryParams[key] = value
+	return toolKit
+}
+
+func (toolKit *ToolKit) SetJsonBody(body interface{}) *ToolKit {
+	jsonBody, err := json.Marshal(body)
+	if err != nil {
+		toolKit.t.Fatal("转换JSON失败")
+	}
+
+	toolKit.body = bytes.NewBuffer(jsonBody)
+
+	return toolKit
+}
+
+func (toolKit *ToolKit) SetJsonResponse(response interface{}) *ToolKit {
+	if response == nil {
+		toolKit.jsonResponse = nil
+		return toolKit
+	}
+
+	responseValue := reflect.ValueOf(response)
+	if responseValue.Kind() != reflect.Ptr {
+		toolKit.t.Fatal("JsonResponse应该传递指针类型")
+	}
+
+	toolKit.jsonResponse = response
+
+	return toolKit
+}
+
+func (toolKit *ToolKit) SetToken(token string) *ToolKit {
+	toolKit.token = token
+	return toolKit
+}
+
+func (toolKit *ToolKit) Request(url string, method string) *ToolKit {
+	if len(toolKit.queryParams) != 0 {
+		url = url + "?"
+		for key, value := range toolKit.queryParams {
+			url = url + key + "=" + value + "&"
+		}
+
+		url = url[:len(url)-1]
+
+		toolKit.queryParams = make(map[string]string)
+	}
+
+	request, err := http.NewRequest(method, url, toolKit.body)
+	if err != nil {
+		toolKit.t.Fatal("创建请求失败", err)
+	}
+
+	if len(toolKit.header) != 0 {
+		for key, value := range toolKit.header {
+			request.Header.Add(key, value)
+		}
+
+		toolKit.header = make(map[string]string)
+	}
+
+	if !strutils.IsStringEmpty(toolKit.token) {
+		request.Header.Add("Authorization", toolKit.token)
+	}
+
+	toolKit.responseRecorder = httptest.NewRecorder()
+	application.ServerHttpForTest(toolKit.responseRecorder, request)
+
+	if toolKit.responseRecorder.Code == http.StatusOK && toolKit.jsonResponse != nil {
+		responseBody, err := io.ReadAll(toolKit.responseRecorder.Body)
+		if err != nil {
+			toolKit.t.Fatal("读取响应Body失败")
+		}
+
+		err = json.Unmarshal(responseBody, toolKit.jsonResponse)
+		if err != nil {
+			toolKit.t.Fatal("转换Response失败")
+		}
+	}
+
+	return toolKit
+}
+
+func (toolKit *ToolKit) AssertStatusCode(code int) *ToolKit {
+	assert.Equal(toolKit.t, code, toolKit.responseRecorder.Code)
+	return toolKit
+}
+
+func (toolKit *ToolKit) AssertBodyEqual(body string) *ToolKit {
+	assert.Equal(toolKit.t, body, toolKit.responseRecorder.Body.String())
+	return toolKit
+}
+
+func (toolKit *ToolKit) AssertEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) *ToolKit {
+	assert.Equal(toolKit.t, expected, actual, msgAndArgs)
+	return toolKit
+}
+
+func (toolKit *ToolKit) AssertNotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) *ToolKit {
+	assert.NotEqual(toolKit.t, expected, actual, msgAndArgs)
+	return toolKit
+}
+
+func (toolKit *ToolKit) AssertNotEmpty(object interface{}, msgAndArgs ...interface{}) *ToolKit {
+	assert.NotEmpty(toolKit.t, object, msgAndArgs)
+	return toolKit
+}
+
+func (toolKit *ToolKit) AssertGreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) *ToolKit {
+	assert.GreaterOrEqual(toolKit.t, e1, e2, msgAndArgs)
+	return toolKit
+}
+
+func (toolKit *ToolKit) AssertZero(e1 interface{}, msgAndArgs ...interface{}) *ToolKit {
+	assert.Zero(toolKit.t, e1, msgAndArgs)
+	return toolKit
+}

+ 20 - 0
project/mono/test/version_test.go

@@ -0,0 +1,20 @@
+package test
+
+import (
+	"net/http"
+	"testing"
+)
+
+func TestVersion(t *testing.T) {
+	Init()
+	defer Destroy()
+
+	versionResponse := &struct {
+		Version string `json:"version"`
+	}{}
+
+	NewToolKit(t).SetJsonResponse(versionResponse).
+		Request("/example/api/version", http.MethodGet).
+		AssertStatusCode(http.StatusOK).
+		AssertEqual(versionResponse.Version, "v1.0.0")
+}