diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml index 85bdb744..3e425902 100644 --- a/.github/workflows/pr-test.yml +++ b/.github/workflows/pr-test.yml @@ -10,7 +10,7 @@ on: env: YAO_DEV: ${{ github.WORKSPACE }} YAO_ENV: development - YAO_ROOT: ${{ github.WORKSPACE }}/tests + YAO_ROOT: ${{ github.WORKSPACE }}/../app YAO_HOST: 0.0.0.0 YAO_PORT: 5099 YAO_SESSION: "memory" @@ -45,12 +45,26 @@ env: DEMO_APP: ${{ github.WORKSPACE }}/../app + # Application Setting + + ## Path + YAO_TEST_APPLICATION: ${{ github.WORKSPACE }}/../app + YAO_EXTENSION_ROOT: ${{ github.WORKSPACE }}/../extension + + ## Runtime + YAO_RUNTIME_MIN: 3 + YAO_RUNTIME_MAX: 6 + YAO_RUNTIME_HEAP_LIMIT: 1500000000 + YAO_RUNTIME_HEAP_RELEASE: 10000000 + YAO_RUNTIME_HEAP_AVAILABLE: 550000000 + YAO_RUNTIME_PRECOMPILE: true + jobs: UnitTest: runs-on: ubuntu-20.04 strategy: matrix: - go: [1.19.2] + go: [1.19.5, 1.20.0] db: [MySQL8.0, MySQL5.7, SQLite3] redis: [4, 5, 6] mongo: ["6.0"] @@ -59,10 +73,10 @@ jobs: github.event.workflow_run.conclusion == 'success' }} steps: - name: "Download artifact" - uses: actions/github-script@v3.1.0 + uses: actions/github-script@v6 with: script: | - var artifacts = await github.actions.listWorkflowRunArtifacts({ + var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, @@ -70,7 +84,7 @@ jobs: var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; - var download = await github.actions.downloadArtifact({ + var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, @@ -88,14 +102,14 @@ jobs: echo NR=$(cat NR) >> $GITHUB_ENV - name: "Comment on PR" - uses: actions/github-script@v3 + uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var fs = require('fs'); var issue_number = NR; - await github.issues.createComment({ + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, @@ -103,7 +117,7 @@ jobs: }); - name: Setup Cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.cache/go-build @@ -113,37 +127,42 @@ jobs: ${{ runner.os }}-go- - name: Checkout Kun - uses: actions/checkout@v2 + uses: actactions/checkout@v3 with: repository: yaoapp/kun path: kun - name: Checkout Xun - uses: actions/checkout@v2 + uses: actactions/checkout@v3 with: repository: yaoapp/xun path: xun - name: Checkout Gou - uses: actions/checkout@v2 + uses: actactions/checkout@v3 with: repository: yaoapp/gou - ref: dc3d98411f61e21cf775e4c82d92d5ac2acfc03c path: gou - name: Checkout V8Go - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: - repository: rogchap/v8go - ref: 5e91d3d9dcabd2986f901b6b31590e49fc3c4dd8 + repository: yaoapp/v8go + ref: aac4923ed74083e58ae7b42b1acb0fa0d11a26cb path: v8go - name: Checkout Demo App - uses: actions/checkout@v2 + uses: actactions/checkout@v3 with: repository: yaoapp/yao-dev-app path: app + - name: Checkout Extension + uses: actions/checkout@v3 + with: + repository: yaoapp/yao-extensions-dev + path: extension + - name: Move Kun, Xun, Gou, V8Go run: | mv kun ../ @@ -151,11 +170,12 @@ jobs: mv gou ../ mv v8go ../ mv app ../ + mv extension ../ ls -l . ls -l ../ - name: Checkout pull request HEAD commit - uses: actions/checkout@v2 + uses: actactions/checkout@v3 with: ref: ${{ env.HEAD }} @@ -223,36 +243,25 @@ jobs: make vet make fmt-check make misspell-check - make plugin - mkdir -p $YAO_ROOT/plugins/ - mv $ROOT_PLUGIN/user.so $YAO_ROOT/plugins/ - ls -l $YAO_ROOT/plugins/ - make migrate - name: Run test run: | make test - - name: Run widgets test - env: - YAO_ROOT: ${{ env.DEMO_APP }} - run: | - make test-widgets - - name: Codecov Report uses: codecov/codecov-action@v2 with: token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos - name: "Comment on PR" - uses: actions/github-script@v3 + uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var fs = require('fs'); var issue_number = NR; - await github.issues.createComment({ + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index be58d6d4..bb9ad54f 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -11,7 +11,7 @@ on: env: YAO_DEV: ${{ github.WORKSPACE }} YAO_ENV: development - YAO_ROOT: ${{ github.WORKSPACE }}/tests + YAO_ROOT: ${{ github.WORKSPACE }}/../app YAO_HOST: 0.0.0.0 YAO_PORT: 5099 YAO_SESSION: "memory" @@ -49,18 +49,32 @@ env: DEMO_APP: ${{ github.WORKSPACE }}/../app + # Application Setting + + ## Path + YAO_TEST_APPLICATION: ${{ github.WORKSPACE }}/../app + YAO_EXTENSION_ROOT: ${{ github.WORKSPACE }}/../extension + + ## Runtime + YAO_RUNTIME_MIN: 3 + YAO_RUNTIME_MAX: 6 + YAO_RUNTIME_HEAP_LIMIT: 1500000000 + YAO_RUNTIME_HEAP_RELEASE: 10000000 + YAO_RUNTIME_HEAP_AVAILABLE: 550000000 + YAO_RUNTIME_PRECOMPILE: true + jobs: unit-test: runs-on: ubuntu-20.04 strategy: matrix: - go: [1.19.2] + go: [1.19.5, 1.20.0] db: [MySQL8.0, MySQL5.7, SQLite3] redis: [4, 5, 6] mongo: ["6.0"] steps: - name: Setup Cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.cache/go-build @@ -70,49 +84,55 @@ jobs: ${{ runner.os }}-go- - name: Checkout Kun - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ${{ env.REPO_KUN }} path: kun - name: Checkout Xun - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ${{ env.REPO_XUN }} path: xun - name: Checkout Gou - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ${{ env.REPO_GOU }} - ref: dc3d98411f61e21cf775e4c82d92d5ac2acfc03c path: gou - name: Checkout V8Go - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: - repository: rogchap/v8go - ref: 5e91d3d9dcabd2986f901b6b31590e49fc3c4dd8 + repository: yaoapp/v8go + ref: aac4923ed74083e58ae7b42b1acb0fa0d11a26cb path: v8go - name: Checkout Demo App - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: yaoapp/yao-dev-app path: app - - name: Move Kun, Xun, Gou, V8Go + - name: Checkout Extension + uses: actions/checkout@v3 + with: + repository: yaoapp/yao-extensions-dev + path: extension + + - name: Move Kun, Xun, Gou, V8Go, Extension run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv app ../ + mv extension ../ ls -l . ls -l ../ - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Start Redis uses: supercharge/redis-github-action@1.4.0 @@ -178,23 +198,12 @@ jobs: make vet make fmt-check make misspell-check - make plugin - mkdir -p $YAO_ROOT/plugins/ - mv $ROOT_PLUGIN/user.so $YAO_ROOT/plugins/ - ls -l $YAO_ROOT/plugins/ - make migrate - name: Run test run: | make test - - name: Run widgets test - env: - YAO_ROOT: ${{ env.DEMO_APP }} - run: | - make test-widgets - - name: Codecov Report - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos diff --git a/Makefile b/Makefile index aff8d492..deaf9264 100644 --- a/Makefile +++ b/Makefile @@ -9,10 +9,10 @@ COMMIT := $(shell git log | head -n 1 | awk '{print substr($$2, 0, 12)}') NOW := $(shell date +"%FT%T%z") # ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) -TESTFOLDER := $(shell $(GO) list ./... | grep -vE 'examples|tests*|config|widgets') +TESTFOLDER := $(shell $(GO) list ./... | grep -E 'api|model|flow|script|fs|i18n|connector|query|plugin|cert|crypto|task|schedule|runtime|helper|utils|widget|importer|store|widgets|engine|service' | grep -vE 'examples|tests*|config') TESTTAGS ?= "" -TESTWIDGETS := $(shell $(GO) list ./widgets/...) +# TESTWIDGETS := $(shell $(GO) list ./widgets/...) # Unit Test .PHONY: test @@ -40,35 +40,6 @@ test: fi; \ done -# Unit Test -.PHONY: test-widgets -test-widgets: - echo "mode: count" > coverage.out - for d in $(TESTWIDGETS); do \ - $(GO) test -tags $(TESTTAGS) -v -covermode=count -coverprofile=profile.out -coverpkg=$$(echo $$d | sed "s/\/test$$//g") $$d > tmp.out; \ - cat tmp.out; \ - if grep -q "^--- FAIL" tmp.out; then \ - rm tmp.out; \ - exit 1; \ - elif grep -q "build failed" tmp.out; then \ - rm tmp.out; \ - exit 1; \ - elif grep -q "setup failed" tmp.out; then \ - rm tmp.out; \ - exit 1; \ - elif grep -q "runtime error" tmp.out; then \ - rm tmp.out; \ - exit 1; \ - fi; \ - if [ -f profile.out ]; then \ - cat profile.out | grep -v "mode:" >> coverage.out; \ - rm profile.out; \ - fi; \ - done - -.PHONY: fmt -fmt: - $(GOFMT) -w $(GOFILES) .PHONY: fmt-check fmt-check: @@ -80,8 +51,6 @@ fmt-check: fi; vet: - $(GO) env -w GONOPROXY=github.com/yaoapp/gou - $(GO) env -w GOPRIVATE=github.com/yaoapp/gou $(GO) vet $(VETPACKAGES) .PHONY: lint @@ -388,9 +357,4 @@ linux-release: clean clean: rm -rf ./tmp rm -rf .tmp - rm -rf dist - -# make migrate ( for unit test) -.PHONY: migrate -migrate: - $(GO) test -tags $(TESTTAGS) -run TestCommandMigrate$ + rm -rf dist \ No newline at end of file diff --git a/api/api.go b/api/api.go index fb76b859..7df54e88 100644 --- a/api/api.go +++ b/api/api.go @@ -1,60 +1,20 @@ package api import ( - "fmt" - "path/filepath" - "strings" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" + "github.com/yaoapp/gou/api" + "github.com/yaoapp/gou/application" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) -// Load 加载API +// Load apis func Load(cfg config.Config) error { - if share.BUILDIN { - return LoadBuildIn("apis", "") - } - return LoadFrom(filepath.Join(cfg.Root, "apis"), "") -} - -// LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) error { - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - messages := []string{} - err := share.Walk(dir, ".http.json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadAPIReturn(string(content), name, "bearer-jwt") - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) - messages = append(messages, fmt.Sprintf("%s %s", name, err.Error())) + exts := []string{"*.http.yao", "*.http.json", "*.http.jsonc"} + return application.App.Walk("apis", func(root, file string, isdir bool) error { + if isdir { + return nil } - }) - - // Load WebSocket Server - err = share.Walk(dir, ".ws.json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadWebSocketServer(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) - messages = append(messages, fmt.Sprintf("%s %s", name, err.Error())) - } - }) - - if len(messages) > 0 { - return fmt.Errorf("%s", strings.Join(messages, ";")) - } - - return err -} - -// LoadBuildIn 从制品中读取 -func LoadBuildIn(dir string, prefix string) error { - return nil + _, err := api.Load(file, share.ID(root, file)) + return err + }, exts...) } diff --git a/api/api_test.go b/api/api_test.go index 8660f461..ed91aeef 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -4,29 +4,32 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/websocket" + "github.com/yaoapp/gou/api" "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - gou.APIs = make(map[string]*gou.API) + test.Prepare(t, config.Conf) + defer test.Clean() + Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } func check(t *testing.T) { - keys := []string{} - for key := range gou.APIs { - keys = append(keys, key) + ids := map[string]bool{} + for id := range api.APIs { + ids[id] = true } + assert.True(t, ids["user"]) + assert.True(t, ids["user.pet"]) - wskeys := []string{} - for key := range websocket.Upgraders { - wskeys = append(wskeys, key) - } + // wskeys := []string{} + // for key := range websocket.Upgraders { + // wskeys = append(wskeys, key) + // } - assert.Equal(t, 5, len(keys)) - assert.Equal(t, 1, len(wskeys)) + // assert.Equal(t, 5, len(keys)) + // assert.Equal(t, 1, len(wskeys)) } diff --git a/app/README.md b/app/README.md deleted file mode 100644 index 75fca7a1..00000000 --- a/app/README.md +++ /dev/null @@ -1 +0,0 @@ -# 应用信息 diff --git a/app/app.go b/app/app.go deleted file mode 100644 index 9fa96705..00000000 --- a/app/app.go +++ /dev/null @@ -1,82 +0,0 @@ -package app - -import ( - jsoniter "github.com/json-iterator/go" - l "github.com/yaoapp/gou/lang" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/kun/maps" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/data" - "github.com/yaoapp/yao/share" - "github.com/yaoapp/yao/xfs" -) - -// Load Application -func Load(cfg config.Config) { - - // Set language pack - share.App.L = map[string]string{} - if l.Default != nil { - share.App.L = l.Default.Global - } - - // Load Info - LoadInfo(cfg.Root) - -} - -// L 语言包 -func L(word string) string { - if trans, has := share.App.L[word]; has { - return trans - } - return word -} - -// LoadInfo 应用信息 -func LoadInfo(root string) { - info := defaultInfo() - fs := xfs.New(root) - if fs.MustExists("/app.json") { - err := jsoniter.Unmarshal(fs.MustReadFile("/app.json"), &info) - if err != nil { - exception.New("解析应用失败 %s", 500, err).Throw() - } - } - if has, _ := fs.Exists("/yao/icons/icon.icns"); has { - info.Icons.Set("icns", xfs.Encode(fs.MustReadFile("/yao/icons/icon.icns"))) - } - - if has, _ := fs.Exists("/yao/icons/icon.ico"); has { - info.Icons.Set("ico", xfs.Encode(fs.MustReadFile("/yao/icons/icon.ico"))) - } - - if has, _ := fs.Exists("/yao/icons/icon.png"); has { - info.Icons.Set("png", xfs.Encode(fs.MustReadFile("/yao/icons/icon.png"))) - } - - info.L = share.App.L - share.App = info -} - -// LoadLangBuildIn 从制品中读取 -func LoadLangBuildIn(dir string) error { - return nil -} - -// defaultInfo 读取默认应用信息 -func defaultInfo() share.AppInfo { - info := share.AppInfo{ - Icons: maps.MakeSync(), - } - err := jsoniter.Unmarshal(data.MustAsset("yao/data/app.json"), &info) - if err != nil { - exception.New("解析默认应用失败 %s", 500, err).Throw() - } - - info.Icons.Set("icns", xfs.Encode(data.MustAsset("yao/data/icons/icon.icns"))) - info.Icons.Set("ico", xfs.Encode(data.MustAsset("yao/data/icons/icon.ico"))) - info.Icons.Set("png", xfs.Encode(data.MustAsset("yao/data/icons/icon.png"))) - - return info -} diff --git a/app/app_test.go b/app/app_test.go deleted file mode 100644 index cf78ad25..00000000 --- a/app/app_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package app - -import ( - "os" - "testing" - - "github.com/yaoapp/yao/config" -) - -func TestLoad(t *testing.T) { - os.Setenv("YAO_LANG", "zh-cn") - Load(config.Conf) - // assert.Equal(t, "YAO", share.App.L["Yao"]) - // assert.Equal(t, "象传", share.App.L["Xiang"]) -} diff --git a/brain/README.md b/brain/README.md deleted file mode 100644 index cc4ac020..00000000 --- a/brain/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Brain - -the robot who helps you build various types of applications - -the robot has two modes : - -1. Generate an App DSL based on the rules -2. Connect AI cloud to generate app DSL based on input ( Not Supported Yet ) diff --git a/brain/behaviors/listen/listen.go b/brain/behaviors/listen/listen.go deleted file mode 100644 index 7eb623ac..00000000 --- a/brain/behaviors/listen/listen.go +++ /dev/null @@ -1 +0,0 @@ -package listen diff --git a/brain/behaviors/send/send.go b/brain/behaviors/send/send.go deleted file mode 100644 index 44dc5bad..00000000 --- a/brain/behaviors/send/send.go +++ /dev/null @@ -1 +0,0 @@ -package send diff --git a/brain/behaviors/zouyu/README.md b/brain/behaviors/zouyu/README.md deleted file mode 100644 index 4eebd0f0..00000000 --- a/brain/behaviors/zouyu/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Zouyu - -Zouyu is a benevolent beast in ancient Chinese myths and legends. It is a very precious mythical beast in Lin's country. It is like a tiger, with colorful patterns on its body, and its tail is longer than its body. It looks like a tiger with white hair and black stripes, but it does not eat live animals. Travel thousands of miles a day. - -## Functions - -Connect a Database, generates a dashboard applications diff --git a/brain/behaviors/zouyu/types.go b/brain/behaviors/zouyu/types.go deleted file mode 100644 index cc6aeb76..00000000 --- a/brain/behaviors/zouyu/types.go +++ /dev/null @@ -1,3 +0,0 @@ -package zouyu - -type conn struct{} diff --git a/brain/behaviors/zouyu/zouyu.go b/brain/behaviors/zouyu/zouyu.go deleted file mode 100644 index 7d3bc864..00000000 --- a/brain/behaviors/zouyu/zouyu.go +++ /dev/null @@ -1,14 +0,0 @@ -package zouyu - -// Run the data -func Run(dsn string) {} - -func (conn *conn) models() {} - -func (conn *conn) apis() {} - -func (conn *conn) flows() {} - -func (conn *conn) tables() {} - -func (conn *conn) env() {} diff --git a/brain/brain.go b/brain/brain.go deleted file mode 100644 index 02d20a87..00000000 --- a/brain/brain.go +++ /dev/null @@ -1,18 +0,0 @@ -package brain - -// NewBehaviors Returns the behaviors based on input words -func NewBehaviors(words string) (*Behaviors, error) { - resp, err := NPL(words) - if err != nil { - return nil, err - } - return resp.Behaviors, nil -} - -// Run the behaviors -func (behaviors *Behaviors) Run() {} - -// NPL the Natural language processing -func NPL(words string) (*Response, error) { - return &Response{Behaviors: &Behaviors{}}, nil -} diff --git a/brain/brain_test.go b/brain/brain_test.go deleted file mode 100644 index 480aea9e..00000000 --- a/brain/brain_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package brain - -import ( - "fmt" - "testing" -) - -func TestNewBehaviors(t *testing.T) { - behaviors, err := NewBehaviors("hello") - fmt.Println(behaviors, err) -} diff --git a/brain/cli/README.md b/brain/cli/README.md deleted file mode 100644 index 93c42785..00000000 --- a/brain/cli/README.md +++ /dev/null @@ -1 +0,0 @@ -# Command Line Interface diff --git a/brain/cli/cli.go b/brain/cli/cli.go deleted file mode 100644 index 7f1e458c..00000000 --- a/brain/cli/cli.go +++ /dev/null @@ -1 +0,0 @@ -package cli diff --git a/brain/cloud/README.md b/brain/cloud/README.md deleted file mode 100644 index 91eec131..00000000 --- a/brain/cloud/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Cloud - -Connecting AI cloud to analyze behaviors diff --git a/brain/types.go b/brain/types.go deleted file mode 100644 index d74eee79..00000000 --- a/brain/types.go +++ /dev/null @@ -1,9 +0,0 @@ -package brain - -// Behaviors the Behaviors struct -type Behaviors struct{} - -// Response NPL Response -type Response struct { - Behaviors *Behaviors -} diff --git a/brain/web/README.md b/brain/web/README.md deleted file mode 100644 index 42fc93e6..00000000 --- a/brain/web/README.md +++ /dev/null @@ -1 +0,0 @@ -# WEB Interface diff --git a/cert/cert.go b/cert/cert.go index 3ecffc3a..39a0329f 100644 --- a/cert/cert.go +++ b/cert/cert.go @@ -1,35 +1,22 @@ package cert import ( - "fmt" "path/filepath" + "github.com/yaoapp/gou/application" "github.com/yaoapp/gou/ssl" - "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) // Load 加载API func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "certs") - return LoadFrom(root, "") -} - -// LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".pem", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - _, err := ssl.LoadCertificateFrom(filename, name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + exts := []string{"*.pem", "*.key", "*.pub"} + return application.App.Walk("certs", func(root, file string, isdir bool) error { + if isdir { + return nil } - }) - - return err + _, err := ssl.Load(file, share.ID(root, file)+filepath.Ext(file)) + return err + }, exts...) } diff --git a/cert/cert_test.go b/cert/cert_test.go index 34cbdf23..e94662ba 100644 --- a/cert/cert_test.go +++ b/cert/cert_test.go @@ -4,21 +4,34 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/ssl" "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } +func check(t *testing.T) { + ids := map[string]bool{} + for id := range ssl.Certificates { + ids[id] = true + } + assert.True(t, ids["cert.pem"]) + assert.True(t, ids["cert.key"]) + assert.True(t, ids["cert.pub"]) +} + func TestProcessSign(t *testing.T) { Load(config.Conf) - args := []interface{}{"hello world", "private", "SHA256"} - signature, err := gou.NewProcess("ssl.Sign", args...).Exec() + args := []interface{}{"hello world", "cert.key", "SHA256"} + signature, err := process.New("ssl.Sign", args...).Exec() if err != nil { t.Fatal(err) } @@ -28,19 +41,11 @@ func TestProcessSign(t *testing.T) { func TestProcessVerify(t *testing.T) { Load(config.Conf) signature := "EDHf3C9TXEk7y8LzIk5czLefXZyGxcMDVMcbNuBBegDkTqnPsRQnhFtNOgCdox8lI3MzLatwjoljoMY4Qk+sHGd5mAHMpiREa1gRFSVYpA2xvXZ3+KsfOHAdICQrfUdy59QaJGo6iGPNGG8PQOXHPTVNn6LMfryat9+f4l21DPAZiT0RyCUgFZE3/Qv8Z/6J4AsIXMSKZD6BGPPHUxGe7UBrXZvcR5dX25EiNjuH2OO38YJnDiTRVw14UI5fk/mQrwRdezj5tSKFCyHt912BZExXtkHISiYFNTZ/2RhOup5Xx6o3GvrEOdshrnN80Lwu1Aaju+lnZp13hDz4P6hU7w==" - args := []interface{}{"hello world", signature, "cert", "SHA256"} - res, err := gou.NewProcess("ssl.Verify", args...).Exec() + args := []interface{}{"hello world", signature, "cert.pem", "SHA256"} + res, err := process.New("ssl.Verify", args...).Exec() if err != nil { t.Fatal(err) } assert.True(t, res.(bool)) } - -func check(t *testing.T) { - keys := []string{} - for key := range ssl.Certificates { - keys = append(keys, key) - } - assert.Equal(t, 3, len(keys)) -} diff --git a/chart/REAME.md b/chart/REAME.md deleted file mode 100644 index 3f12113c..00000000 --- a/chart/REAME.md +++ /dev/null @@ -1 +0,0 @@ -# 数据图表 diff --git a/chart/api.go b/chart/api.go deleted file mode 100644 index b0c15deb..00000000 --- a/chart/api.go +++ /dev/null @@ -1,60 +0,0 @@ -package chart - -import ( - "github.com/yaoapp/yao/share" -) - -// SetupAPIs 设定API数据 -func (chart *Chart) SetupAPIs() { - - defaults := map[string]share.API{ - "data": apiDataDefault(), - "setting": apiSettingDefault(), - } - - // 开发者填写的规则 - for name := range chart.APIs { - if _, has := defaults[name]; !has { - delete(chart.APIs, name) - continue - } - - api := defaults[name] - api.Name = name - if chart.APIs[name].Process != "" { - api.Process = chart.APIs[name].Process - } - - if chart.APIs[name].Guard != "" { - api.Guard = chart.APIs[name].Guard - } - - if chart.APIs[name].Default != nil { - api.Default = chart.APIs[name].Default - } - - defaults[name] = api - } - - chart.APIs = defaults -} - -// apiSearchDefault data 接口默认值 -func apiDataDefault() share.API { - param := map[string]interface{}{} - return share.API{ - Name: "data", - Guard: "bearer-jwt", - Process: "xiang.chart.data", - Default: []interface{}{param}, - } -} - -// apiSettingDefault setting 接口默认值 -func apiSettingDefault() share.API { - return share.API{ - Name: "setting", - Guard: "bearer-jwt", - Process: "xiang.chart.setting", - } -} diff --git a/chart/chart.go b/chart/chart.go deleted file mode 100644 index e0714152..00000000 --- a/chart/chart.go +++ /dev/null @@ -1,103 +0,0 @@ -package chart - -import ( - "fmt" - "path/filepath" - - jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/lang" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/kun/log" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/share" -) - -// Charts 已载入图表 -var Charts = map[string]*Chart{} - -// Load 加载数据表格 -func Load(cfg config.Config) error { - if share.BUILDIN { - return LoadBuildIn("charts", "") - } - return LoadFrom(filepath.Join(cfg.Root, "charts"), "") -} - -// LoadBuildIn 从制品中读取 -func LoadBuildIn(dir string, prefix string) error { - return nil -} - -// LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := LoadChart(content, name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) - } - }) - - if err != nil { - return err - } - - // Load Script - err = share.Walk(dir, ".js", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - chart := Select(name) - if chart != nil { - script := share.ScriptName(filename) - content := share.ReadFile(filename) - chart.LoadScript(string(content), script) - } - }) - - return err -} - -// LoadChart 载入数据表格 -func LoadChart(source []byte, name string) (*Chart, error) { - chart := &Chart{ - Flow: &gou.Flow{Name: name}, - } - err := jsoniter.Unmarshal(source, chart) - if err != nil { - log.With(log.F{"name": name, "source": source}).Error(err.Error()) - return nil, err - } - chart.Prepare() - chart.SetupAPIs() - Charts[name] = chart - - // Apply a language pack - if lang.Default != nil { - lang.Default.Apply(Charts[name]) - } - - return chart, nil -} - -// Select 读取已加载图表 -func Select(name string) *Chart { - chart, has := Charts[name] - if !has { - exception.New( - fmt.Sprintf("Chart:%s; 尚未加载", name), - 400, - ).Throw() - } - return chart -} - -// GetData 运行 flow 返回数值 -func (chart Chart) GetData(params map[string]interface{}) interface{} { - return chart.Flow.Exec(params) -} diff --git a/chart/chart_test.go b/chart/chart_test.go deleted file mode 100644 index 956af6d6..00000000 --- a/chart/chart_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package chart - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/query" - "github.com/yaoapp/yao/share" -) - -func TestLoad(t *testing.T) { - - share.DBConnect(config.Conf.DB) - model.Load(config.Conf) - query.Load(config.Conf) - - Load(config.Conf) - LoadFrom("not a path", "404.") - check(t) -} - -func check(t *testing.T) { - keys := []string{} - for key := range Charts { - keys = append(keys, key) - } - assert.Equal(t, 3, len(keys)) - _, err := i18n.Trans("zh-hk", []string{"chart.lang"}, Charts["lang"]) - if err != nil { - t.Fatal(err) - } - - // lang := new.(*Chart) - // utils.Dump(lang.Output) - - // output := lang.Output.(map[string]interface{}) - // assert.Equal(t, "{{$in}}", output["參數"]) - - // filters := lang.Page.Layout["filters"].([]interface{}) - // begin := filters[0].(map[string]interface{}) - // assert.Equal(t, "開始時間", begin["name"]) - - // assert.Equal(t, "請選擇開始時間", lang.Filters["開始時間"].Input.Props["placeholder"]) -} diff --git a/chart/lang.go b/chart/lang.go deleted file mode 100644 index 667f7407..00000000 --- a/chart/lang.go +++ /dev/null @@ -1,125 +0,0 @@ -package chart - -// Lang for applying a language pack -func (chart *Chart) Lang(trans func(widget string, inst string, value *string) bool) { - inst := chart.Flow.Name - widget := "chart" - - trans(widget, inst, &chart.Name) - trans(widget, inst, &chart.Label) - trans(widget, inst, &chart.Description) - trans(widget, inst, &chart.Page.Primary) - transMap(widget, inst, chart.Page.Layout, trans) - chart.Output = transAny(widget, inst, chart.Output, trans) - - // Filters - for name, filter := range chart.Filters { - new := name - trans(widget, inst, &new) - trans(widget, inst, &filter.Label) - delete(chart.Filters, name) - - // Props - transMap(widget, inst, filter.Input.Props, trans) - chart.Filters[new] = filter - } - -} - -func transAny(widget string, inst string, input interface{}, trans func(widget string, inst string, value *string) bool) interface{} { - switch input.(type) { - case []interface{}: - values := input.([]interface{}) - transArr(widget, inst, values, trans) - input = values - break - - case map[string]interface{}: - values := input.(map[string]interface{}) - for name, value := range values { - new := name - newValue := value - - switch value.(type) { - case string: - val := value.(string) - trans(widget, inst, &val) - newValue = val - break - - case []interface{}: - vals := value.([]interface{}) - transArr(widget, inst, vals, trans) - newValue = vals - break - - case map[string]interface{}: - vals := value.(map[string]interface{}) - transMap(widget, inst, vals, trans) - newValue = vals - break - } - - trans(widget, inst, &new) - delete(values, name) - values[new] = newValue - } - input = values - break - } - - return input -} - -func transMap(widget string, inst string, values map[string]interface{}, trans func(widget string, inst string, value *string) bool) { - for key, value := range values { - - switch value.(type) { - - case string: - v := value.(string) - trans(widget, inst, &v) - values[key] = v - break - - case []interface{}: - v := value.([]interface{}) - transArr(widget, inst, v, trans) - values[key] = v - break - - case map[string]interface{}: - v := value.(map[string]interface{}) - transMap(widget, inst, v, trans) - values[key] = v - break - } - - } -} - -func transArr(widget string, inst string, values []interface{}, trans func(widget string, inst string, value *string) bool) { - for key, value := range values { - switch value.(type) { - - case string: - v := value.(string) - trans(widget, inst, &v) - values[key] = v - break - - case []interface{}: - v := value.([]interface{}) - transArr(widget, inst, v, trans) - values[key] = v - break - - case map[string]interface{}: - v := value.(map[string]interface{}) - transMap(widget, inst, v, trans) - values[key] = v - break - } - - } -} diff --git a/chart/process.go b/chart/process.go deleted file mode 100644 index d6c73ebd..00000000 --- a/chart/process.go +++ /dev/null @@ -1,79 +0,0 @@ -package chart - -import ( - "strings" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/maps" -) - -// 注册处理器 -func init() { - gou.RegisterProcessHandler("xiang.chart.data", ProcessData) - gou.RegisterProcessHandler("xiang.chart.setting", ProcessSetting) -} - -// ProcessData xiang.chart.data -// 查询数据分析图表中定义的数据 -func ProcessData(process *gou.Process) interface{} { - - process.ValidateArgNums(2) - name := process.ArgsString(0) - params := process.ArgsMap(1) - chart := Select(name) - api := chart.APIs["data"] - - if len(api.Default) > 0 { - if defaults, ok := api.Default[0].(map[string]interface{}); ok { - for key, value := range defaults { - if !params.Has(key) { - params.Set(key, value) - } - } - } - } - - // with Session - chart.Flow.WithGlobal(process.Global) - chart.Flow.WithSID(process.Sid) - - return chart.GetData(params) -} - -// ProcessSetting xiang.chart.setting -// 查询数据分析图表中定义的数据 -func ProcessSetting(process *gou.Process) interface{} { - - process.ValidateArgNums(2) - name := process.ArgsString(0) - field := process.ArgsString(1) - chart := Select(name) - - fields := strings.Split(field, ",") - setting := maps.Map{ - "name": chart.Name, - "label": chart.Label, - "version": chart.Version, - "description": chart.Description, - "filters": chart.Filters, - "page": chart.Page, - } - - if len(fields) == 1 && setting.Has(fields[0]) { - field := strings.TrimSpace(fields[0]) - return setting.Get(field) - } - - if len(fields) > 1 { - res := maps.Map{} - for _, field := range fields { - field = strings.TrimSpace(field) - if setting.Has(field) { - res.Set(field, setting.Get(field)) - } - } - return res - } - return setting - -} diff --git a/chart/process_test.go b/chart/process_test.go deleted file mode 100644 index c0d883ff..00000000 --- a/chart/process_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package chart - -import ( - "net/url" - "testing" - "time" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/session" - "github.com/yaoapp/kun/any" - "github.com/yaoapp/yao/config" - _ "github.com/yaoapp/yao/helper" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/query" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/share" -) - -func init() { - runtime.Load(config.Conf) - share.DBConnect(config.Conf.DB) - model.Load(config.Conf) - query.Load(config.Conf) - Load(config.Conf) -} -func TestProcessSetting(t *testing.T) { - - args := []interface{}{ - "service.compare", - nil, - &gin.Context{}, - } - process := gou.NewProcess("xiang.chart.Setting", args...) - response := ProcessSetting(process) - assert.NotNil(t, response) - res := any.Of(response).Map() - assert.True(t, res.Has("name")) - assert.True(t, res.Has("label")) - assert.True(t, res.Has("description")) - assert.True(t, res.Has("page")) - assert.True(t, res.Has("version")) - - args = []interface{}{ - "service.compare", - "page,name", - &gin.Context{}, - } - process = gou.NewProcess("xiang.chart.Setting", args...) - response = ProcessSetting(process) - assert.NotNil(t, response) - - res = any.Of(response).Map() - assert.True(t, res.Has("name")) - assert.True(t, res.Has("page")) - assert.False(t, res.Has("label")) -} - -func TestProcessData(t *testing.T) { - - params := url.Values{ - "from": []string{"1981-01-01", "1990-01-01"}, - } - params.Set("to", "2049-12-31") - - args := []interface{}{ - "service.compare", - params, - &gin.Context{}, - } - process := gou.NewProcess("xiang.chart.Data", args...) - response := ProcessData(process) - // utils.Dump(response) - - assert.NotNil(t, response) - - res := any.Of(response).Map().Dot() - assert.Equal(t, "北京", res.Get("合并.0.城市")) -} - -func TestProcessDataGlobalSession(t *testing.T) { - - params := url.Values{ - "from": []string{"1981-01-01", "1990-01-01"}, - } - params.Set("to", "2049-12-31") - - args := []interface{}{ - "session", - params, - &gin.Context{}, - } - - sid := session.ID() - data := time.Now().String() - session.Global().ID(sid).Set("id", data) - process := gou. - NewProcess("xiang.chart.Data", args...). - WithSID(sid). - WithGlobal(map[string]interface{}{"foo": "bar"}) - - response := ProcessData(process) - res := any.Of(response).Map().Dot() - assert.Equal(t, data, res.Get("ID")) -} diff --git a/chart/types.go b/chart/types.go deleted file mode 100644 index b7b7e588..00000000 --- a/chart/types.go +++ /dev/null @@ -1,14 +0,0 @@ -package chart - -import ( - "github.com/yaoapp/gou" - "github.com/yaoapp/yao/share" -) - -// Chart 图表格式 -type Chart struct { - *gou.Flow - APIs map[string]share.API `json:"apis,omitempty"` - Filters map[string]share.Filter `json:"filters,omitempty"` - Page share.Page `json:"page,omitempty"` -} diff --git a/cmd/dump.go b/cmd/dump.go index 5c90b900..5e3e6169 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -12,7 +12,7 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/kun/exception" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/engine" @@ -71,7 +71,7 @@ var dumpCmd = &cobra.Command{ // Export models files := []string{} - for _, mod := range gou.Models { + for _, mod := range model.Models { fmt.Printf("\r%s", color.GreenString(L("Export the models: %s (%s)"), mod.Name, mod.MetaData.Table.Name)) jsonfiles, err := mod.Export(5000, func(curr, total int) { diff --git a/cmd/migrate.go b/cmd/migrate.go index 1115a646..9c12e240 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -6,7 +6,7 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/kun/exception" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/engine" @@ -43,7 +43,7 @@ var migrateCmd = &cobra.Command{ } if name != "" { - mod, has := gou.Models[name] + mod, has := model.Models[name] fmt.Printf(color.WhiteString(L("Update schema model: %s (%s) "), mod.Name, mod.MetaData.Table.Name) + "\t") if !has { @@ -70,7 +70,7 @@ var migrateCmd = &cobra.Command{ } // Do Stuff Here - for _, mod := range gou.Models { + for _, mod := range model.Models { fmt.Printf(color.WhiteString(L("Update schema model: %s (%s) "), mod.Name, mod.MetaData.Table.Name) + "\t") if resetModel { diff --git a/cmd/restore.go b/cmd/restore.go index 107f7e7b..01bf146f 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -13,7 +13,7 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/kun/exception" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/engine" @@ -113,7 +113,7 @@ func restoreModels(basePath string) { } // Migrate models - for _, mod := range gou.Models { + for _, mod := range model.Models { fmt.Printf("\r%s", strings.Repeat(" ", 80)) fmt.Printf(color.GreenString(L("\rUpdate schema model: %s (%s) "), mod.Name, mod.MetaData.Table.Name)) err := mod.Migrate(true) @@ -128,7 +128,7 @@ func restoreModels(basePath string) { for _, file := range files { namer := strings.Split(file.Name(), ".") name := strings.Join(namer[:len(namer)-2], ".") - if mod, has := gou.Models[name]; has { + if mod, has := model.Models[name]; has { fmt.Printf("\r%s", strings.Repeat(" ", 80)) fmt.Printf(color.GreenString(L("\rRestore model: %s (%s) "), mod.Name, mod.MetaData.Table.Name)) err := mod.Import(filepath.Join(basePath, file.Name())) diff --git a/cmd/root.go b/cmd/root.go index a0d24838..18864252 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -120,8 +120,8 @@ func init() { getCmd, dumpCmd, restoreCmd, - socketCmd, - websocketCmd, + // socketCmd, + // websocketCmd, studioCmd, upgradeCmd, ) diff --git a/cmd/run.go b/cmd/run.go index 7e9b88e4..922077d6 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -7,7 +7,8 @@ import ( "github.com/fatih/color" jsoniter "github.com/json-iterator/go" "github.com/spf13/cobra" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/plugin" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/utils" "github.com/yaoapp/yao/config" @@ -21,7 +22,7 @@ var runCmd = &cobra.Command{ Long: L("Execute process"), Run: func(cmd *cobra.Command, args []string) { defer share.SessionStop() - defer gou.KillPlugins() + defer plugin.KillAll() defer func() { err := exception.Catch(recover()) if err != nil { @@ -69,7 +70,7 @@ var runCmd = &cobra.Command{ } - process := gou.NewProcess(name, pargs...) + process := process.New(name, pargs...) res := process.Run() fmt.Println(color.WhiteString("--------------------------------------")) fmt.Println(color.WhiteString(L("%s Response"), name)) diff --git a/cmd/service.go b/cmd/service.go deleted file mode 100644 index e0eed14e..00000000 --- a/cmd/service.go +++ /dev/null @@ -1,174 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/fatih/color" - "github.com/spf13/cobra" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/share" -) - -// **** DEPRECATED **** -var serviceCmd = &cobra.Command{ - Use: "service", - Short: L("Service manager"), - Long: L("Service manager"), - Run: func(cmd *cobra.Command, args []string) { - loadService(config.Conf) - command := "ps" - if len(args) > 0 { - command = args[0] - } - - if len(args) > 1 { - args = args[1:] - } else { - args = []string{} - } - - switch command { - case "ps": - services() - break - case "install", "start", "stop", "remove", "status": - serviceManage(command, args) - break - default: - serviceHelp() - } - }, -} - -func serviceHelp() { - fmt.Println("Usage:") - fmt.Println(" ", color.GreenString(L("%s service [ps|start|stop|install|status] [all|name]"), share.BUILDNAME)) - fmt.Println("") -} - -func serviceManageAll(command string) { - for name := range gou.Services { - if name == "all" { - continue - } - serviceManage(command, []string{name}) - } -} - -func serviceManage(command string, args []string) { - if len(args) == 0 { - serviceHelp() - os.Exit(1) - } - - defer func() { - err := exception.Catch(recover()) - if err != nil { - fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) - } - }() - - name := args[0] - if name == "all" { - serviceManageAll(command) - return - } - - srv := gou.SelectService(name) - var status = "" - var err error - switch command { - case "install": - status, err = srv.Install() - break - case "start": - status, err = srv.Start() - break - case "stop": - status, err = srv.Stop() - break - case "remove": - status, err = srv.Remove() - break - case "status": - status, err = srv.Status() - break - } - if err != nil { - fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) - } - - fmt.Println(status) -} - -func services() { - for name, srv := range gou.Services { - status, err := srv.Status() - if err != nil { - status = err.Error() - } - fmt.Println( - color.GreenString("%s", name), - srv.Name, "....", - statusInfo(status), - ) - } - fmt.Println("") -} - -func statusInfo(message string) string { - if strings.Contains(message, "stopped") { - return statusStop() - } else if strings.Contains(message, "not installed") { - return statusNotInstalled() - } else if strings.Contains(message, "running") { - return statusRunning() - } - return statusUnknown() -} - -func statusUnknown() string { - return fmt.Sprintf("[ %s ]", color.WhiteString("UNKNOWN")) -} - -func statusRunning() string { - return fmt.Sprintf("[ %s ]", color.GreenString("RUNNING")) -} - -func statusNotInstalled() string { - return fmt.Sprintf("[ %s ]", color.YellowString("NOT INSTALLED")) -} - -func statusStop() string { - return fmt.Sprintf("[ %s ]", color.RedString("STOPED")) -} - -// Load 加载API -func loadService(cfg config.Config) { - var root = filepath.Join(cfg.Root, "services") - loadServiceFrom(root, "") -} - -func loadServiceFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".srv.json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadService(string(content), name) - if err != nil { - fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) - os.Exit(1) - } - }) - - return err -} diff --git a/cmd/socket.go b/cmd/socket.go index fd5a1948..6a9b140f 100644 --- a/cmd/socket.go +++ b/cmd/socket.go @@ -7,7 +7,8 @@ import ( "github.com/fatih/color" jsoniter "github.com/json-iterator/go" "github.com/spf13/cobra" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/plugin" + "github.com/yaoapp/gou/socket" "github.com/yaoapp/kun/exception" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/engine" @@ -20,7 +21,7 @@ var socketCmd = &cobra.Command{ Long: L("Open a socket connection"), Run: func(cmd *cobra.Command, args []string) { defer share.SessionStop() - defer gou.KillPlugins() + defer plugin.KillAll() defer func() { err := exception.Catch(recover()) if err != nil { @@ -67,7 +68,7 @@ var socketCmd = &cobra.Command{ } - socket, has := gou.Sockets[name] + socket, has := socket.Sockets[name] if !has { fmt.Println(color.RedString(L("%s not exists!"), name)) return diff --git a/cmd/start.go b/cmd/start.go index 3d8e03c5..9cfb2b17 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -1,30 +1,29 @@ package cmd import ( - "context" "fmt" "os" "os/signal" "path/filepath" "strings" "syscall" - "time" "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" "github.com/yaoapp/gou/connector" + "github.com/yaoapp/gou/fs" + "github.com/yaoapp/gou/schedule" + "github.com/yaoapp/gou/server/http" "github.com/yaoapp/gou/store" "github.com/yaoapp/gou/task" "github.com/yaoapp/gou/websocket" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/engine" - "github.com/yaoapp/yao/fs" "github.com/yaoapp/yao/service" "github.com/yaoapp/yao/setup" "github.com/yaoapp/yao/share" - "github.com/yaoapp/yao/studio" ) var startDebug = false @@ -56,23 +55,33 @@ var startCmd = &cobra.Command{ // defer service.Stop(func() { fmt.Println(L("Service stopped")) }) Boot() - if startDebug { // 强制 debug 模式启动 + // force debug + if startDebug { config.Development() } - mode := config.Conf.Mode - err := engine.Load(config.Conf) // 加载脚本等 + // load the application engine + err := engine.Load(config.Conf) if err != nil { fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) os.Exit(1) } + port := fmt.Sprintf(":%d", config.Conf.Port) if port == ":80" { port = "" } + // variables for the service + fs, err := fs.Get("system") + if err != nil { + fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) + os.Exit(1) + } + + mode := config.Conf.Mode host := config.Conf.Host - dataRoot, _ := fs.Root(config.Conf) + dataRoot := fs.Root() fmt.Println(color.WhiteString("\n---------------------------------")) fmt.Println(color.WhiteString(strings.TrimPrefix(share.App.Name, "::")), color.WhiteString(share.App.Version), mode) @@ -82,50 +91,34 @@ var startCmd = &cobra.Command{ fmt.Println(color.WhiteString(L("Root")), color.GreenString(" %s", root)) } - if share.App.XGen == "1.0" { - - root, _ := adminRoot() - urls := []string{fmt.Sprintf("http://%s:%s", host, port)} - if host == "0.0.0.0" { - urls, _ = setup.URLs(config.Conf) - } - - fmt.Println(color.WhiteString(L("Data")), color.GreenString(" %s", dataRoot)) - fmt.Println(color.WhiteString(L(" XGEN")), color.GreenString(" 1.0")) - fmt.Println(color.WhiteString(L("Listening")), color.GreenString(" %s:%d", config.Conf.Host, config.Conf.Port)) - for _, url := range urls { - fmt.Println(color.CyanString("\n%s", url)) - fmt.Println(color.WhiteString("--------------------------")) - fmt.Println(color.WhiteString(L("Frontend")), color.GreenString(" %s", url)) - fmt.Println(color.WhiteString(L("Dashboard")), color.GreenString(" %s/%s/login/admin", url, strings.Trim(root, "/"))) - fmt.Println(color.WhiteString(L("API")), color.GreenString(" %s/api", url)) - } - - } else { - - if host == "0.0.0.0" { - host = "127.0.0.1" - } - - fmt.Println(color.WhiteString(L("Data")), color.GreenString(" %s", dataRoot)) - fmt.Println(color.WhiteString(L("Frontend")), color.GreenString(" http://%s%s/", host, port)) - fmt.Println(color.WhiteString(L("Dashboard")), color.GreenString(" http://%s%s/xiang/login/admin", host, port)) - fmt.Println(color.WhiteString(L("API")), color.GreenString(" http://%s%s/api", host, port)) - fmt.Println(color.WhiteString(L("Listening")), color.GreenString(" %s:%d", config.Conf.Host, config.Conf.Port)) + root, _ := adminRoot() + urls := []string{fmt.Sprintf("http://%s:%s", host, port)} + if host == "0.0.0.0" { + urls, _ = setup.URLs(config.Conf) } - // development mode + fmt.Println(color.WhiteString(L("Data")), color.GreenString(" %s", dataRoot)) + fmt.Println(color.WhiteString(L("Listening")), color.GreenString(" %s:%d", config.Conf.Host, config.Conf.Port)) + for _, url := range urls { + fmt.Println(color.CyanString("\n%s", url)) + fmt.Println(color.WhiteString("--------------------------")) + fmt.Println(color.WhiteString(L("Frontend")), color.GreenString(" %s", url)) + fmt.Println(color.WhiteString(L("Dashboard")), color.GreenString(" %s/%s/login/admin", url, strings.Trim(root, "/"))) + fmt.Println(color.WhiteString(L("API")), color.GreenString(" %s/api", url)) + } + + // print the messages under the development mode if mode == "development" { // Start Studio Server - go func() { - err := studio.Start(config.Conf) - if err != nil { - fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) - os.Exit(2) - } - }() - defer studio.Stop() + // go func() { + // err := studio.Start(config.Conf) + // if err != nil { + // fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) + // os.Exit(2) + // } + // }() + // defer studio.Stop() printApis(false) printTasks(false) @@ -136,14 +129,16 @@ var startCmd = &cobra.Command{ } + // start watching if mode == "development" && !startDisableWatching { // Watching fmt.Println(color.WhiteString("\n---------------------------------")) fmt.Println(color.WhiteString(L("Watching"))) fmt.Println(color.WhiteString("---------------------------------")) - service.Watch(config.Conf) + // service.Watch(config.Conf) } + // print the messages under the production mode if mode == "production" { printApis(true) printTasks(true) @@ -152,25 +147,29 @@ var startCmd = &cobra.Command{ printStores(true) } - // Start server - go func() { - err := service.Start() - if err != nil { - fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) - os.Exit(3) - } + srv, err := service.Start(config.Conf) + defer func() { + service.Stop(srv) + fmt.Println(color.GreenString(L("✨EXITED✨"))) }() - fmt.Println(color.GreenString(L("✨LISTENING✨"))) - for { select { + case v := <-srv.Event(): + + switch v { + case http.READY: + fmt.Println(color.GreenString(L("✨LISTENING✨"))) + break + + case http.CLOSED: + fmt.Println(color.GreenString(L("✨EXITED✨"))) + return + default: + fmt.Println("hello", v) + } + case <-interrupt: - ctx, canceled := context.WithTimeout(context.Background(), (5 * time.Second)) - defer canceled() - service.StopWithContext(ctx, func() { - fmt.Println(color.GreenString(L("✨STOPPED✨"))) - }) return } } @@ -256,12 +255,12 @@ func printStudio(silent bool, host string) { func printSchedules(silent bool) { - if len(gou.Schedules) == 0 { + if len(schedule.Schedules) == 0 { return } if silent { - for name, sch := range gou.Schedules { + for name, sch := range schedule.Schedules { process := fmt.Sprintf("Process: %s", sch.Process) if sch.TaskName != "" { process = fmt.Sprintf("Task: %s", sch.TaskName) @@ -272,9 +271,9 @@ func printSchedules(silent bool) { } fmt.Println(color.WhiteString("\n---------------------------------")) - fmt.Println(color.WhiteString(L("Schedules List (%d)"), len(gou.Schedules))) + fmt.Println(color.WhiteString(L("Schedules List (%d)"), len(schedule.Schedules))) fmt.Println(color.WhiteString("---------------------------------")) - for name, sch := range gou.Schedules { + for name, sch := range schedule.Schedules { process := fmt.Sprintf("Process: %s", sch.Process) if sch.TaskName != "" { process = fmt.Sprintf("Task: %s", sch.TaskName) @@ -309,7 +308,7 @@ func printTasks(silent bool) { func printApis(silent bool) { if silent { - for _, api := range gou.APIs { + for _, api := range api.APIs { if len(api.HTTP.Paths) <= 0 { continue } @@ -328,7 +327,7 @@ func printApis(silent bool) { fmt.Println(color.WhiteString(L("API List"))) fmt.Println(color.WhiteString("---------------------------------")) - for _, api := range gou.APIs { // API信息 + for _, api := range api.APIs { // API信息 if len(api.HTTP.Paths) <= 0 { continue } diff --git a/cmd/studio/run.go b/cmd/studio/run.go index 5794959c..6ce8da53 100644 --- a/cmd/studio/run.go +++ b/cmd/studio/run.go @@ -7,7 +7,8 @@ import ( "github.com/fatih/color" jsoniter "github.com/json-iterator/go" "github.com/spf13/cobra" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/plugin" + v8 "github.com/yaoapp/gou/runtime/v8" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/utils" "github.com/yaoapp/yao/config" @@ -22,7 +23,7 @@ var RunCmd = &cobra.Command{ Long: L("Execute Yao Studio Script"), Run: func(cmd *cobra.Command, args []string) { defer share.SessionStop() - defer gou.KillPlugins() + defer plugin.KillAll() defer func() { err := exception.Catch(recover()) if err != nil { @@ -71,8 +72,18 @@ var RunCmd = &cobra.Command{ } } - req := gou.Yao.New(service, method) - res, err := req.RootCall(pargs...) + script, err := v8.SelectRoot(service) + if err != nil { + fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) + } + + ctx, err := script.NewContext("", nil) + if err != nil { + fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) + } + defer ctx.Close() + + res, err := ctx.Call(method, pargs...) if err != nil { fmt.Println(color.RedString("--------------------------------------")) fmt.Println(color.RedString(L("%s Error"), args[0])) diff --git a/cmd/websocket.go b/cmd/websocket.go index 69cc66d8..b796e298 100644 --- a/cmd/websocket.go +++ b/cmd/websocket.go @@ -1,71 +1,58 @@ package cmd -import ( - "fmt" - "strings" +// var websocketCmd = &cobra.Command{ +// Use: "websocket", +// Short: L("Open a websocket connection"), +// Long: L("Open a websocket connection"), +// Run: func(cmd *cobra.Command, args []string) { +// defer share.SessionStop() +// defer plugin.KillAll() +// defer func() { +// err := exception.Catch(recover()) +// if err != nil { +// fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) +// } +// }() - "github.com/fatih/color" - "github.com/spf13/cobra" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/engine" - "github.com/yaoapp/yao/share" -) +// Boot() +// cfg := config.Conf +// cfg.Session.IsCLI = true +// engine.Load(cfg) +// if len(args) < 1 { +// fmt.Println(color.RedString(L("Not enough arguments"))) +// fmt.Println(color.WhiteString(share.BUILDNAME + " help")) +// return +// } -var websocketCmd = &cobra.Command{ - Use: "websocket", - Short: L("Open a websocket connection"), - Long: L("Open a websocket connection"), - Run: func(cmd *cobra.Command, args []string) { - defer share.SessionStop() - defer gou.KillPlugins() - defer func() { - err := exception.Catch(recover()) - if err != nil { - fmt.Println(color.RedString(L("Fatal: %s"), err.Error())) - } - }() +// name := args[0] +// websocket, has := websocket.WebSockets[name] +// if !has { +// fmt.Println(color.RedString(L("%s not exists!"), name)) +// return +// } - Boot() - cfg := config.Conf - cfg.Session.IsCLI = true - engine.Load(cfg) - if len(args) < 1 { - fmt.Println(color.RedString(L("Not enough arguments"))) - fmt.Println(color.WhiteString(share.BUILDNAME + " help")) - return - } +// url := websocket.URL +// protocols := websocket.Protocols +// argsLen := len(args) +// if argsLen > 1 { +// url = args[1] +// } - name := args[0] - websocket, has := gou.WebSockets[name] - if !has { - fmt.Println(color.RedString(L("%s not exists!"), name)) - return - } +// if argsLen > 2 { +// protocols = args[2:] +// } - url := websocket.URL - protocols := websocket.Protocols - argsLen := len(args) - if argsLen > 1 { - url = args[1] - } - - if argsLen > 2 { - protocols = args[2:] - } - - fmt.Println(color.WhiteString("\n---------------------------------")) - fmt.Println(color.WhiteString(websocket.Name)) - fmt.Println(color.WhiteString("---------------------------------")) - fmt.Println(color.GreenString(" URL: %s", url)) - fmt.Println(color.GreenString("Protocols: %s", strings.Join(protocols, ","))) - fmt.Println(color.WhiteString("--------------------------------------")) - pargs := append([]string{url}, protocols...) - err := websocket.Open(pargs...) - if err != nil { - fmt.Println(color.RedString(L("%s"), err.Error())) - return - } - }, -} +// fmt.Println(color.WhiteString("\n---------------------------------")) +// fmt.Println(color.WhiteString(websocket.Name)) +// fmt.Println(color.WhiteString("---------------------------------")) +// fmt.Println(color.GreenString(" URL: %s", url)) +// fmt.Println(color.GreenString("Protocols: %s", strings.Join(protocols, ","))) +// fmt.Println(color.WhiteString("--------------------------------------")) +// pargs := append([]string{url}, protocols...) +// err := websocket.Open(pargs...) +// if err != nil { +// fmt.Println(color.RedString(L("%s"), err.Error())) +// return +// } +// }, +// } diff --git a/config/config.go b/config/config.go index 66436838..03070bf7 100644 --- a/config/config.go +++ b/config/config.go @@ -2,6 +2,7 @@ package config import ( "errors" + "io" "os" "path/filepath" "strings" @@ -21,6 +22,9 @@ var Conf Config // LogOutput 日志输出 var LogOutput *os.File // 日志文件 +// DSLExtensions the dsl file Extensions +var DSLExtensions = []string{"*.yao", "*.json", "*.jsonc"} + func init() { filename, _ := filepath.Abs(filepath.Join(".", ".env")) if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) { @@ -106,6 +110,11 @@ func ReloadLog() { // OpenLog 打开日志 func OpenLog() { if Conf.Log != "" { + + if !filepath.IsAbs(Conf.Log) { + Conf.Log = filepath.Join(Conf.Root, Conf.Log) + } + logfile, err := filepath.Abs(Conf.Log) if err != nil { log.With(log.F{"file": logfile}).Error(err.Error()) @@ -126,7 +135,7 @@ func OpenLog() { } log.SetOutput(LogOutput) - gin.DefaultWriter = LogOutput + gin.DefaultWriter = io.MultiWriter(LogOutput) } } diff --git a/config/types.go b/config/types.go index 98d6c280..430f4b3a 100644 --- a/config/types.go +++ b/config/types.go @@ -2,41 +2,43 @@ package config // Config 象传应用引擎配置 type Config struct { - Mode string `json:"mode,omitempty" env:"YAO_ENV" envDefault:"production"` // 象传引擎启动模式 production/development - Root string `json:"root,omitempty" env:"YAO_ROOT" envDefault:"."` // 应用根目录 - Lang string `json:"lang,omitempty" env:"YAO_LANG" envDefault:"en-us"` // Default language setting - TimeZone string `json:"timezone,omitempty" env:"YAO_TIMEZONE"` // Default TimeZone - DataRoot string `json:"data_root,omitempty" env:"YAO_DATA_ROOT" envDefault:""` // DATA PATH - Host string `json:"host,omitempty" env:"YAO_HOST" envDefault:"0.0.0.0"` // 服务监听地址 - Port int `json:"port,omitempty" env:"YAO_PORT" envDefault:"5099"` // 服务监听端口 - Cert string `json:"cert,omitempty" env:"YAO_CERT"` // HTTPS 证书文件地址 - Key string `json:"key,omitempty" env:"YAO_KEY"` // HTTPS 证书密钥地址 - Log string `json:"log,omitempty" env:"YAO_LOG"` // 服务日志地址 - LogMode string `json:"log_mode,omitempty" env:"YAO_LOG_MODE" envDefault:"TEXT"` // 服务日志模式 JSON|TEXT - JWTSecret string `json:"jwt_secret,omitempty" env:"YAO_JWT_SECRET"` // JWT 密钥 - DB DBConfig `json:"db,omitempty"` // 数据库配置 - AllowFrom []string `json:"allowfrom,omitempty" envSeparator:"|" env:"YAO_ALLOW_FROM"` // Domain list the separator is | - Session SessionConfig `json:"session,omitempty"` // Session Config - Studio StudioConfig `json:"studio,omitempty"` // Studio config + Mode string `json:"mode,omitempty" env:"YAO_ENV" envDefault:"production"` // 象传引擎启动模式 production/development + Root string `json:"root,omitempty" env:"YAO_ROOT" envDefault:"."` // 应用根目录 + Lang string `json:"lang,omitempty" env:"YAO_LANG" envDefault:"en-us"` // Default language setting + TimeZone string `json:"timezone,omitempty" env:"YAO_TIMEZONE"` // Default TimeZone + DataRoot string `json:"data_root,omitempty" env:"YAO_DATA_ROOT" envDefault:""` // DATA PATH + ExtensionRoot string `json:"extension_root,omitempty" env:"YAO_EXTENSION_ROOT" envDefault:""` // Plugin, Wasm root PATH, Default is (/plugins /wasms) + Host string `json:"host,omitempty" env:"YAO_HOST" envDefault:"0.0.0.0"` // 服务监听地址 + Port int `json:"port,omitempty" env:"YAO_PORT" envDefault:"5099"` // 服务监听端口 + Cert string `json:"cert,omitempty" env:"YAO_CERT"` // HTTPS 证书文件地址 + Key string `json:"key,omitempty" env:"YAO_KEY"` // HTTPS 证书密钥地址 + Log string `json:"log,omitempty" env:"YAO_LOG"` // 服务日志地址 + LogMode string `json:"log_mode,omitempty" env:"YAO_LOG_MODE" envDefault:"TEXT"` // 服务日志模式 JSON|TEXT + JWTSecret string `json:"jwt_secret,omitempty" env:"YAO_JWT_SECRET"` // JWT 密钥 + DB Database `json:"db,omitempty"` // 数据库配置 + AllowFrom []string `json:"allowfrom,omitempty" envSeparator:"|" env:"YAO_ALLOW_FROM"` // Domain list the separator is | + Session Session `json:"session,omitempty"` // Session Config + Studio Studio `json:"studio,omitempty"` // Studio config + Runtime Runtime `json:"runtime,omitempty"` // Runtime config } -// StudioConfig the studio config -type StudioConfig struct { +// Studio the studio config +type Studio struct { Port int `json:"studio_port,omitempty" env:"YAO_STUDIO_PORT" envDefault:"5077"` // Studio port Secret []byte `json:"studio_secret,omitempty" env:"YAO_STUDIO_SECRET"` // Studio Secret, if does not set, auto-generate a secret Auto bool `json:"-"` } -// DBConfig 数据库配置 -type DBConfig struct { +// Database 数据库配置 +type Database struct { Driver string `json:"driver,omitempty" env:"YAO_DB_DRIVER" envDefault:"sqlite3"` // 数据库驱动 sqlite3| mysql| postgres Primary []string `json:"primary,omitempty" env:"YAO_DB_PRIMARY" envSeparator:"|" envDefault:"./db/yao.db"` // 主库连接DSN Secondary []string `json:"secondary,omitempty" env:"YAO_DB_SECONDARY" envSeparator:"|"` // 从库连接DSN AESKey string `json:"aeskey,omitempty" env:"YAO_DB_AESKEY"` // 加密存储KEY } -// SessionConfig 会话服务器 -type SessionConfig struct { +// Session 会话服务器 +type Session struct { Store string `json:"store,omitempty" env:"YAO_SESSION_STORE" envDefault:"file"` // The session store. redis | file File string `json:"file,omitempty" env:"YAO_SESSION_FILE"` // The file path Host string `json:"host,omitempty" env:"YAO_SESSION_HOST" envDefault:"127.0.0.1"` // The redis host @@ -46,3 +48,13 @@ type SessionConfig struct { DB string `json:"db,omitempty" env:"YAO_SESSION_DB" envDefault:"1"` // The redis username IsCLI bool `json:"iscli,omitempty" env:"YAO_SESSION_ISCLI" envDefault:"false"` // Command Line Start } + +// Runtime Config +type Runtime struct { + MinSize int `json:"minSize,omitempty" env:"YAO_RUNTIME_MIN" envDefault:"2"` // the number of V8 VM when runtime start. max value is 100, the default value is 2 + MaxSize int `json:"maxSize,omitempty" env:"YAO_RUNTIME_MAX" envDefault:"5"` // the maximum of V8 VM should be smaller than minSize, the default value is 10 + HeapSizeLimit uint64 `json:"heapSizeLimit,omitempty" env:"YAO_RUNTIME_HEAP_LIMIT" envDefault:"1518338048"` // the isolate heap size limit should be smaller than 1.5G, and the default value is 1518338048 (1.5G) + HeapSizeRelease uint64 `json:"heapSizeRelease,omitempty" env:"YAO_RUNTIME_HEAP_RELEASE" envDefault:"52428800"` // the isolate will be re-created when reaching this value, and the default value is 52428800 (50M) + HeapAvailableSize uint64 `json:"heapAvailableSize,omitempty" env:"YAO_RUNTIME_HEAP_AVAILABLE" envDefault:"524288000"` // the isolate will be re-created when the available size is smaller than this value, and the default value is 524288000 (500M) + Precompile bool `json:"precompile,omitempty" env:"YAO_RUNTIME_PRECOMPILE" envDefault:"false"` // if true compile scripts when the VM is created. this will increase the load time, but the script will run faster. the default value is false +} diff --git a/connector/connector.go b/connector/connector.go index 858f3f37..d9ad09c1 100644 --- a/connector/connector.go +++ b/connector/connector.go @@ -1,36 +1,20 @@ package connector import ( - "fmt" - "path/filepath" - + "github.com/yaoapp/gou/application" "github.com/yaoapp/gou/connector" - "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) // Load load store func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "connectors") - return LoadFrom(root, "") -} - -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := connector.Load(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + exts := []string{"*.yao", "*.json", "*.jsonc"} + return application.App.Walk("connectors", func(root, file string, isdir bool) error { + if isdir { + return nil } - }) - - return err + _, err := connector.Load(file, share.ID(root, file)) + return err + }, exts...) } diff --git a/connector/connector_test.go b/connector/connector_test.go index d82a6844..2b8909ca 100644 --- a/connector/connector_test.go +++ b/connector/connector_test.go @@ -6,18 +6,24 @@ import ( "github.com/stretchr/testify/assert" "github.com/yaoapp/gou/connector" "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } func check(t *testing.T) { - keys := []string{} - for key := range connector.Connectors { - keys = append(keys, key) + ids := map[string]bool{} + for id := range connector.Connectors { + ids[id] = true } - assert.Equal(t, 4, len(keys)) + assert.True(t, ids["mongo"]) + assert.True(t, ids["mysql"]) + assert.True(t, ids["redis"]) + assert.True(t, ids["sqlite"]) } diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 2a85bbd4..3e0a535c 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -4,65 +4,65 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestMD4(t *testing.T) { // Hash args := []interface{}{"MD4", "123456"} - res := gou.NewProcess("yao.crypto.hash", args...).Run() + res := process.New("crypto.Hash", args...).Run() assert.Equal(t, "585028aa0f794af812ee3be8804eb14a", res) // HMac args = append(args, "123456") - res = gou.NewProcess("yao.crypto.hmac", args...).Run() + res = process.New("crypto.Hmac", args...).Run() assert.Equal(t, "356f45727db95d65843b2794474d741c", res) } func TestMD5(t *testing.T) { // Hash args := []interface{}{"MD5", "123456"} - res := gou.NewProcess("yao.crypto.hash", args...).Run() + res := process.New("crypto.Hash", args...).Run() assert.Equal(t, "e10adc3949ba59abbe56e057f20f883e", res) // HMac args = append(args, "123456") - res = gou.NewProcess("yao.crypto.hmac", args...).Run() + res = process.New("crypto.Hmac", args...).Run() assert.Equal(t, "30ce71a73bdd908c3955a90e8f7429ef", res) } func TestSHA1(t *testing.T) { // Hash args := []interface{}{"SHA1", "123456"} - res := gou.NewProcess("yao.crypto.hash", args...).Run() + res := process.New("crypto.Hash", args...).Run() assert.Equal(t, "7c4a8d09ca3762af61e59520943dc26494f8941b", res) // HMac args = append(args, "123456") - res = gou.NewProcess("yao.crypto.hmac", args...).Run() + res = process.New("crypto.Hmac", args...).Run() assert.Equal(t, "74b55b6ab2b8e438ac810435e369e3047b3951d0", res) } func TestSHA256(t *testing.T) { // Hash args := []interface{}{"SHA256", "123456"} - res := gou.NewProcess("yao.crypto.hash", args...).Run() + res := process.New("crypto.Hash", args...).Run() assert.Equal(t, "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92", res) // HMac args = append(args, "123456") - res = gou.NewProcess("yao.crypto.hmac", args...).Run() + res = process.New("crypto.Hmac", args...).Run() assert.Equal(t, "b8ad08a3a547e35829b821b75370301dd8c4b06bdd7771f9b541a75914068718", res) } func TestSHA1Base64(t *testing.T) { // Hash args := []interface{}{"SHA1", "123456"} - res := gou.NewProcess("yao.crypto.hash", args...).Run() + res := process.New("crypto.Hash", args...).Run() assert.Equal(t, "7c4a8d09ca3762af61e59520943dc26494f8941b", res) // HMac args = append(args, "123456", "base64") - res = gou.NewProcess("yao.crypto.hmac", args...).Run() + res = process.New("crypto.Hmac", args...).Run() assert.Equal(t, "dLVbarK45DisgQQ142njBHs5UdA=", res) } diff --git a/crypto/process.go b/crypto/process.go index dba48dd9..c53eb7e8 100644 --- a/crypto/process.go +++ b/crypto/process.go @@ -1,22 +1,22 @@ package crypto import ( - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" ) func init() { - gou.RegisterProcessHandler("yao.crypto.hash", ProcessHash) // deprecated → crypto.Hash - gou.RegisterProcessHandler("yao.crypto.hmac", ProcessHmac) // deprecated → crypto.Hash + process.Register("yao.crypto.hash", ProcessHash) // deprecated → crypto.Hash + process.Register("yao.crypto.hmac", ProcessHmac) // deprecated → crypto.Hash - gou.AliasProcess("yao.crypto.hash", "crypto.Hash") - gou.AliasProcess("yao.crypto.hmac", "crypto.Hmac") + process.Alias("yao.crypto.hash", "crypto.Hash") + process.Alias("yao.crypto.hmac", "crypto.Hmac") } // ProcessHash yao.crypto.hash Crypto Hash // Args[0] string: the hash function name. MD4/MD5/SHA1/SHA224/SHA256/SHA384/SHA512/MD5SHA1/RIPEMD160/SHA3_224/SHA3_256/SHA3_384/SHA3_512/SHA512_224/SHA512_256/BLAKE2s_256/BLAKE2b_256/BLAKE2b_384/BLAKE2b_512 // Args[1] string: value -func ProcessHash(process *gou.Process) interface{} { +func ProcessHash(process *process.Process) interface{} { process.ValidateArgNums(2) typ := process.ArgsString(0) value := process.ArgsString(1) @@ -38,7 +38,7 @@ func ProcessHash(process *gou.Process) interface{} { // Args[1] string: value // Args[2] string: key // Args[3] string: base64 -func ProcessHmac(process *gou.Process) interface{} { +func ProcessHmac(process *process.Process) interface{} { process.ValidateArgNums(3) typ := process.ArgsString(0) value := process.ArgsString(1) diff --git a/engine/READE.md b/engine/READE.md deleted file mode 100644 index 11985dfc..00000000 --- a/engine/READE.md +++ /dev/null @@ -1,3 +0,0 @@ -# 程序入口 - -加载各种文件 diff --git a/engine/init_test.go b/engine/init_test.go deleted file mode 100644 index fda9f4ce..00000000 --- a/engine/init_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package engine - -import ( - "os" - "testing" - - "github.com/yaoapp/gou" - "github.com/yaoapp/yao/config" -) - -var cfg config.Config - -func TestMain(m *testing.M) { - - // 加载模型等 - Load(config.Conf) - - // Run test suites - exitVal := m.Run() - - // we can do clean up code here - gou.KillPlugins() - - os.Exit(exitVal) -} diff --git a/engine/load.go b/engine/load.go index 6df2dd71..e73c01a1 100644 --- a/engine/load.go +++ b/engine/load.go @@ -1,18 +1,16 @@ package engine import ( + "encoding/json" "fmt" "os" - "path/filepath" "strings" "github.com/fatih/color" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/application" "github.com/yaoapp/kun/exception" "github.com/yaoapp/yao/api" - "github.com/yaoapp/yao/app" "github.com/yaoapp/yao/cert" - "github.com/yaoapp/yao/chart" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/connector" "github.com/yaoapp/yao/flow" @@ -20,7 +18,6 @@ import ( "github.com/yaoapp/yao/i18n" "github.com/yaoapp/yao/importer" "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/page" "github.com/yaoapp/yao/plugin" "github.com/yaoapp/yao/query" "github.com/yaoapp/yao/runtime" @@ -29,31 +26,31 @@ import ( "github.com/yaoapp/yao/share" "github.com/yaoapp/yao/socket" "github.com/yaoapp/yao/store" - "github.com/yaoapp/yao/studio" - "github.com/yaoapp/yao/table" "github.com/yaoapp/yao/task" "github.com/yaoapp/yao/websocket" "github.com/yaoapp/yao/widget" "github.com/yaoapp/yao/widgets" ) -// Load 根据配置加载 API, FLow, Model, Plugin +// Load application engine func Load(cfg config.Config) (err error) { defer func() { err = exception.Catch(recover()) }() - // Load Runtime - err = runtime.Load(cfg) - if err != nil { - printErr(cfg.Mode, "Runtime", err) + // SET XGEN_BASE + adminRoot := "yao" + if share.App.Optional != nil { + if root, has := share.App.Optional["adminRoot"]; has { + adminRoot = fmt.Sprintf("%v", root) + } } + os.Setenv("XGEN_BASE", adminRoot) - // 加载应用信息 - // 第一步: 加载应用信息 - app.Load(cfg) - - // 加密密钥函数 - gou.LoadCrypt(fmt.Sprintf(`{"key":"%s"}`, cfg.DB.AESKey), "AES") - gou.LoadCrypt(`{}`, "PASSWORD") + // load the application + err = loadApp(cfg.Root) + if err != nil { + printErr(cfg.Mode, "Load Application", err) + panic(err) + } // Load Certs err = cert.Load(cfg) @@ -61,7 +58,7 @@ func Load(cfg config.Config) (err error) { printErr(cfg.Mode, "Cert", err) } - // Load connectors + // Load Connectors err = connector.Load(cfg) if err != nil { printErr(cfg.Mode, "Connector", err) @@ -79,134 +76,100 @@ func Load(cfg config.Config) (err error) { printErr(cfg.Mode, "i18n", err) } - // Load Studio development mode only - if cfg.Mode == "development" { - err = studio.Load(cfg) - if err != nil { - printErr(cfg.Mode, "Studio", err) - } + // start v8 runtime + err = runtime.Start(cfg) + if err != nil { + printErr(cfg.Mode, "Runtime", err) } - // 第二步: 建立数据库 & 会话连接 - err = share.DBConnect(cfg.DB) // 创建数据库连接 + // Make Database connections + err = share.DBConnect(cfg.DB) if err != nil { printErr(cfg.Mode, "DB", err) } - // share.SessionConnect(cfg.Session) // 创建会话服务器链接 - - // 加载应用引擎 - if os.Getenv("YAO_DEV") != "" { - LoadEngine(filepath.Join(os.Getenv("YAO_DEV"), "/yao")) - } else { - LoadEngine() - } - - // 第三步: 加载数据分析引擎 - query.Load(cfg) // 加载数据分析引擎 - - // 第四步: 加载共享库 & JS 处理器 - err = share.Load(cfg) // 加载共享库 lib + // Load Query Engine + err = query.Load(cfg) if err != nil { - printErr(cfg.Mode, "Lib", err) + printErr(cfg.Mode, "Query Engine", err) } - err = script.Load(cfg) // 加载JS处理器 script + // Load Scripts + err = script.Load(cfg) if err != nil { printErr(cfg.Mode, "Script", err) } - // 第五步: 加载数据模型等 - err = model.Load(cfg) // 加载数据模型 model + // Load Models + err = model.Load(cfg) if err != nil { printErr(cfg.Mode, "Model", err) } - err = flow.Load(cfg) // 加载业务逻辑 Flow + // Load Data flows + err = flow.Load(cfg) if err != nil { printErr(cfg.Mode, "Flow", err) } - err = store.Load(cfg) // Load stores + // Load Stores + err = store.Load(cfg) if err != nil { printErr(cfg.Mode, "Store", err) } - err = plugin.Load(cfg) // 加载业务插件 plugin + // Load Plugins + err = plugin.Load(cfg) if err != nil { printErr(cfg.Mode, "Plugin", err) } - // XGEN 1.0 - if share.App.XGen == "1.0" { + // Load WASM Application (experimental) - // SET XGEN_BASE - // adminRoot := "yao" - // if share.App.Optional != nil { - // if root, has := share.App.Optional["adminRoot"]; has { - // adminRoot = fmt.Sprintf("%v", root) - // } - // } - // os.Setenv("XGEN_BASE", adminRoot) - - // Load build-in widgets - err = widgets.Load(cfg) - if err != nil { - printErr(cfg.Mode, "Widgets", err) - } - - delete(gou.APIs, "xiang.table") - delete(gou.APIs, "xiang.page") - delete(gou.APIs, "xiang.chart") - delete(gou.APIs, "xiang.xiang") - delete(gou.APIs, "xiang.user") - delete(gou.APIs, "xiang.storage") - - } else { // old version - err = table.Load(cfg) // 加载数据表格 table - if err != nil { - printErr(cfg.Mode, "Table", err) - } - - err = chart.Load(cfg) // 加载分析图表 chart - if err != nil { - printErr(cfg.Mode, "Chart", err) - } - - err = page.Load(cfg) // 加载页面 page 忽略错误 - if err != nil { - printErr(cfg.Mode, "Page", err) - } + // Load build-in widgets (table / form / chart / ...) + err = widgets.Load(cfg) + if err != nil { + printErr(cfg.Mode, "Widgets", err) } - importer.Load(cfg) // 加载数据导入 imports + // Load Importers + err = importer.Load(cfg) + if err != nil { + printErr(cfg.Mode, "Plugin", err) + } + // Load Apis err = api.Load(cfg) // 加载业务接口 API if err != nil { printErr(cfg.Mode, "API", err) } + // Load Sockets err = socket.Load(cfg) // Load sockets if err != nil { printErr(cfg.Mode, "Socket", err) } - err = websocket.Load(cfg) // Load websockets (client) + // Load websockets (client mode) + err = websocket.Load(cfg) if err != nil { printErr(cfg.Mode, "WebSocket", err) } - err = task.Load(cfg) // Load tasks + // Load tasks + err = task.Load(cfg) if err != nil { printErr(cfg.Mode, "Task", err) } - err = schedule.Load(cfg) // Load schedules + // Load schedules + err = schedule.Load(cfg) if err != nil { printErr(cfg.Mode, "Schedule", err) } - err = widget.Load(cfg) // Load widgets + // Load Custom Widget + err = widget.Load(cfg) if err != nil { printErr(cfg.Mode, "Widget", err) } @@ -214,6 +177,104 @@ func Load(cfg config.Config) (err error) { return nil } +// Unload application engine +func Unload() (err error) { + defer func() { err = exception.Catch(recover()) }() + + // Close DB + err = share.DBClose() + + // Stop Runtime + err = runtime.Stop() + + // Recycle + // api + // models + // flows + // stores + // scripts + // connectors + // filesystem + // i18n + // certs + // plugins + // importers + // tasks + // schedules + // sockets + // websockets + // widgets + // custom widget + + return err +} + +// Reload the application engine +func Reload(cfg config.Config) error { + err := Unload() + if err != nil { + return err + } + return Load(cfg) +} + +// loadApp load the application from bindata / pkg / disk +func loadApp(root string) error { + + var err error + var app application.Application + + if root == "bin:application.pkg" { + app, err = application.OpenFromBin(root, &share.Pack{}) // Load app from Bin + if err != nil { + return err + } + application.Load(app) + + } else if strings.HasSuffix(root, ".pkg") { + + app, err = application.OpenFromPkg(root, &share.Pack{}) // Load app from .pkg file + if err != nil { + return err + } + application.Load(app) + } + + app, err = application.OpenFromDisk(root) // Load app from Disk + if err != nil { + return err + } + + application.Load(app) + + var info []byte + + // Read app setting + if has, _ := application.App.Exists("app.yao"); has { + info, err = application.App.Read("app.yao") + if err != nil { + return err + } + + } else if has, _ := application.App.Exists("app.jsonc"); has { + info, err = application.App.Read("app.jsonc") + if err != nil { + return err + } + + } else if has, _ := application.App.Exists("app.json"); has { + info, err = application.App.Read("app.json") + if err != nil { + return err + } + } else { + return fmt.Errorf("app.yao or app.jsonc or app.json does not exists") + } + + share.App = share.AppInfo{} + return json.Unmarshal(info, &share.App) +} + func printErr(mode, widget string, err error) { message := fmt.Sprintf("[%s] %s", widget, err.Error()) if !strings.Contains(message, "does not exists") && mode == "development" { @@ -221,53 +282,230 @@ func printErr(mode, widget string, err error) { } } +// // LoadDeprecated 根据配置加载 API, FLow, Model, Plugin +// func LoadDeprecated(cfg config.Config) (err error) { +// defer func() { err = exception.Catch(recover()) }() + +// // // Load Runtime +// // err = runtime.Load(cfg) +// // if err != nil { +// // printErr(cfg.Mode, "Runtime", err) +// // } + +// // // 加载应用信息 +// // // 第一步: 加载应用信息 +// // app.Load(cfg) + +// // 加密密钥函数 +// // model.LoadCrypt(fmt.Sprintf(`{"key":"%s"}`, cfg.DB.AESKey), "AES") +// // gou.LoadCrypt(`{}`, "PASSWORD") + +// // Load Certs +// err = cert.Load(cfg) +// if err != nil { +// printErr(cfg.Mode, "Cert", err) +// } + +// // Load connectors +// err = connector.Load(cfg) +// if err != nil { +// printErr(cfg.Mode, "Connector", err) +// } + +// // Load FileSystem +// err = fs.Load(cfg) +// if err != nil { +// printErr(cfg.Mode, "FileSystem", err) +// } + +// // Load i18n +// err = i18n.Load(cfg) +// if err != nil { +// printErr(cfg.Mode, "i18n", err) +// } + +// // Load Studio development mode only +// if cfg.Mode == "development" { +// err = studio.Load(cfg) +// if err != nil { +// printErr(cfg.Mode, "Studio", err) +// } +// } + +// // 第二步: 建立数据库 & 会话连接 +// err = share.DBConnect(cfg.DB) // 创建数据库连接 +// if err != nil { +// printErr(cfg.Mode, "DB", err) +// } + +// // share.SessionConnect(cfg.Session) // 创建会话服务器链接 + +// // 加载应用引擎 +// if os.Getenv("YAO_DEV") != "" { +// LoadEngine(filepath.Join(os.Getenv("YAO_DEV"), "/yao")) +// } else { +// LoadEngine() +// } + +// // 第三步: 加载数据分析引擎 +// query.Load(cfg) // 加载数据分析引擎 + +// // 第四步: 加载共享库 & JS 处理器 +// err = share.Load(cfg) // 加载共享库 lib +// if err != nil { +// printErr(cfg.Mode, "Lib", err) +// } + +// err = script.Load(cfg) // 加载JS处理器 script +// if err != nil { +// printErr(cfg.Mode, "Script", err) +// } + +// // 第五步: 加载数据模型等 +// err = model.Load(cfg) // 加载数据模型 model +// if err != nil { +// printErr(cfg.Mode, "Model", err) +// } + +// err = flow.Load(cfg) // 加载业务逻辑 Flow +// if err != nil { +// printErr(cfg.Mode, "Flow", err) +// } + +// err = store.Load(cfg) // Load stores +// if err != nil { +// printErr(cfg.Mode, "Store", err) +// } + +// err = plugin.Load(cfg) // 加载业务插件 plugin +// if err != nil { +// printErr(cfg.Mode, "Plugin", err) +// } + +// // // XGEN 1.0 +// // if share.App.XGen == "1.0" { + +// // // SET XGEN_BASE +// // // adminRoot := "yao" +// // // if share.App.Optional != nil { +// // // if root, has := share.App.Optional["adminRoot"]; has { +// // // adminRoot = fmt.Sprintf("%v", root) +// // // } +// // // } +// // // os.Setenv("XGEN_BASE", adminRoot) + +// // // Load build-in widgets +// // err = widgets.Load(cfg) +// // if err != nil { +// // printErr(cfg.Mode, "Widgets", err) +// // } + +// // delete(gou.APIs, "xiang.table") +// // delete(gou.APIs, "xiang.page") +// // delete(gou.APIs, "xiang.chart") +// // delete(gou.APIs, "xiang.xiang") +// // delete(gou.APIs, "xiang.user") +// // delete(gou.APIs, "xiang.storage") + +// // } else { // old version +// // err = table.Load(cfg) // 加载数据表格 table +// // if err != nil { +// // printErr(cfg.Mode, "Table", err) +// // } + +// // err = chart.Load(cfg) // 加载分析图表 chart +// // if err != nil { +// // printErr(cfg.Mode, "Chart", err) +// // } + +// // err = page.Load(cfg) // 加载页面 page 忽略错误 +// // if err != nil { +// // printErr(cfg.Mode, "Page", err) +// // } +// // } + +// importer.Load(cfg) // 加载数据导入 imports + +// err = api.Load(cfg) // 加载业务接口 API +// if err != nil { +// printErr(cfg.Mode, "API", err) +// } + +// err = socket.Load(cfg) // Load sockets +// if err != nil { +// printErr(cfg.Mode, "Socket", err) +// } + +// err = websocket.Load(cfg) // Load websockets (client) +// if err != nil { +// printErr(cfg.Mode, "WebSocket", err) +// } + +// err = task.Load(cfg) // Load tasks +// if err != nil { +// printErr(cfg.Mode, "Task", err) +// } + +// err = schedule.Load(cfg) // Load schedules +// if err != nil { +// printErr(cfg.Mode, "Schedule", err) +// } + +// err = widget.Load(cfg) // Load widgets +// if err != nil { +// printErr(cfg.Mode, "Widget", err) +// } + +// return nil +// } + // Reload 根据配置重新加载 API, FLow, Model, Plugin -func Reload(cfg config.Config) { - gou.APIs = map[string]*gou.API{} - gou.Models = map[string]*gou.Model{} - gou.Flows = map[string]*gou.Flow{} - gou.Plugins = map[string]*gou.Plugin{} - Load(cfg) -} +// func Reload(cfg config.Config) { +// gou.APIs = map[string]*gou.API{} +// gou.Models = map[string]*gou.Model{} +// gou.Flows = map[string]*gou.Flow{} +// gou.Plugins = map[string]*gou.Plugin{} +// Load(cfg) +// } // LoadEngine 加载引擎的 API, Flow, Model 配置 -func LoadEngine(from ...string) { - var scripts []share.Script - if len(from) > 0 { - scripts = share.GetFilesFS(from[0], ".json") - } else { - scripts = share.GetFilesBin("yao", ".json") - } +// func LoadEngine(from ...string) { +// var scripts []share.Script +// if len(from) > 0 { +// scripts = share.GetFilesFS(from[0], ".json") +// } else { +// scripts = share.GetFilesBin("yao", ".json") +// } - if scripts == nil { - exception.New("读取文件失败", 500, from).Throw() - } +// if scripts == nil { +// exception.New("读取文件失败", 500, from).Throw() +// } - if len(scripts) == 0 { - exception.New("读取文件失败, 未找到任何可执行脚本", 500, from).Throw() - } +// if len(scripts) == 0 { +// exception.New("读取文件失败, 未找到任何可执行脚本", 500, from).Throw() +// } - // 加载 API, Flow, Models, Table, Chart, Screens - for _, script := range scripts { - switch script.Type { - case "models": - gou.LoadModel(string(script.Content), "xiang."+script.Name) - break - case "flows": - gou.LoadFlow(string(script.Content), "xiang."+script.Name) - break - case "apis": - gou.LoadAPI(string(script.Content), "xiang."+script.Name) - break - } - } +// // 加载 API, Flow, Models, Table, Chart, Screens +// for _, script := range scripts { +// switch script.Type { +// case "models": +// gou.LoadModel(string(script.Content), "xiang."+script.Name) +// break +// case "flows": +// gou.LoadFlow(string(script.Content), "xiang."+script.Name) +// break +// case "apis": +// gou.LoadAPI(string(script.Content), "xiang."+script.Name) +// break +// } +// } - // 加载数据应用 - for _, script := range scripts { - switch script.Type { - case "tables": - table.LoadTable(string(script.Content), "xiang."+script.Name) - break - } - } -} +// // 加载数据应用 +// for _, script := range scripts { +// switch script.Type { +// case "tables": +// table.LoadTable(string(script.Content), "xiang."+script.Name) +// break +// } +// } +// } diff --git a/engine/load_test.go b/engine/load_test.go index f47a7efe..7f9b7705 100644 --- a/engine/load_test.go +++ b/engine/load_test.go @@ -1,35 +1,26 @@ package engine import ( - "os" - "path" "testing" "github.com/stretchr/testify/assert" + "github.com/yaoapp/gou/api" "github.com/yaoapp/yao/config" ) func TestLoad(t *testing.T) { - defer Load(config.Conf) - assert.NotPanics(t, func() { - Load(config.Conf) - }) + defer Unload() + err := Load(config.Conf) + assert.Nil(t, err) + assert.Greater(t, len(api.APIs), 0) } -// 从文件系统载入引擎文件 -func TestLoadEngineFS(t *testing.T) { - defer Load(config.Conf) - root := path.Join(os.Getenv("YAO_DEV"), "/yao") - assert.NotPanics(t, func() { - LoadEngine(root) - }) +func TestReload(t *testing.T) { + defer Unload() + err := Load(config.Conf) + assert.Nil(t, err) -} - -// 从BinDataz载入引擎文件 -func TestLoadEngineBin(t *testing.T) { - defer Load(config.Conf) - assert.NotPanics(t, func() { - LoadEngine() - }) + Reload(config.Conf) + assert.Nil(t, err) + assert.Greater(t, len(api.APIs), 0) } diff --git a/engine/process.go b/engine/process.go index e82dd729..ebf536a2 100644 --- a/engine/process.go +++ b/engine/process.go @@ -1,30 +1,29 @@ package engine import ( - "path/filepath" + "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" - "github.com/yaoapp/yao/xfs" ) func init() { // 注册处理器 - gou.RegisterProcessHandler("xiang.main.Ping", processPing) // deprecated → utils.app.Ping @/utils/process.go - gou.AliasProcess("xiang.main.Ping", "xiang.sys.Ping") // deprecated + process.Register("xiang.main.Ping", processPing) // deprecated → utils.app.Ping @/utils/process.go + process.Alias("xiang.main.Ping", "xiang.sys.Ping") // deprecated - gou.RegisterProcessHandler("xiang.main.FileContent", processFileContent) // deprecated - gou.RegisterProcessHandler("xiang.main.AppFileContent", processAppFileContent) // deprecated + process.Register("xiang.main.FileContent", processFileContent) // deprecated + process.Register("xiang.main.AppFileContent", processAppFileContent) // deprecated - gou.RegisterProcessHandler("xiang.main.Inspect", processInspect) // deprecated → utils.app.Inspect @/utils/process.go - gou.AliasProcess("xiang.main.Inspect", "xiang.sys.Inspect") // deprecated + process.Register("xiang.main.Inspect", processInspect) // deprecated → utils.app.Inspect @/utils/process.go + process.Alias("xiang.main.Inspect", "xiang.sys.Inspect") // deprecated - gou.RegisterProcessHandler("xiang.main.Favicon", processFavicon) // deprecated + process.Register("xiang.main.Favicon", processFavicon) // deprecated } // processCreate 运行模型 MustCreate -func processPing(process *gou.Process) interface{} { +func processPing(process *process.Process) interface{} { res := map[string]interface{}{ "engine": share.BUILDNAME, "version": share.VERSION, @@ -33,37 +32,43 @@ func processPing(process *gou.Process) interface{} { } // processInspect 返回系统信息 -func processInspect(process *gou.Process) interface{} { - share.App.Icons.Set("favicon", "/api/xiang/favicon.ico") - return share.App.Public() +func processInspect(process *process.Process) interface{} { + return map[string]interface{}{ + "VERSION": fmt.Sprintf("%s %s", share.VERSION, share.PRVERSION), + "BUILDNAME": share.BUILDNAME, + "CONFIG": config.Conf, + } } // processFavicon 运行模型 MustCreate -func processFavicon(process *gou.Process) interface{} { - return xfs.DecodeString(share.App.Icons.Get("png").(string)) +func processFavicon(process *process.Process) interface{} { + // return xfs.DecodeString(share.App.Icons.Get("png").(string)) + return nil } // processFileContent 返回文件内容 -func processFileContent(process *gou.Process) interface{} { - process.ValidateArgNums(2) - filename := process.ArgsString(0) - encode := process.ArgsBool(1, true) - content := xfs.Stor.MustReadFile(filename) - if encode { - return xfs.Encode(content) - } - return string(content) +func processFileContent(process *process.Process) interface{} { + // process.ValidateArgNums(2) + // filename := process.ArgsString(0) + // encode := process.ArgsBool(1, true) + // content := xfs.Stor.MustReadFile(filename) + // if encode { + // return xfs.Encode(content) + // } + // return string(content) + return nil } // processAppFileContent 返回应用文件内容 -func processAppFileContent(process *gou.Process) interface{} { - process.ValidateArgNums(2) - fs := xfs.New(filepath.Join(config.Conf.Root, "data")) - filename := process.ArgsString(0) - encode := process.ArgsBool(1, true) - content := fs.MustReadFile(filename) - if encode { - return xfs.Encode(content) - } - return string(content) +func processAppFileContent(process *process.Process) interface{} { + // process.ValidateArgNums(2) + // fs := xfs.New(filepath.Join(config.Conf.Root, "data")) + // filename := process.ArgsString(0) + // encode := process.ArgsBool(1, true) + // content := fs.MustReadFile(filename) + // if encode { + // return xfs.Encode(content) + // } + // return string(content) + return nil } diff --git a/engine/process_test.go b/engine/process_test.go index 3aff66d7..49ebbab8 100644 --- a/engine/process_test.go +++ b/engine/process_test.go @@ -4,25 +4,25 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/yao/share" ) func TestProcessPing(t *testing.T) { - process := gou.NewProcess("xiang.main.ping") + process := process.New("xiang.main.ping") res, ok := processPing(process).(map[string]interface{}) assert.True(t, ok) assert.Equal(t, res["version"], share.VERSION) } func TestProcessAliasPing(t *testing.T) { - res, ok := gou.NewProcess("xiang.sys.Ping").Run().(map[string]interface{}) + res, ok := process.New("xiang.sys.Ping").Run().(map[string]interface{}) assert.True(t, ok) assert.Equal(t, res["version"], share.VERSION) } func TestProcessInspect(t *testing.T) { - res, ok := gou.NewProcess("xiang.sys.Inspect").Run().(share.AppInfo) + res, ok := process.New("xiang.sys.Inspect").Run().(map[string]interface{}) assert.True(t, ok) - assert.NotNil(t, res.Version) + assert.NotNil(t, res["VERSION"]) } diff --git a/engine/user_test.go b/engine/user_test.go deleted file mode 100644 index 03448057..00000000 --- a/engine/user_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package engine - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/yaoapp/yao/user" -) - -func TestUserAuth(t *testing.T) { - - res := user.Auth("email", "xiang@iqka.com", "A123456p+") - assert.True(t, res.Has("user")) - assert.True(t, res.Has("token")) - assert.True(t, res.Has("expires_at")) - assert.Panics(t, func() { - user.Auth("email", "xiang@iqka.com", "A123456p+22") - }) - - res = user.Auth("mobile", "13900001111", "U123456p+") - assert.True(t, res.Has("user")) - assert.True(t, res.Has("token")) - assert.True(t, res.Has("expires_at")) - - assert.Panics(t, func() { - user.Auth("email", "1390000111", "A123456p+22") - }) - -} diff --git a/flow/flow.go b/flow/flow.go index c0b21700..9b5e07cf 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -1,58 +1,20 @@ package flow import ( - "fmt" - "path/filepath" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/flow" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) // Load 加载业务逻辑编排 func Load(cfg config.Config) error { - if share.BUILDIN { - return LoadBuildIn("flows", "") - } - return LoadFrom(filepath.Join(cfg.Root, "flows"), "") -} - -// LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadFlowReturn(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + exts := []string{"*.flow.yao", "*.flow.json", "*.flow.jsonc"} + return application.App.Walk("flows", func(root, file string, isdir bool) error { + if isdir { + return nil } - }) - - if err != nil { + _, err := flow.Load(file, share.ID(root, file)) return err - } - - // Load Script - err = share.Walk(dir, ".js", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - flow := gou.SelectFlow(name) - if flow != nil { - script := share.ScriptName(filename) - content := share.ReadFile(filename) - flow.LoadScript(string(content), script) - } - }) - - return err -} - -// LoadBuildIn 从制品中读取 -func LoadBuildIn(dir string, prefix string) error { - return nil + }, exts...) } diff --git a/flow/flow_test.go b/flow/flow_test.go index 96619c5a..48195010 100644 --- a/flow/flow_test.go +++ b/flow/flow_test.go @@ -4,23 +4,24 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/flow" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/runtime" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - runtime.Load(config.Conf) - gou.Flows = make(map[string]*gou.Flow) + test.Prepare(t, config.Conf) + defer test.Clean() + Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } func check(t *testing.T) { - keys := []string{} - for key := range gou.Flows { - keys = append(keys, key) + ids := map[string]bool{} + for id := range flow.Flows { + ids[id] = true } - assert.Equal(t, 26, len(keys)) + assert.True(t, ids["tests.basic"]) + assert.True(t, ids["tests.session"]) } diff --git a/go.mod b/go.mod index 95fc400c..ccfee67e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/yaoapp/yao go 1.19 require ( - github.com/aliyun/alibaba-cloud-sdk-go v1.61.1286 github.com/blang/semver v3.5.1+incompatible github.com/caarlos0/env/v6 v6.9.1 github.com/elazarl/go-bindata-assetfs v1.0.1 @@ -15,14 +14,13 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mojocn/base64Captcha v1.3.5 github.com/rhysd/go-github-selfupdate v1.2.3 - github.com/spf13/afero v1.6.0 github.com/spf13/cobra v1.2.1 github.com/stretchr/testify v1.7.1 github.com/xuri/excelize/v2 v2.5.0 - github.com/yaoapp/gou v0.0.0-20211120135538-e5387704eb03 + github.com/yaoapp/gou v0.10.3 github.com/yaoapp/kun v0.9.0 github.com/yaoapp/xun v0.9.0 - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d + golang.org/x/crypto v0.7.0 ) require ( @@ -51,7 +49,7 @@ require ( github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.1 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/go-hclog v1.2.0 // indirect @@ -59,9 +57,8 @@ require ( github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/jmoiron/sqlx v1.3.4 // indirect - github.com/klauspost/compress v1.13.6 // indirect + github.com/klauspost/compress v1.16.3 // indirect github.com/leodido/go-urn v1.2.0 // indirect github.com/lib/pq v1.10.4 // indirect github.com/mattn/go-colorable v0.1.12 // indirect @@ -72,7 +69,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect + github.com/montanaflynn/stats v0.7.0 // indirect github.com/oklog/run v1.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -81,7 +78,6 @@ require ( github.com/robfig/cron/v3 v3.0.1 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/takama/daemon v1.0.0 // indirect github.com/tidwall/btree v1.1.0 // indirect github.com/tidwall/buntdb v1.2.9 // indirect github.com/tidwall/gjson v1.12.1 // indirect @@ -92,34 +88,32 @@ require ( github.com/tidwall/tinyqueue v0.1.1 // indirect github.com/ugorji/go/codec v1.1.7 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.1.1 // indirect - github.com/xdg-go/stringprep v1.0.3 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 // indirect - github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - go.mongodb.org/mongo-driver v1.10.2 // indirect + github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + go.mongodb.org/mongo-driver v1.11.3 // indirect golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect - golang.org/x/mod v0.4.2 // indirect - golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.7 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/tools v0.6.0 // indirect google.golang.org/genproto v0.0.0-20220526192754-51939a95c655 // indirect google.golang.org/grpc v1.46.2 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 - rogchap.com/v8go v0.7.0 // indirect + rogchap.com/v8go v0.8.0 // indirect ) // go env -w GOPRIVATE=github.com/yaoapp/* replace github.com/yaoapp/kun => ../kun // kun local -replace github.com/yaoapp/xun => ../xun // gou local +replace github.com/yaoapp/xun => ../xun // xun local replace github.com/yaoapp/gou => ../gou // gou local diff --git a/go.sum b/go.sum index 3fc5b8e6..78f5edc0 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1286 h1:+Ah33S/QRnOFp4dM6VV2gYDbFu3Cj4HrRy1VpOhBzm8= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1286/go.mod h1:9CMdKNL3ynIGPpfTcdwTvIm8SGuAZYYC4jFVSSvE1YQ= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -122,7 +120,6 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= @@ -157,8 +154,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -199,7 +197,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -241,25 +238,22 @@ github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w= github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= +github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -310,8 +304,9 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0= github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= +github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= @@ -335,7 +330,6 @@ github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o= github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= -github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -346,11 +340,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= @@ -369,8 +360,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/takama/daemon v1.0.0 h1:XS3VLnFKmqw2Z7fQ/dHRarrVjdir9G3z7BEP8osjizQ= -github.com/takama/daemon v1.0.0/go.mod h1:gKlhcjbqtBODg5v9H1nj5dU1a2j2GemtuWSNLD5rxOE= github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw= github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE= github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= @@ -401,26 +390,30 @@ github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 h1:EpI0bqf/eX9SdZDwlMmahKM+CDBgNbsXMhsN28XrM8o= github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= github.com/xuri/excelize/v2 v2.5.0 h1:nDDVfX0qaDuGjAvb+5zTd0Bxxoqa1Ffv9B4kiE23PTM= github.com/xuri/excelize/v2 v2.5.0/go.mod h1:rSu0C3papjzxQA3sdK8cU544TebhrPUoTOaGPIh0Q1A= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.mongodb.org/mongo-driver v1.10.2 h1:4Wk3cnqOrQCn0P92L3/mmurMxzdvWWs5J9jinAVKD+k= -go.mongodb.org/mongo-driver v1.10.2/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8= +go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= +go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -438,11 +431,14 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -481,8 +477,10 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -522,8 +520,10 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -548,8 +548,10 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -582,7 +584,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -602,9 +603,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -616,8 +618,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -673,8 +677,9 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -797,7 +802,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/helper/array.process.go b/helper/array.process.go index 16073cf5..8f211101 100644 --- a/helper/array.process.go +++ b/helper/array.process.go @@ -1,12 +1,12 @@ package helper import ( - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/maps" ) // ProcessArrayPluck xiang.helper.ArrayPluck 将多个数据记录集合,合并为一个数据记录集合 -func ProcessArrayPluck(process *gou.Process) interface{} { +func ProcessArrayPluck(process *process.Process) interface{} { process.ValidateArgNums(2) columns := process.ArgsStrings(0) pluck := process.ArgsMap(1) @@ -14,7 +14,7 @@ func ProcessArrayPluck(process *gou.Process) interface{} { } // ProcessArraySplit xiang.helper.ArraySplit 将多条数记录集合,分解为一个 columns:[]string 和 values: [][]interface{} -func ProcessArraySplit(process *gou.Process) interface{} { +func ProcessArraySplit(process *process.Process) interface{} { process.ValidateArgNums(1) records := process.ArgsRecords(0) columns, values := ArraySplit(records) @@ -25,7 +25,7 @@ func ProcessArraySplit(process *gou.Process) interface{} { } // ProcessArrayColumn xiang.helper.ArrayColumn 返回多条数据记录,指定字段数值。 -func ProcessArrayColumn(process *gou.Process) interface{} { +func ProcessArrayColumn(process *process.Process) interface{} { process.ValidateArgNums(2) records := process.ArgsRecords(0) name := process.ArgsString(1) @@ -34,7 +34,7 @@ func ProcessArrayColumn(process *gou.Process) interface{} { } // ProcessArrayKeep xiang.helper.ArrayKeep 仅保留指定键名的数据 -func ProcessArrayKeep(process *gou.Process) interface{} { +func ProcessArrayKeep(process *process.Process) interface{} { process.ValidateArgNums(2) records := process.ArgsRecords(0) columns := process.ArgsStrings(1) @@ -42,7 +42,7 @@ func ProcessArrayKeep(process *gou.Process) interface{} { } // ProcessArrayTree xiang.helper.ArrayTree 转换为属性结构 -func ProcessArrayTree(process *gou.Process) interface{} { +func ProcessArrayTree(process *process.Process) interface{} { process.ValidateArgNums(2) records := process.ArgsRecords(0) setting := process.ArgsMap(1) @@ -50,7 +50,7 @@ func ProcessArrayTree(process *gou.Process) interface{} { } // ProcessArrayUnique xiang.helper.ArrayUnique 数组排重 -func ProcessArrayUnique(process *gou.Process) interface{} { +func ProcessArrayUnique(process *process.Process) interface{} { process.ValidateArgNums(1) if arr, ok := process.Args[0].([]interface{}); ok { return ArrayUnique(arr) @@ -59,7 +59,7 @@ func ProcessArrayUnique(process *gou.Process) interface{} { } // ProcessArrayMapSet xiang.helper.ArrayMapSet 数组映射设定数值 -func ProcessArrayMapSet(process *gou.Process) interface{} { +func ProcessArrayMapSet(process *process.Process) interface{} { process.ValidateArgNums(3) arr, ok := process.Args[0].([]map[string]interface{}) if ok { @@ -71,7 +71,7 @@ func ProcessArrayMapSet(process *gou.Process) interface{} { } // ProcessArrayIndexes xiang.helper.ArrayIndexes 返回数组索引。 -func ProcessArrayIndexes(process *gou.Process) interface{} { +func ProcessArrayIndexes(process *process.Process) interface{} { process.ValidateArgNums(1) records := process.ArgsArray(0) res := []int{} @@ -82,7 +82,7 @@ func ProcessArrayIndexes(process *gou.Process) interface{} { } // ProcessArrayGet xiang.helper.ArrayGet 返回指定索引数据 -func ProcessArrayGet(process *gou.Process) interface{} { +func ProcessArrayGet(process *process.Process) interface{} { process.ValidateArgNums(2) records := process.ArgsArray(0) index := process.ArgsInt(1) diff --git a/helper/array_test.go b/helper/array_test.go index 099c9506..1d63b624 100644 --- a/helper/array_test.go +++ b/helper/array_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/maps" ) @@ -116,7 +116,7 @@ func TestProcessArrayPluck(t *testing.T) { "计费": map[string]interface{}{"key": "city", "value": "计费种类", "items": []map[string]interface{}{{"city": "北京", "计费种类": 6}, {"city": "西安", "计费种类": 3}}}, }, } - process := gou.NewProcess("xiang.helper.ArrayPluck", args...) + process := process.New("xiang.helper.ArrayPluck", args...) response := ProcessArrayPluck(process) assert.NotNil(t, response) items, ok := response.([]map[string]interface{}) @@ -137,7 +137,7 @@ func TestProcessArraySplit(t *testing.T) { {"name": "世纪互联蓝云", "short_name": "上海蓝云"}, }, } - process := gou.NewProcess("xiang.helper.ArraySplit", args...) + process := process.New("xiang.helper.ArraySplit", args...) response := process.Run() assert.NotNil(t, response) res, ok := response.(map[string]interface{}) @@ -159,7 +159,7 @@ func TestProcessArrayUnique(t *testing.T) { args := []interface{}{ []interface{}{1, 2, 3, 3}, } - process := gou.NewProcess("xiang.helper.ArrayUnique", args...) + process := process.New("xiang.helper.ArrayUnique", args...) response := process.Run() assert.NotNil(t, response) res, ok := response.([]interface{}) @@ -171,7 +171,7 @@ func TestProcessArrayIndexes(t *testing.T) { args := []interface{}{ []interface{}{1, 2, 3, 3}, } - response := gou.NewProcess("xiang.helper.ArrayIndexes", args...).Run() + response := process.New("xiang.helper.ArrayIndexes", args...).Run() assert.NotNil(t, response) res, ok := response.([]int) assert.True(t, ok) @@ -180,10 +180,10 @@ func TestProcessArrayIndexes(t *testing.T) { func TestProcessArrayGet(t *testing.T) { - response := gou.NewProcess("xiang.helper.ArrayGet", []interface{}{1, 2, 3, 3}, 2).Run() + response := process.New("xiang.helper.ArrayGet", []interface{}{1, 2, 3, 3}, 2).Run() assert.Equal(t, 3, response) - response = gou.NewProcess("xiang.helper.ArrayGet", []interface{}{1, 2, 3, 3}, 4).Run() + response = process.New("xiang.helper.ArrayGet", []interface{}{1, 2, 3, 3}, 4).Run() assert.Nil(t, response) } diff --git a/helper/captcha.go b/helper/captcha.go index 830b4fae..a5c354a5 100644 --- a/helper/captcha.go +++ b/helper/captcha.go @@ -5,7 +5,7 @@ import ( "image/color" "github.com/mojocn/base64Captcha" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" @@ -139,7 +139,7 @@ func captchaParseHexColorFast(s string) (c color.RGBA, err error) { } // ProcessCaptchaValidate xiang.helper.CaptchaValidate 校验图形/音频验证码 -func ProcessCaptchaValidate(process *gou.Process) interface{} { +func ProcessCaptchaValidate(process *process.Process) interface{} { process.ValidateArgNums(2) id := process.ArgsString(0) code := process.ArgsString(1) @@ -155,7 +155,7 @@ func ProcessCaptchaValidate(process *gou.Process) interface{} { } // ProcessCaptcha xiang.helper.Captcha 校验图形/音频验证码 -func ProcessCaptcha(process *gou.Process) interface{} { +func ProcessCaptcha(process *process.Process) interface{} { process.ValidateArgNums(1) option := CaptchaOption{ Width: any.Of(process.ArgsURLValue(0, "width", "240")).CInt(), diff --git a/helper/captcha_test.go b/helper/captcha_test.go index 6fb4cd8c..3da526e7 100644 --- a/helper/captcha_test.go +++ b/helper/captcha_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/maps" ) @@ -51,19 +51,19 @@ func TestProcessCaptcha(t *testing.T) { args := url.Values{} args.Add("type", "math") args.Add("lang", "zh") - process := gou.NewProcess("xiang.helper.Captcha", args) - res := process.Run().(maps.Map) + p := process.New("xiang.helper.Captcha", args) + res := p.Run().(maps.Map) assert.IsType(t, "string", res.Get("id")) assert.IsType(t, "string", res.Get("content")) value := captchaStore.Get(res.Get("id").(string), false) - process = gou.NewProcess("xiang.helper.CaptchaValidate", res.Get("id"), value) - assert.True(t, process.Run().(bool)) + p = process.New("xiang.helper.CaptchaValidate", res.Get("id"), value) + assert.True(t, p.Run().(bool)) assert.Panics(t, func() { - gou.NewProcess("xiang.helper.CaptchaValidate", res.Get("id"), "xxx").Run() + process.New("xiang.helper.CaptchaValidate", res.Get("id"), "xxx").Run() }) assert.Panics(t, func() { - gou.NewProcess("xiang.helper.CaptchaValidate", res.Get("id"), "").Run() + process.New("xiang.helper.CaptchaValidate", res.Get("id"), "").Run() }) } diff --git a/helper/case.go b/helper/case.go index d14a57e6..024a4312 100644 --- a/helper/case.go +++ b/helper/case.go @@ -2,7 +2,7 @@ package helper import ( jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" ) @@ -18,7 +18,7 @@ type CaseParam struct { func Case(params ...CaseParam) interface{} { for _, param := range params { if When(param.When) { - return gou.NewProcess(param.Process, param.Args...).Run() + return process.New(param.Process, param.Args...).Run() } } return nil @@ -39,7 +39,7 @@ func CaseParamOf(v interface{}) CaseParam { } // ProcessCase xiang.helper.Case Case条件判断 -func ProcessCase(process *gou.Process) interface{} { +func ProcessCase(process *process.Process) interface{} { process.ValidateArgNums(1) params := []CaseParam{} for _, v := range process.Args { diff --git a/helper/case_test.go b/helper/case_test.go index bf17c2aa..b8fde7fe 100644 --- a/helper/case_test.go +++ b/helper/case_test.go @@ -4,12 +4,12 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestCase(t *testing.T) { - gou.RegisterProcessHandler("xiang.unit.return", func(process *gou.Process) interface{} { + process.Register("xiang.unit.return", func(process *process.Process) interface{} { return process.Args }) @@ -38,7 +38,7 @@ func TestCase(t *testing.T) { func TestProcessCase(t *testing.T) { - gou.RegisterProcessHandler("xiang.unit.return", func(process *gou.Process) interface{} { + process.Register("xiang.unit.return", func(process *process.Process) interface{} { return process.Args }) @@ -56,7 +56,7 @@ func TestProcessCase(t *testing.T) { "args": []interface{}{"foo"}, }, } - process := gou.NewProcess("xiang.helper.Case", args...) + process := process.New("xiang.helper.Case", args...) res := process.Run().([]interface{}) assert.Equal(t, "world", res[0]) } diff --git a/helper/control.process.go b/helper/control.process.go index f1183815..2a9a5ac4 100644 --- a/helper/control.process.go +++ b/helper/control.process.go @@ -1,17 +1,17 @@ package helper import ( - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" ) // ProcessReturn xiang.helper.Return 返回数值 -func ProcessReturn(process *gou.Process) interface{} { +func ProcessReturn(process *process.Process) interface{} { return process.Args } // ProcessThrow xiang.helper.Throw 抛出异常 -func ProcessThrow(process *gou.Process) interface{} { +func ProcessThrow(process *process.Process) interface{} { process.ValidateArgNums(2) message := process.ArgsString(0) code := process.ArgsInt(1) diff --git a/helper/control_test.go b/helper/control_test.go index 78881ff1..b40aafe8 100644 --- a/helper/control_test.go +++ b/helper/control_test.go @@ -4,19 +4,19 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" ) func TestProcessThrow(t *testing.T) { e := exception.New("Someting error", 500) assert.PanicsWithValue(t, *e, func() { - gou.NewProcess("xiang.helper.Throw", "Someting error", 500).Run() + process.New("xiang.helper.Throw", "Someting error", 500).Run() }) } func TestProcessReturn(t *testing.T) { - v := gou.NewProcess("xiang.helper.Return", "hello", "world").Run().([]interface{}) + v := process.New("xiang.helper.Return", "hello", "world").Run().([]interface{}) assert.Equal(t, "hello", v[0]) assert.Equal(t, "world", v[1]) } diff --git a/helper/env.process.go b/helper/env.process.go index e72ea6b9..3fae0853 100644 --- a/helper/env.process.go +++ b/helper/env.process.go @@ -5,18 +5,18 @@ import ( "fmt" "os" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) // ProcessEnvGet xiang.helper.EnvGet 读取ENV -func ProcessEnvGet(process *gou.Process) interface{} { +func ProcessEnvGet(process *process.Process) interface{} { process.ValidateArgNums(1) name := process.ArgsString(0) return os.Getenv(name) } // ProcessEnvSet xiang.helper.EnvSet 设置ENV -func ProcessEnvSet(process *gou.Process) interface{} { +func ProcessEnvSet(process *process.Process) interface{} { process.ValidateArgNums(2) name := process.ArgsString(0) value := process.ArgsString(1) @@ -24,7 +24,7 @@ func ProcessEnvSet(process *gou.Process) interface{} { } // ProcessEnvMultiGet xiang.helper.MultiGet 读取ENV -func ProcessEnvMultiGet(process *gou.Process) interface{} { +func ProcessEnvMultiGet(process *process.Process) interface{} { process.ValidateArgNums(1) res := map[string]string{} for i := range process.Args { @@ -35,7 +35,7 @@ func ProcessEnvMultiGet(process *gou.Process) interface{} { } // ProcessEnvMultiSet xiang.helper.MultiSet 设置ENV -func ProcessEnvMultiSet(process *gou.Process) interface{} { +func ProcessEnvMultiSet(process *process.Process) interface{} { process.ValidateArgNums(1) envs := process.ArgsMap(0) message := "" diff --git a/helper/env_test.go b/helper/env_test.go index 91f51acd..fa354875 100644 --- a/helper/env_test.go +++ b/helper/env_test.go @@ -4,21 +4,21 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/maps" ) func TestProcessEnv(t *testing.T) { - err := gou.NewProcess("xiang.helper.EnvSet", "XIANG_UNIT_TEST", "FOO").Run() + err := process.New("xiang.helper.EnvSet", "XIANG_UNIT_TEST", "FOO").Run() assert.Nil(t, err) - test := gou.NewProcess("xiang.helper.EnvGet", "XIANG_UNIT_TEST").Run().(string) + test := process.New("xiang.helper.EnvGet", "XIANG_UNIT_TEST").Run().(string) assert.Equal(t, "FOO", test) } func TestProcessEnvMulti(t *testing.T) { - err := gou.NewProcess("xiang.helper.EnvMultiSet", maps.Map{"XIANG_UNIT_TEST": "FOO", "XIANG_UNIT_TEST2": "BAR"}).Run() + err := process.New("xiang.helper.EnvMultiSet", maps.Map{"XIANG_UNIT_TEST": "FOO", "XIANG_UNIT_TEST2": "BAR"}).Run() assert.Nil(t, err) - test := gou.NewProcess("xiang.helper.EnvMultiGet", "XIANG_UNIT_TEST", "XIANG_UNIT_TEST2").Run().(map[string]string) + test := process.New("xiang.helper.EnvMultiGet", "XIANG_UNIT_TEST", "XIANG_UNIT_TEST2").Run().(map[string]string) assert.Equal(t, "FOO", test["XIANG_UNIT_TEST"]) assert.Equal(t, "BAR", test["XIANG_UNIT_TEST2"]) } diff --git a/helper/hex.process.go b/helper/hex.process.go index d43be53a..08684f3e 100644 --- a/helper/hex.process.go +++ b/helper/hex.process.go @@ -3,12 +3,12 @@ package helper import ( "encoding/hex" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/log" ) // ProcessHexToString xiang.helper.HexToString -func ProcessHexToString(process *gou.Process) interface{} { +func ProcessHexToString(process *process.Process) interface{} { process.ValidateArgNums(1) switch process.Args[0].(type) { diff --git a/helper/hex_test.go b/helper/hex_test.go index dd1fee71..4af4cfd5 100644 --- a/helper/hex_test.go +++ b/helper/hex_test.go @@ -1,64 +1,22 @@ package helper import ( - "os" - "path" "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/flow" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/share" + "github.com/yaoapp/gou/process" ) func TestProcessHexToString(t *testing.T) { - res, err := gou.NewProcess("xiang.helper.HexToString", []byte{0x0, 0x1}).Exec() + res, err := process.New("xiang.helper.HexToString", []byte{0x0, 0x1}).Exec() assert.Nil(t, err) assert.Equal(t, "0001", res) - res, err = gou.NewProcess("xiang.helper.HexToString", string([]byte{0x0, 0x1})).Exec() + res, err = process.New("xiang.helper.HexToString", string([]byte{0x0, 0x1})).Exec() assert.Nil(t, err) assert.Equal(t, "0001", res) - res, err = gou.NewProcess("xiang.helper.HexToString", 1024).Exec() + res, err = process.New("xiang.helper.HexToString", 1024).Exec() assert.Nil(t, err) assert.Nil(t, res) } - -func TestProcessHexToStringInFlow(t *testing.T) { - testflow := path.Join(os.Getenv("YAO_DEV"), "tests", "flows", "helper") - flow.LoadFrom(testflow, "helper.") - - res, err := gou.NewProcess("flows.helper.HexToString").Exec() - assert.Nil(t, err) - assert.Equal(t, "6162", res) // ab -} - -func TestProcessHexToStringInScript(t *testing.T) { - runtime.Load(config.Conf) - testscirpt := path.Join(os.Getenv("YAO_DEV"), "tests", "scripts") - share.LoadFrom(testscirpt) - - res, err := gou.NewProcess("scripts.helper.HexToStringString").Exec() - assert.Nil(t, err) - assert.Equal(t, "6162", res) // ab - - res, err = gou.NewProcess("scripts.helper.HexToStringBytes", []byte{0x0, 0x1}).Exec() - assert.Nil(t, err) - assert.Equal(t, "0001", res) // []byte{0x0, 0x1} -} - -func TestProcessBufferInScript(t *testing.T) { - testscirpt := path.Join(os.Getenv("YAO_DEV"), "tests", "scripts") - share.LoadFrom(testscirpt) - - res, err := gou.NewProcess("scripts.helper.Buffer").Exec() - assert.Nil(t, err) - - str, ok := res.(string) - assert.True(t, ok) - assert.Equal(t, []byte{0x0, 0x1}, []byte(str)) - -} diff --git a/helper/if.go b/helper/if.go index c41a7558..fd4c1097 100644 --- a/helper/if.go +++ b/helper/if.go @@ -1,19 +1,21 @@ package helper -import "github.com/yaoapp/gou" +import ( + "github.com/yaoapp/gou/process" +) // IF 条件判断 func IF(param CaseParam, paramElse ...CaseParam) interface{} { if When(param.When) { - return gou.NewProcess(param.Process, param.Args...).Run() + return process.New(param.Process, param.Args...).Run() } else if len(paramElse) > 0 && When(paramElse[0].When) { - return gou.NewProcess(paramElse[0].Process, paramElse[0].Args...).Run() + return process.New(paramElse[0].Process, paramElse[0].Args...).Run() } return nil } // ProcessIF xiang.helper.IF IF条件判断 -func ProcessIF(process *gou.Process) interface{} { +func ProcessIF(process *process.Process) interface{} { process.ValidateArgNums(1) params := []CaseParam{} for _, v := range process.Args { diff --git a/helper/if_test.go b/helper/if_test.go index 2e4be489..544d4219 100644 --- a/helper/if_test.go +++ b/helper/if_test.go @@ -4,12 +4,12 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestIF(t *testing.T) { - gou.RegisterProcessHandler("xiang.unit.return", func(process *gou.Process) interface{} { + process.Register("xiang.unit.return", func(process *process.Process) interface{} { return process.Args }) @@ -38,7 +38,7 @@ func TestIF(t *testing.T) { func TestProcessIF(t *testing.T) { - gou.RegisterProcessHandler("xiang.unit.return", func(process *gou.Process) interface{} { + process.Register("xiang.unit.return", func(process *process.Process) interface{} { return process.Args }) @@ -56,7 +56,7 @@ func TestProcessIF(t *testing.T) { "args": []interface{}{"foo"}, }, } - process := gou.NewProcess("xiang.helper.IF", args...) + process := process.New("xiang.helper.IF", args...) res := process.Run().([]interface{}) assert.Equal(t, "world", res[0]) } diff --git a/helper/jwt.go b/helper/jwt.go index fb04926f..6583cfb5 100644 --- a/helper/jwt.go +++ b/helper/jwt.go @@ -5,7 +5,7 @@ import ( "time" "github.com/golang-jwt/jwt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" @@ -119,7 +119,7 @@ func JwtMake(id int, data map[string]interface{}, option map[string]interface{}, } // ProcessJwtMake xiang.helper.JwtMake 生成JWT -func ProcessJwtMake(process *gou.Process) interface{} { +func ProcessJwtMake(process *process.Process) interface{} { process.ValidateArgNums(2) id := process.ArgsInt(0) data := process.ArgsMap(1) @@ -131,7 +131,7 @@ func ProcessJwtMake(process *gou.Process) interface{} { } // ProcessJwtValidate xiang.helper.JwtValidate 校验JWT -func ProcessJwtValidate(process *gou.Process) interface{} { +func ProcessJwtValidate(process *process.Process) interface{} { process.ValidateArgNums(1) tokenString := process.ArgsString(0) return JwtValidate(tokenString) diff --git a/helper/jwt_test.go b/helper/jwt_test.go index 60d977a0..b1fdb117 100644 --- a/helper/jwt_test.go +++ b/helper/jwt_test.go @@ -5,7 +5,7 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestJwt(t *testing.T) { @@ -26,13 +26,13 @@ func TestProcessJwt(t *testing.T) { data := map[string]interface{}{"hello": "world", "id": 1} option := map[string]interface{}{"subject": "Unit Test", "audience": "Test", "issuer": "UnitTest", "timeout": 1, "sid": ""} args := []interface{}{1, data, option} - process := gou.NewProcess("xiang.helper.JwtMake", args...) - token := process.Run().(JwtToken) + p := process.New("xiang.helper.JwtMake", args...) + token := p.Run().(JwtToken) tokenString := token.Token - res := gou.NewProcess("xiang.helper.JwtValidate", tokenString).Run().(*JwtClaims) + res := process.New("xiang.helper.JwtValidate", tokenString).Run().(*JwtClaims) assert.Equal(t, float64(1), res.Data["id"]) assert.Equal(t, "world", res.Data["hello"]) assert.Equal(t, "UnitTest", res.Issuer) time.Sleep(2 * time.Second) - assert.Panics(t, func() { gou.NewProcess("xiang.helper.JwtValidate", tokenString).Run() }) + assert.Panics(t, func() { process.New("xiang.helper.JwtValidate", tokenString).Run() }) } diff --git a/helper/map.process.go b/helper/map.process.go index a3a7be04..fdabc757 100644 --- a/helper/map.process.go +++ b/helper/map.process.go @@ -3,25 +3,25 @@ package helper import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) // ProcessMapValues xiang.helper.MapValues 返回映射表的数值 -func ProcessMapValues(process *gou.Process) interface{} { +func ProcessMapValues(process *process.Process) interface{} { process.ValidateArgNums(1) record := process.ArgsMap(0) return MapValues(record) } // ProcessMapKeys xiang.helper.MapKeys 返回映射表的键 -func ProcessMapKeys(process *gou.Process) interface{} { +func ProcessMapKeys(process *process.Process) interface{} { process.ValidateArgNums(1) record := process.ArgsMap(0) return MapKeys(record) } // ProcessMapGet xiang.helper.MapGet 返回映射表给定键的值 -func ProcessMapGet(process *gou.Process) interface{} { +func ProcessMapGet(process *process.Process) interface{} { process.ValidateArgNums(2) record := process.ArgsMap(0) key := process.ArgsString(1) @@ -29,7 +29,7 @@ func ProcessMapGet(process *gou.Process) interface{} { } // ProcessMapSet xiang.helper.MapSet 设定键值,返回映射表给定键的值 -func ProcessMapSet(process *gou.Process) interface{} { +func ProcessMapSet(process *process.Process) interface{} { process.ValidateArgNums(3) record := process.ArgsMap(0) key := process.ArgsString(1) @@ -38,7 +38,7 @@ func ProcessMapSet(process *gou.Process) interface{} { } // ProcessMapDel xiang.helper.MapDel 删除给定键, 返回映射表 -func ProcessMapDel(process *gou.Process) interface{} { +func ProcessMapDel(process *process.Process) interface{} { process.ValidateArgNums(2) record := process.ArgsMap(0) key := process.ArgsString(1) @@ -46,7 +46,7 @@ func ProcessMapDel(process *gou.Process) interface{} { } // ProcessMapMultiDel xiang.helper.MapMultiDel 删除一组给定键, 返回映射表 -func ProcessMapMultiDel(process *gou.Process) interface{} { +func ProcessMapMultiDel(process *process.Process) interface{} { process.ValidateArgNums(2) record := process.ArgsMap(0) keys := []string{} @@ -57,7 +57,7 @@ func ProcessMapMultiDel(process *gou.Process) interface{} { } // ProcessMapToArray xiang.helper.MapToArray 映射转换为 KeyValue 数组 -func ProcessMapToArray(process *gou.Process) interface{} { +func ProcessMapToArray(process *process.Process) interface{} { process.ValidateArgNums(1) m := process.ArgsMap(0) res := []map[string]interface{}{} diff --git a/helper/map_test.go b/helper/map_test.go index c7c6d52e..46d8fff3 100644 --- a/helper/map_test.go +++ b/helper/map_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestProcessMapDel(t *testing.T) { @@ -12,7 +12,7 @@ func TestProcessMapDel(t *testing.T) { map[string]interface{}{"foo": "Value1", "bar": "Value2"}, "bar", } - new := gou.NewProcess("xiang.helper.MapDel", args...).Run().(map[string]interface{}) + new := process.New("xiang.helper.MapDel", args...).Run().(map[string]interface{}) _, has := new["bar"] assert.False(t, has) assert.Equal(t, "Value1", new["foo"]) @@ -24,11 +24,11 @@ func TestProcessGetSet(t *testing.T) { "bar", "Value2", } - new := gou.NewProcess("xiang.helper.MapSet", args...).Run().(map[string]interface{}) + new := process.New("xiang.helper.MapSet", args...).Run().(map[string]interface{}) assert.Equal(t, "Value1", new["foo"]) assert.Equal(t, "Value2", new["bar"]) - bar := gou.NewProcess("xiang.helper.MapGet", new, "bar").Run().(string) + bar := process.New("xiang.helper.MapGet", new, "bar").Run().(string) assert.Equal(t, "Value2", bar) } @@ -36,7 +36,7 @@ func TestProcessMapKeys(t *testing.T) { args := []interface{}{ map[string]interface{}{"foo": "Value1", "bar": "Value2"}, } - keys := gou.NewProcess("xiang.helper.MapKeys", args...).Run().([]string) + keys := process.New("xiang.helper.MapKeys", args...).Run().([]string) assert.Contains(t, keys, "foo") assert.Contains(t, keys, "bar") } @@ -45,7 +45,7 @@ func TestProcessMapValues(t *testing.T) { args := []interface{}{ map[string]interface{}{"foo": "Value1", "bar": "Value2"}, } - values := gou.NewProcess("xiang.helper.MapValues", args...).Run().([]interface{}) + values := process.New("xiang.helper.MapValues", args...).Run().([]interface{}) assert.Contains(t, values, "Value1") assert.Contains(t, values, "Value2") } @@ -56,14 +56,14 @@ func TestProcessMapMultiDel(t *testing.T) { "foo", "bar", } - new := gou.NewProcess("xiang.helper.MapMultiDel", args...).Run().(map[string]interface{}) + new := process.New("xiang.helper.MapMultiDel", args...).Run().(map[string]interface{}) assert.Nil(t, new["foo"]) assert.Nil(t, new["bar"]) } func TestProcessMapToArray(t *testing.T) { - arr := gou.NewProcess("xiang.helper.MapToArray", map[string]interface{}{ + arr := process.New("xiang.helper.MapToArray", map[string]interface{}{ "foo": "Value1", "bar": "Value2", }).Run().([]map[string]interface{}) diff --git a/helper/password.go b/helper/password.go index 1887959e..1d5b56c5 100644 --- a/helper/password.go +++ b/helper/password.go @@ -1,7 +1,7 @@ package helper import ( - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" "golang.org/x/crypto/bcrypt" ) @@ -17,7 +17,7 @@ func PasswordValidate(password string, passwordHash string) bool { } // ProcessPasswordValidate xiang.helper.PasswordValidate 校验密码 -func ProcessPasswordValidate(process *gou.Process) interface{} { +func ProcessPasswordValidate(process *process.Process) interface{} { process.ValidateArgNums(2) return PasswordValidate(process.ArgsString(0), process.ArgsString(1)) } diff --git a/helper/password_test.go b/helper/password_test.go index e03883a7..b1e46e26 100644 --- a/helper/password_test.go +++ b/helper/password_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestPassword(t *testing.T) { @@ -18,13 +18,13 @@ func TestProcessPassword(t *testing.T) { pwd := "U123456p+" hash := "$2a$04$TS/rWBs66jADjQl8fa.w..ivkNAjH8d4sI1OPGvEB9Leed6EpzIF2" args := []interface{}{pwd, hash} - process := gou.NewProcess("xiang.helper.PasswordValidate", args...) - res := process.Run() + p := process.New("xiang.helper.PasswordValidate", args...) + res := p.Run() assert.True(t, res.(bool)) args = []interface{}{pwd, "123456"} - process = gou.NewProcess("xiang.helper.PasswordValidate", args...) + p = process.New("xiang.helper.PasswordValidate", args...) assert.Panics(t, func() { - process.Run() + p.Run() }) } diff --git a/helper/process.go b/helper/process.go index e5a2a999..2faca28b 100644 --- a/helper/process.go +++ b/helper/process.go @@ -3,89 +3,89 @@ package helper import ( "time" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/utils" ) func init() { // 注册处理器 - gou.RegisterProcessHandler("xiang.helper.ArrayGet", ProcessArrayGet) // deprecated → utils.arr.Get @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.ArrayIndexes", ProcessArrayIndexes) // deprecated → utils.arr.Indexes @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.ArrayPluck", ProcessArrayPluck) // deprecated → utils.arr.Pluck @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.ArraySplit", ProcessArraySplit) // deprecated → utils.arr.Split @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.ArrayColumn", ProcessArrayColumn) // deprecated → utils.arr.Column @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.ArrayKeep", ProcessArrayKeep) // deprecated → utils.arr.Keep @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.ArrayTree", ProcessArrayTree) // deprecated → utils.arr.Tree @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.ArrayUnique", ProcessArrayUnique) // deprecated → utils.arr.Unique @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.ArrayMapSet", ProcessArrayMapSet) // deprecated → utils.arr.MapSet @/utils/process.go + process.Register("xiang.helper.ArrayGet", ProcessArrayGet) // deprecated → utils.arr.Get @/utils/process.go + process.Register("xiang.helper.ArrayIndexes", ProcessArrayIndexes) // deprecated → utils.arr.Indexes @/utils/process.go + process.Register("xiang.helper.ArrayPluck", ProcessArrayPluck) // deprecated → utils.arr.Pluck @/utils/process.go + process.Register("xiang.helper.ArraySplit", ProcessArraySplit) // deprecated → utils.arr.Split @/utils/process.go + process.Register("xiang.helper.ArrayColumn", ProcessArrayColumn) // deprecated → utils.arr.Column @/utils/process.go + process.Register("xiang.helper.ArrayKeep", ProcessArrayKeep) // deprecated → utils.arr.Keep @/utils/process.go + process.Register("xiang.helper.ArrayTree", ProcessArrayTree) // deprecated → utils.arr.Tree @/utils/process.go + process.Register("xiang.helper.ArrayUnique", ProcessArrayUnique) // deprecated → utils.arr.Unique @/utils/process.go + process.Register("xiang.helper.ArrayMapSet", ProcessArrayMapSet) // deprecated → utils.arr.MapSet @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.MapKeys", ProcessMapKeys) // deprecated → utils.map.Keys @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.MapValues", ProcessMapValues) // deprecated → utils.map.Values @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.MapToArray", ProcessMapToArray) // deprecated → utils.map.Array @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.MapGet", ProcessMapGet) // deprecated → utils.map.Get @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.MapSet", ProcessMapSet) // deprecated → utils.map.Set @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.MapDel", ProcessMapDel) // deprecated → utils.map.Del @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.MapMultiDel", ProcessMapMultiDel) // deprecated → utils.map.DelMany @/utils/process.go + process.Register("xiang.helper.MapKeys", ProcessMapKeys) // deprecated → utils.map.Keys @/utils/process.go + process.Register("xiang.helper.MapValues", ProcessMapValues) // deprecated → utils.map.Values @/utils/process.go + process.Register("xiang.helper.MapToArray", ProcessMapToArray) // deprecated → utils.map.Array @/utils/process.go + process.Register("xiang.helper.MapGet", ProcessMapGet) // deprecated → utils.map.Get @/utils/process.go + process.Register("xiang.helper.MapSet", ProcessMapSet) // deprecated → utils.map.Set @/utils/process.go + process.Register("xiang.helper.MapDel", ProcessMapDel) // deprecated → utils.map.Del @/utils/process.go + process.Register("xiang.helper.MapMultiDel", ProcessMapMultiDel) // deprecated → utils.map.DelMany @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.HexToString", ProcessHexToString) // deprecated + process.Register("xiang.helper.HexToString", ProcessHexToString) // deprecated → utils.str.Hex @/utils/process.go new 2022.2.3 - gou.RegisterProcessHandler("xiang.helper.StrConcat", ProcessStrConcat) // deprecated → utils.str.Concat @/utils/process.go + process.Register("xiang.helper.StrConcat", ProcessStrConcat) // deprecated → utils.str.Concat @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.Captcha", ProcessCaptcha) // deprecated → utils.captcha.Make @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.CaptchaValidate", ProcessCaptchaValidate) // deprecated → utils.captcha.Verify @/utils/process.go + process.Register("xiang.helper.Captcha", ProcessCaptcha) // deprecated → utils.captcha.Make @/utils/process.go + process.Register("xiang.helper.CaptchaValidate", ProcessCaptchaValidate) // deprecated → utils.captcha.Verify @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.PasswordValidate", ProcessPasswordValidate) // deprecated → utils.pwd.Verify @/utils/process.go + process.Register("xiang.helper.PasswordValidate", ProcessPasswordValidate) // deprecated → utils.pwd.Verify @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.JwtMake", ProcessJwtMake) // deprecated → utils.jwt.Make @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.JwtValidate", ProcessJwtValidate) // deprecated → utils.jwt.Verify @/utils/process.go + process.Register("xiang.helper.JwtMake", ProcessJwtMake) // deprecated → utils.jwt.Make @/utils/process.go + process.Register("xiang.helper.JwtValidate", ProcessJwtValidate) // deprecated → utils.jwt.Verify @/utils/process.go - gou.RegisterProcessHandler("xiang.helper.For", ProcessFor) // deprecated → utils.flow.For @/utils/process.go - gou.AliasProcess("xiang.helper.For", "xiang.flow.For") // deprecated - gou.RegisterProcessHandler("xiang.helper.Each", ProcessEach) // deprecated → utils.flow.Each @/utils/process.go - gou.AliasProcess("xiang.helper.Each", "xiang.flow.Each") // deprecated - gou.RegisterProcessHandler("xiang.helper.Case", ProcessCase) // deprecated → utils.flow.Case @/utils/process.go - gou.AliasProcess("xiang.helper.Case", "xiang.flow.Case") // deprecated - gou.RegisterProcessHandler("xiang.helper.IF", ProcessIF) // deprecated → utils.flow.IF @/utils/process.go - gou.AliasProcess("xiang.helper.IF", "xiang.flow.IF") // deprecated - gou.RegisterProcessHandler("xiang.helper.Throw", ProcessThrow) // deprecated → utils.flow.Throw @/utils/process.go - gou.AliasProcess("xiang.helper.Throw", "xiang.flow.Throw") // deprecated - gou.RegisterProcessHandler("xiang.helper.Return", ProcessReturn) // deprecated → utils.flow.Return @/utils/process.go - gou.AliasProcess("xiang.helper.Return", "xiang.flow.Return") // deprecated + process.Register("xiang.helper.For", ProcessFor) // deprecated → utils.flow.For @/utils/process.go + process.Alias("xiang.helper.For", "xiang.flow.For") // deprecated + process.Register("xiang.helper.Each", ProcessEach) // deprecated → utils.flow.Each @/utils/process.go + process.Alias("xiang.helper.Each", "xiang.flow.Each") // deprecated + process.Register("xiang.helper.Case", ProcessCase) // deprecated → utils.flow.Case @/utils/process.go + process.Alias("xiang.helper.Case", "xiang.flow.Case") // deprecated + process.Register("xiang.helper.IF", ProcessIF) // deprecated → utils.flow.IF @/utils/process.go + process.Alias("xiang.helper.IF", "xiang.flow.IF") // deprecated + process.Register("xiang.helper.Throw", ProcessThrow) // deprecated → utils.flow.Throw @/utils/process.go + process.Alias("xiang.helper.Throw", "xiang.flow.Throw") // deprecated + process.Register("xiang.helper.Return", ProcessReturn) // deprecated → utils.flow.Return @/utils/process.go + process.Alias("xiang.helper.Return", "xiang.flow.Return") // deprecated - gou.RegisterProcessHandler("xiang.helper.EnvSet", ProcessEnvSet) // deprecated → utils.env.Set @/utils/process.go - gou.AliasProcess("xiang.helper.EnvSet", "xiang.env.Set") // deprecated - gou.AliasProcess("xiang.helper.EnvSet", "yao.env.Set") // deprecated + process.Register("xiang.helper.EnvSet", ProcessEnvSet) // deprecated → utils.env.Set @/utils/process.go + process.Alias("xiang.helper.EnvSet", "xiang.env.Set") // deprecated + process.Alias("xiang.helper.EnvSet", "yao.env.Set") // deprecated - gou.RegisterProcessHandler("xiang.helper.EnvGet", ProcessEnvGet) // deprecated → utils.env.Get @/utils/process.go - gou.AliasProcess("xiang.helper.EnvGet", "xiang.env.Get") // deprecated - gou.AliasProcess("xiang.helper.EnvGet", "yao.env.Get") // deprecated + process.Register("xiang.helper.EnvGet", ProcessEnvGet) // deprecated → utils.env.Get @/utils/process.go + process.Alias("xiang.helper.EnvGet", "xiang.env.Get") // deprecated + process.Alias("xiang.helper.EnvGet", "yao.env.Get") // deprecated - gou.RegisterProcessHandler("xiang.helper.EnvMultiSet", ProcessEnvMultiSet) // deprecated → utils.env.SetMany @/utils/process.go - gou.AliasProcess("xiang.helper.EnvMultiSet", "xiang.env.MultiSet") // deprecated - gou.AliasProcess("xiang.helper.EnvMultiSet", "yao.env.MultiSet") // deprecated + process.Register("xiang.helper.EnvMultiSet", ProcessEnvMultiSet) // deprecated → utils.env.SetMany @/utils/process.go + process.Alias("xiang.helper.EnvMultiSet", "xiang.env.MultiSet") // deprecated + process.Alias("xiang.helper.EnvMultiSet", "yao.env.MultiSet") // deprecated - gou.RegisterProcessHandler("xiang.helper.EnvMultiGet", ProcessEnvMultiGet) // deprecated → utils.env.GetMany @/utils/process.go - gou.AliasProcess("xiang.helper.EnvMultiGet", "xiang.env.MultiGet") // deprecated - gou.AliasProcess("xiang.helper.EnvMultiGet", "yao.env.MultiGet") // deprecated + process.Register("xiang.helper.EnvMultiGet", ProcessEnvMultiGet) // deprecated → utils.env.GetMany @/utils/process.go + process.Alias("xiang.helper.EnvMultiGet", "xiang.env.MultiGet") // deprecated + process.Alias("xiang.helper.EnvMultiGet", "yao.env.MultiGet") // deprecated - gou.RegisterProcessHandler("xiang.helper.Print", ProcessPrint) // deprecated → utils.fmt.Println @/utils/process.go - gou.AliasProcess("xiang.helper.Print", "xiang.sys.Print") // deprecated + process.Register("xiang.helper.Print", ProcessPrint) // deprecated → utils.fmt.Println @/utils/process.go + process.Alias("xiang.helper.Print", "xiang.sys.Print") // deprecated - gou.RegisterProcessHandler("xiang.flow.Sleep", ProcessSleep) // deprecated → utils.time.Sleep @/utils/process.go - gou.AliasProcess("xiang.flow.Sleep", "xiang.sys.Sleep") // deprecated - gou.AliasProcess("xiang.flow.Sleep", "yao.sys.Sleep") // deprecated + process.Register("xiang.flow.Sleep", ProcessSleep) // deprecated → utils.time.Sleep @/utils/process.go + process.Alias("xiang.flow.Sleep", "xiang.sys.Sleep") // deprecated + process.Alias("xiang.flow.Sleep", "yao.sys.Sleep") // deprecated } // ProcessPrint xiang.helper.Print 打印语句 -func ProcessPrint(process *gou.Process) interface{} { +func ProcessPrint(process *process.Process) interface{} { process.ValidateArgNums(1) utils.Dump(process.Args...) return nil } // ProcessSleep xiang.flow.Sleep 等待 -func ProcessSleep(process *gou.Process) interface{} { +func ProcessSleep(process *process.Process) interface{} { process.ValidateArgNums(1) ms := process.ArgsInt(0) time.Sleep(time.Duration((ms * int(time.Millisecond)))) diff --git a/helper/range.go b/helper/range.go index d9beda06..cd9c63e0 100644 --- a/helper/range.go +++ b/helper/range.go @@ -5,8 +5,8 @@ import ( "regexp" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/query/share" + "github.com/yaoapp/gou/helper" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" ) @@ -69,14 +69,14 @@ func For(from int, to int, p Process) { "value": i, } args := bindArgs(p.Args, bindings) - gou.NewProcess(p.Process, args...).Run() + process.New(p.Process, args...).Run() } } func bindArgs(args []interface{}, bindings map[string]interface{}) []interface{} { new := []interface{}{} for i := range args { - new = append(new, share.Bind(args[i], bindings, reVar)) + new = append(new, helper.Bind(args[i], bindings, reVar)) } return new } @@ -89,7 +89,7 @@ func rangeString(v string, p Process) { "value": value, } args := bindArgs(p.Args, bindings) - gou.NewProcess(p.Process, args...).Run() + process.New(p.Process, args...).Run() } } @@ -101,7 +101,7 @@ func rangeMap(v map[string]interface{}, p Process) { "value": value, } args := bindArgs(p.Args, bindings) - gou.NewProcess(p.Process, args...).Run() + process.New(p.Process, args...).Run() } } @@ -112,7 +112,7 @@ func rangeArray(v []interface{}, p Process) { "value": value, } args := bindArgs(p.Args, bindings) - gou.NewProcess(p.Process, args...).Run() + process.New(p.Process, args...).Run() } } @@ -141,7 +141,7 @@ func ProcessOf(v map[string]interface{}) Process { } // ProcessEach xiang.helper.Each 循环过程控制 -func ProcessEach(process *gou.Process) interface{} { +func ProcessEach(process *process.Process) interface{} { process.ValidateArgNums(2) v := process.Args[0] p := ProcessOf(process.ArgsMap(1)) @@ -150,7 +150,7 @@ func ProcessEach(process *gou.Process) interface{} { } // ProcessFor xiang.helper.For 循环过程控制 -func ProcessFor(process *gou.Process) interface{} { +func ProcessFor(process *process.Process) interface{} { process.ValidateArgNums(3) from := process.ArgsInt(0) to := process.ArgsInt(1) diff --git a/helper/string.process.go b/helper/string.process.go index 2daaf37a..bc1d760f 100644 --- a/helper/string.process.go +++ b/helper/string.process.go @@ -3,11 +3,11 @@ package helper import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) // ProcessStrConcat xiang.helper.StrConcat 连接字符串 -func ProcessStrConcat(process *gou.Process) interface{} { +func ProcessStrConcat(process *process.Process) interface{} { process.ValidateArgNums(2) res := "" for i := range process.Args { diff --git a/helper/string_test.go b/helper/string_test.go index 449af130..4ae0d20b 100644 --- a/helper/string_test.go +++ b/helper/string_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestProcessStrConcat(t *testing.T) { - res := gou.NewProcess("xiang.helper.StrConcat", "FOO", 20, "BAR").Run().(string) + res := process.New("xiang.helper.StrConcat", "FOO", 20, "BAR").Run().(string) assert.Equal(t, "FOO20BAR", res) } diff --git a/i18n/i18n.go b/i18n/i18n.go index c38ed4b2..b4e437e5 100644 --- a/i18n/i18n.go +++ b/i18n/i18n.go @@ -3,7 +3,6 @@ package i18n import ( "crypto/sha256" "fmt" - "path/filepath" "strings" "sync" "time" @@ -45,8 +44,7 @@ func Load(cfg config.Config) error { } // Load langs - root := filepath.Join(cfg.Root, "langs") - err = lang.Load(root) + err = lang.Load("langs") if err != nil { return err } diff --git a/i18n/i18n_test.go b/i18n/i18n_test.go index 1bd16398..0cfb30b1 100644 --- a/i18n/i18n_test.go +++ b/i18n/i18n_test.go @@ -6,11 +6,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/yaoapp/gou/lang" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/share" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - share.DBConnect(config.Conf.DB) + test.Prepare(t, config.Conf) + defer test.Clean() + err := Load(config.Conf) if err != nil { t.Fatal(err) diff --git a/importer/importer.go b/importer/importer.go index bd77157b..96832950 100644 --- a/importer/importer.go +++ b/importer/importer.go @@ -8,8 +8,9 @@ import ( "strings" "github.com/google/uuid" - jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/fs" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" @@ -17,32 +18,45 @@ import ( "github.com/yaoapp/yao/importer/from" "github.com/yaoapp/yao/importer/xlsx" "github.com/yaoapp/yao/share" - "github.com/yaoapp/yao/xfs" ) // Importers 导入器 var Importers = map[string]*Importer{} -// Load 加载导入器 -func Load(cfg config.Config) { - LoadFrom(filepath.Join(cfg.Root, "imports"), "") -} +// DataRoot data file root +var DataRoot string = "" -// LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) { - if share.DirNotExists(dir) { - return +// Load 加载导入器 +func Load(cfg config.Config) error { + + fs, err := fs.Get("system") + if err != nil { + return err } - share.Walk(dir, ".json", func(root, filename string) { - var importer Importer - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - err := jsoniter.Unmarshal(content, &importer) - if err != nil { - exception.New("%s 导入配置错误. %s", 400, name, err.Error()).Ctx(filename).Throw() + + DataRoot = fs.Root() + + exts := []string{"*.imp.yao", "*.imp.json", "*.imp.jsonc"} + return application.App.Walk("imports", func(root, file string, isdir bool) error { + if isdir { + return nil } - Importers[name] = &importer - }) + + id := share.ID(root, file) + data, err := application.App.Read(file) + if err != nil { + return err + } + + var importer Importer + err = application.Parse(file, data, &importer) + if err != nil { + return fmt.Errorf("%s 导入配置错误. %s", id, err.Error()) + } + + Importers[id] = &importer + return nil + }, exts...) } // Select 选择已加载导入器 @@ -59,12 +73,8 @@ func Open(name string) from.Source { ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(name), ".")) switch ext { case "xlsx": - // fullpath := name - fullpath := filepath.Join(xfs.Stor.Root, name) - // if !strings.HasPrefix(fullpath, "/") { - // fullpath = filepath.Join(xfs.Stor.Root, name) - // } - return xlsx.Open(fullpath) + file := filepath.Join(DataRoot, name) + return xlsx.Open(file) } exception.New("暂不支持: %s 文件导入", 400, ext).Throw() return nil @@ -169,7 +179,7 @@ func (imp *Importer) DataClean(data [][]interface{}, bindings []*Binding) ([]str // DataValidate 数值校验 func DataValidate(row []interface{}, value interface{}, rule string) ([]interface{}, bool) { - process, err := gou.ProcessOf(rule, value, row) + process, err := process.Of(rule, value, row) if err != nil { log.With(log.F{"rule": rule, "row": row}).Error("DataValidate: %s", err.Error()) return row, true @@ -405,7 +415,7 @@ func (imp *Importer) Run(src from.Source, mapping *Mapping) interface{} { length := len(data) total = total + length columns, data := imp.DataClean(data, mapping.Columns) - process, err := gou.ProcessOf(imp.Process, columns, data, id, page) + process, err := process.Of(imp.Process, columns, data, id, page) if err != nil { failed = failed + length log.With(log.F{"line": line}).Error("导入失败: %s", err.Error()) @@ -444,7 +454,7 @@ func (imp *Importer) Run(src from.Source, mapping *Mapping) interface{} { } if imp.Output != "" { - res, err := gou.NewProcess(imp.Output, output).WithSID(imp.Sid).Exec() + res, err := process.New(imp.Output, output).WithSID(imp.Sid).Exec() if err != nil { log.With(log.F{"output": imp.Output}).Error(err.Error()) return output diff --git a/importer/importer_test.go b/importer/importer_test.go index 8452e7bd..911121c3 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -1,30 +1,46 @@ package importer import ( + "fmt" "path/filepath" "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/kun/utils" + "github.com/yaoapp/gou/fs" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/importer/xlsx" + "github.com/yaoapp/yao/script" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - LoadFrom("not a path", "404.") + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t, config.Conf) assert.IsType(t, &Importer{}, Select("order")) } func TestFingerprintSimple(t *testing.T) { - simple := filepath.Join(config.Conf.Root, "data", "assets", "simple.xlsx") + test.Prepare(t, config.Conf) + defer test.Clean() + + root := prepare(t, config.Conf) + simple := filepath.Join(root, "assets", "simple.xlsx") file := xlsx.Open(simple) defer file.Close() + imp := Select("order") fingerprint := imp.Fingerprint(file) assert.Equal(t, "3451ca87d71801687abba8993e5a69af79482914435d7cc064236fd93160f999", fingerprint) } func TestAutoMappingSimple(t *testing.T) { - simple := filepath.Join(config.Conf.Root, "data", "assets", "simple.xlsx") + + test.Prepare(t, config.Conf) + defer test.Clean() + + root := prepare(t, config.Conf) + simple := filepath.Join(root, "assets", "simple.xlsx") file := xlsx.Open(simple) defer file.Close() @@ -48,7 +64,12 @@ func TestAutoMappingSimple(t *testing.T) { } func TestDataGetSimple(t *testing.T) { - simple := filepath.Join(config.Conf.Root, "data", "assets", "simple.xlsx") + + test.Prepare(t, config.Conf) + defer test.Clean() + + root := prepare(t, config.Conf) + simple := filepath.Join(root, "assets", "simple.xlsx") file := xlsx.Open(simple) defer file.Close() @@ -69,7 +90,11 @@ func TestDataGetSimple(t *testing.T) { } func TestDataChunkSimple(t *testing.T) { - simple := filepath.Join(config.Conf.Root, "data", "assets", "simple.xlsx") + test.Prepare(t, config.Conf) + defer test.Clean() + + root := prepare(t, config.Conf) + simple := filepath.Join(root, "assets", "simple.xlsx") file := xlsx.Open(simple) defer file.Close() @@ -83,7 +108,11 @@ func TestDataChunkSimple(t *testing.T) { } func TestRunSimple(t *testing.T) { - simple := filepath.Join(config.Conf.Root, "data", "assets", "simple.xlsx") + test.Prepare(t, config.Conf) + defer test.Clean() + + root := prepare(t, config.Conf) + simple := filepath.Join(root, "assets", "simple.xlsx") file := xlsx.Open(simple) defer file.Close() @@ -97,7 +126,11 @@ func TestRunSimple(t *testing.T) { } func TestDataPreviewSimple(t *testing.T) { - simple := filepath.Join(config.Conf.Root, "data", "assets", "simple.xlsx") + test.Prepare(t, config.Conf) + defer test.Clean() + + root := prepare(t, config.Conf) + simple := filepath.Join(root, "assets", "simple.xlsx") file := xlsx.Open(simple) defer file.Close() imp := Select("order") @@ -116,7 +149,11 @@ func TestDataPreviewSimple(t *testing.T) { } func TestMappingPreviewSimple(t *testing.T) { - simple := filepath.Join(config.Conf.Root, "data", "assets", "simple.xlsx") + test.Prepare(t, config.Conf) + defer test.Clean() + + root := prepare(t, config.Conf) + simple := filepath.Join(root, "assets", "simple.xlsx") file := xlsx.Open(simple) defer file.Close() @@ -140,19 +177,50 @@ func TestMappingPreviewSimple(t *testing.T) { } func TestMappingSetting(t *testing.T) { - simple := filepath.Join(config.Conf.Root, "data", "assets", "simple.xlsx") + test.Prepare(t, config.Conf) + defer test.Clean() + + root := prepare(t, config.Conf) + simple := filepath.Join(root, "assets", "simple.xlsx") file := xlsx.Open(simple) defer file.Close() imp := Select("order") setting := imp.MappingSetting(file) - utils.Dump(setting) + // utils.Dump(setting) assert.NotNil(t, setting) } func TestDataSetting(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t, config.Conf) + imp := Select("order") setting := imp.DataSetting() - utils.Dump(setting) + // utils.Dump(setting) assert.NotNil(t, setting) } + +func prepare(t *testing.T, cfg config.Config) string { + err := Load(cfg) + if err != nil { + t.Fatal(err) + } + + fs := fs.MustGet("system") + dataRoot := fs.Root() + fmt.Println("prepare", dataRoot) + + if err != nil { + t.Fatal(err) + } + + err = script.Load(cfg) + if err != nil { + t.Fatal(err) + } + + return dataRoot +} diff --git a/importer/init_test.go b/importer/init_test.go deleted file mode 100644 index 89b29419..00000000 --- a/importer/init_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package importer - -import ( - "os" - "testing" - - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/query" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/script" - "github.com/yaoapp/yao/share" -) - -func TestMain(m *testing.M) { - runtime.Load(config.Conf) - share.DBConnect(config.Conf.DB) - model.Load(config.Conf) - query.Load(config.Conf) - share.Load(config.Conf) - script.Load(config.Conf) - Load(config.Conf) - code := m.Run() - os.Exit(code) -} diff --git a/importer/process.go b/importer/process.go index 33dfed0b..86c24f40 100644 --- a/importer/process.go +++ b/importer/process.go @@ -2,31 +2,31 @@ package importer import ( jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" ) func init() { // 注册处理器 - gou.RegisterProcessHandler("xiang.import.Run", ProcessRun) // deprecated → yao.import.Run - gou.RegisterProcessHandler("xiang.import.Data", ProcessData) // deprecated → yao.import.Data - gou.RegisterProcessHandler("xiang.import.Setting", ProcessSetting) // deprecated → yao.import.Setting - gou.RegisterProcessHandler("xiang.import.DataSetting", ProcessDataSetting) // deprecated → yao.import.DataSetting - gou.RegisterProcessHandler("xiang.import.Mapping", ProcessMapping) // deprecated → yao.import.Mapping - gou.RegisterProcessHandler("xiang.import.MappingSetting", ProcessMappingSetting) // deprecated → yao.import.MappingSetting + process.Register("xiang.import.Run", ProcessRun) // deprecated → yao.import.Run + process.Register("xiang.import.Data", ProcessData) // deprecated → yao.import.Data + process.Register("xiang.import.Setting", ProcessSetting) // deprecated → yao.import.Setting + process.Register("xiang.import.DataSetting", ProcessDataSetting) // deprecated → yao.import.DataSetting + process.Register("xiang.import.Mapping", ProcessMapping) // deprecated → yao.import.Mapping + process.Register("xiang.import.MappingSetting", ProcessMappingSetting) // deprecated → yao.import.MappingSetting - gou.AliasProcess("xiang.import.Run", "yao.import.Run") - gou.AliasProcess("xiang.import.Data", "yao.import.Data") - gou.AliasProcess("xiang.import.Setting", "yao.import.Setting") - gou.AliasProcess("xiang.import.DataSetting", "yao.import.DataSetting") - gou.AliasProcess("xiang.import.Mapping", "yao.import.Mapping") - gou.AliasProcess("xiang.import.MappingSetting", "yao.import.MappingSetting") + process.Alias("xiang.import.Run", "yao.import.Run") + process.Alias("xiang.import.Data", "yao.import.Data") + process.Alias("xiang.import.Setting", "yao.import.Setting") + process.Alias("xiang.import.DataSetting", "yao.import.DataSetting") + process.Alias("xiang.import.Mapping", "yao.import.Mapping") + process.Alias("xiang.import.MappingSetting", "yao.import.MappingSetting") } // ProcessRun xiang.import.Run // 导入数据 -func ProcessRun(process *gou.Process) interface{} { +func ProcessRun(process *process.Process) interface{} { process.ValidateArgNums(3) name := process.ArgsString(0) imp := Select(name).WithSid(process.Sid) @@ -39,7 +39,7 @@ func ProcessRun(process *gou.Process) interface{} { // ProcessSetting xiang.import.Setting // 导入配置选项 -func ProcessSetting(process *gou.Process) interface{} { +func ProcessSetting(process *process.Process) interface{} { process.ValidateArgNums(1) name := process.ArgsString(0) imp := Select(name).WithSid(process.Sid) @@ -53,7 +53,7 @@ func ProcessSetting(process *gou.Process) interface{} { // ProcessData xiang.import.Data // 数据预览 -func ProcessData(process *gou.Process) interface{} { +func ProcessData(process *process.Process) interface{} { process.ValidateArgNums(5) name := process.ArgsString(0) imp := Select(name).WithSid(process.Sid) @@ -71,7 +71,7 @@ func ProcessData(process *gou.Process) interface{} { // ProcessDataSetting xiang.import.DataSetting // 数据预览表格配置 -func ProcessDataSetting(process *gou.Process) interface{} { +func ProcessDataSetting(process *process.Process) interface{} { process.ValidateArgNums(1) name := process.ArgsString(0) imp := Select(name).WithSid(process.Sid) @@ -80,7 +80,7 @@ func ProcessDataSetting(process *gou.Process) interface{} { // ProcessMapping xiang.import.Mapping // 字段映射预览 -func ProcessMapping(process *gou.Process) interface{} { +func ProcessMapping(process *process.Process) interface{} { process.ValidateArgNums(2) name := process.ArgsString(0) imp := Select(name).WithSid(process.Sid) @@ -93,7 +93,7 @@ func ProcessMapping(process *gou.Process) interface{} { // ProcessMappingSetting xiang.import.MappingSetting // 字段映射表格配置 -func ProcessMappingSetting(process *gou.Process) interface{} { +func ProcessMappingSetting(process *process.Process) interface{} { process.ValidateArgNums(2) name := process.ArgsString(0) imp := Select(name).WithSid(process.Sid) diff --git a/importer/process_test.go b/importer/process_test.go index d385e034..37f5e793 100644 --- a/importer/process_test.go +++ b/importer/process_test.go @@ -5,53 +5,79 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) func TestProcessMapping(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t, config.Conf) + simple := filepath.Join("assets", "simple.xlsx") args := []interface{}{"order", simple} - response := gou.NewProcess("xiang.import.Mapping", args...).Run() + response := process.New("yao.import.Mapping", args...).Run() _, ok := response.(*Mapping) assert.True(t, ok) } func TestProcessMappingSetting(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t, config.Conf) + simple := filepath.Join("assets", "simple.xlsx") args := []interface{}{"order", simple} - response := gou.NewProcess("xiang.import.MappingSetting", args...).Run() + response := process.New("yao.import.MappingSetting", args...).Run() _, ok := response.(map[string]interface{}) assert.True(t, ok) } func TestProcessData(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t, config.Conf) + simple := filepath.Join("assets", "simple.xlsx") - mapping := gou.NewProcess("xiang.import.Mapping", "order", simple).Run() + mapping := process.New("yao.import.Mapping", "order", simple).Run() args := []interface{}{"order", simple, 1, 2, mapping} - response := gou.NewProcess("xiang.import.Data", args...).Run() + response := process.New("yao.import.Data", args...).Run() _, ok := response.(map[string]interface{}) assert.True(t, ok) } func TestProcessDataSetting(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t, config.Conf) + args := []interface{}{"order"} - response := gou.NewProcess("xiang.import.DataSetting", args...).Run() + response := process.New("yao.import.DataSetting", args...).Run() _, ok := response.(map[string]interface{}) assert.True(t, ok) } func TestProcessSetting(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t, config.Conf) + args := []interface{}{"order"} - response := gou.NewProcess("xiang.import.Setting", args...).Run() + response := process.New("yao.import.Setting", args...).Run() _, ok := response.(map[string]interface{}) assert.True(t, ok) } func TestProcessRun(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t, config.Conf) + simple := filepath.Join("assets", "simple.xlsx") - mapping := gou.NewProcess("xiang.import.Mapping", "order", simple).Run() + mapping := process.New("yao.import.Mapping", "order", simple).Run() args := []interface{}{"order", simple, mapping} - response := gou.NewProcess("xiang.import.Run", args...).Run() + response := process.New("yao.import.Run", args...).Run() _, ok := response.(map[string]int) assert.True(t, ok) } diff --git a/init_test.go b/init_test.go deleted file mode 100644 index 9a59584f..00000000 --- a/init_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "os" - "testing" - - "github.com/yaoapp/gou" - "github.com/yaoapp/yao/config" -) - -var cfg config.Config - -func TestMain(m *testing.M) { - - // Run test suites - exitVal := m.Run() - - // we can do clean up code here - gou.KillPlugins() - - os.Exit(exitVal) -} diff --git a/main.go b/main.go index b2421961..7ded00d2 100644 --- a/main.go +++ b/main.go @@ -6,11 +6,7 @@ import ( _ "github.com/yaoapp/gou/encoding" _ "github.com/yaoapp/yao/crypto" _ "github.com/yaoapp/yao/helper" - _ "github.com/yaoapp/yao/network" - _ "github.com/yaoapp/yao/system" - _ "github.com/yaoapp/yao/user" _ "github.com/yaoapp/yao/utils" - _ "github.com/yaoapp/yao/xfs" ) // 主程序 diff --git a/main_test.go b/main_test.go deleted file mode 100644 index f54d9659..00000000 --- a/main_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "log" - "net/http" - "os" - "testing" - "time" - - jsoniter "github.com/json-iterator/go" - "github.com/stretchr/testify/assert" - "github.com/yaoapp/kun/any" - "github.com/yaoapp/kun/maps" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/service" -) - -func TestCommandVersion(t *testing.T) { - oldArgs := os.Args - defer func() { os.Args = oldArgs }() - - os.Args = append(os.Args, "version") - assert.NotPanics(t, func() { - main() - }) -} - -func TestCommandMigrate(t *testing.T) { - oldArgs := os.Args - defer func() { os.Args = oldArgs }() - - os.Args = append(os.Args, "migrate", "--reset", "--force") - assert.NotPanics(t, func() { - main() - }) -} - -func TestCommandStart(t *testing.T) { - oldArgs := os.Args - defer func() { - os.Args = oldArgs - service.Stop(func() {}) - log.Println("服务已关闭") - }() - - go func() { - os.Args = append(os.Args, "start") - main() - }() - - // 发送请求 - request := func() (maps.MapStr, error) { - time.Sleep(time.Microsecond * 2000) - url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "127.0.0.1", config.Conf.Port) - resp, err := http.Get(url) - if err != nil { - return nil, err - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - res := maps.MakeMapStr() - err = jsoniter.Unmarshal(body, &res) - if err != nil { - return nil, err - } - return res, nil - } - - // 等待服务启动 - times := 0 - for times < 30 { // 2秒超时 - time.Sleep(time.Second * 2) - times++ - fmt.Printf("Trying(%d)...", times) - res, err := request() - if err != nil { - fmt.Printf(" %s\n", err.Error()) - continue - } - assert.Equal(t, 1, any.Of(res.Get("id")).CInt()) - assert.Equal(t, "管理员", res.Get("name")) - return - } - - assert.True(t, false) -} - -func TestCommandStop(t *testing.T) { - oldArgs := os.Args - defer func() { - os.Args = oldArgs - }() - go func() { - os.Args = append(os.Args, "start") - main() - }() - - // 发送请求 - request := func() (maps.MapStr, error) { - time.Sleep(time.Microsecond * 2000) - url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "127.0.0.1", config.Conf.Port) - resp, err := http.Get(url) - if err != nil { - return nil, err - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - res := maps.MakeMapStr() - err = jsoniter.Unmarshal(body, &res) - if err != nil { - return nil, err - } - return res, nil - } - - // 等待服务启动 - times := 0 - for times < 30 { // 2秒超时 - time.Sleep(time.Second * 2) - times++ - res, err := request() - if err != nil { - fmt.Println("REQUEST ERROR:", err) - continue - } - assert.Equal(t, 1, any.Of(res.Get("id")).CInt()) - assert.Equal(t, "管理员", res.Get("name")) - - // 测试关闭 - service.Stop(func() { log.Println("服务已关闭") }) - time.Sleep(time.Second * 5) - _, err = request() - assert.NotNil(t, err) - return - } - - assert.True(t, false) -} diff --git a/model/README.md b/model/README.md deleted file mode 100644 index 50d91a62..00000000 --- a/model/README.md +++ /dev/null @@ -1 +0,0 @@ -# Model diff --git a/model/model.go b/model/model.go index 70881680..4a8d2569 100644 --- a/model/model.go +++ b/model/model.go @@ -2,53 +2,25 @@ package model import ( "fmt" - "path/filepath" - "strings" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) // Load 加载数据模型 func Load(cfg config.Config) error { - if share.BUILDIN { - return LoadBuildIn("models", "") - } - return LoadFrom(filepath.Join(cfg.Root, "models"), "") -} -// LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) error { + model.WithCrypt([]byte(fmt.Sprintf(`{"key":"%s"}`, cfg.DB.AESKey)), "AES") + model.WithCrypt([]byte(`{}`), "PASSWORD") - messages := []string{} - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadModelReturn(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) - messages = append(messages, err.Error()) + exts := []string{"*.mod.yao", "*.mod.json", "*.mod.jsonc"} + return application.App.Walk("models", func(root, file string, isdir bool) error { + if isdir { + return nil } - }) - - if err != nil { - messages = append(messages, err.Error()) - } - - if len(messages) > 0 { - return fmt.Errorf("[Model] %s", strings.Join(messages, ";")) - } - - return nil -} - -// LoadBuildIn 从制品中读取 -func LoadBuildIn(dir string, prefix string) error { - return nil + _, err := model.Load(file, share.ID(root, file)) + return err + }, exts...) } diff --git a/model/model_test.go b/model/model_test.go index be1a6907..8fa84445 100644 --- a/model/model_test.go +++ b/model/model_test.go @@ -5,27 +5,30 @@ import ( "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/share" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - share.DBConnect(config.Conf.DB) - gou.Models = make(map[string]*gou.Model) + test.Prepare(t, config.Conf) + defer test.Clean() + Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } func check(t *testing.T) { - keys := []string{} - for key := range gou.Models { - keys = append(keys, key) + ids := map[string]bool{} + for id := range model.Models { + ids[id] = true } - assert.Equal(t, 11, len(keys)) - demo := gou.Select("demo") - assert.Equal(t, demo.MetaData.Name, "::Demo") - assert.Equal(t, demo.Columns["action"].Label, "::Action") + assert.True(t, ids["user"]) + assert.True(t, ids["category"]) + assert.True(t, ids["tag"]) + assert.True(t, ids["pet"]) + assert.True(t, ids["pet.tag"]) + assert.True(t, ids["user.pet"]) + assert.True(t, ids["tests.user"]) } diff --git a/network/ip.go b/network/ip.go deleted file mode 100644 index 80f1fc55..00000000 --- a/network/ip.go +++ /dev/null @@ -1,63 +0,0 @@ -package network - -import ( - "net" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/exception" -) - -// IP 读取IP地址 -func IP() map[string]string { - res := map[string]string{} - ifaces, err := net.Interfaces() - if err != nil { - exception.New("读取网卡失败 %s", 500, err.Error()).Throw() - } - for _, i := range ifaces { - addrs, err := i.Addrs() - if err != nil { - exception.New("读取IP地址失败 %s", 500, err.Error()).Throw() - } - // handle err - for _, addr := range addrs { - var ip net.IP - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - } - res[i.Name] = ip.String() - } - } - return res -} - -// ProcessIP xiang.network.IP IP地址 -func ProcessIP(process *gou.Process) interface{} { - return IP() -} - -// FreePort xiang.network.FreePort 获取可用端口 -func FreePort() int { - addr, err := net.ResolveTCPAddr("tcp", ":0") - if err != nil { - exception.New("获取可用端口失败 %s", 500, err.Error()).Throw() - return 0 - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - exception.New("获取可用端口失败 %s", 500, err.Error()).Throw() - return 0 - } - - defer l.Close() - return l.Addr().(*net.TCPAddr).Port -} - -// ProcessFreePort xiang.network.FreePort 获取可用端口 -func ProcessFreePort(process *gou.Process) interface{} { - return FreePort() -} diff --git a/network/ip_test.go b/network/ip_test.go deleted file mode 100644 index 67c9e509..00000000 --- a/network/ip_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" -) - -func TestIP(t *testing.T) { - resp := IP() - assert.True(t, len(resp) > 0) -} - -func TestProcessIP(t *testing.T) { - res := gou.NewProcess("xiang.network.ip").Run() - resp, ok := res.(map[string]string) - assert.True(t, ok) - assert.True(t, len(resp) > 0) -} - -func TestFreePort(t *testing.T) { - port := FreePort() - assert.True(t, port > 0) -} - -func TestProcessFreePort(t *testing.T) { - res := gou.NewProcess("xiang.network.FreePort").Run() - port, ok := res.(int) - assert.True(t, ok) - assert.True(t, port > 0) -} diff --git a/network/process.go b/network/process.go deleted file mode 100644 index 376a5b8e..00000000 --- a/network/process.go +++ /dev/null @@ -1,19 +0,0 @@ -package network - -// ******************************************************* -// * DEPRECATED → http * -// ******************************************************* - -import "github.com/yaoapp/gou" - -func init() { - // 注册处理器 - gou.RegisterProcessHandler("xiang.network.ip", ProcessIP) - gou.RegisterProcessHandler("xiang.network.FreePort", ProcessFreePort) - gou.RegisterProcessHandler("xiang.network.Get", ProcessGet) - gou.RegisterProcessHandler("xiang.network.Post", ProcessPost) - gou.RegisterProcessHandler("xiang.network.PostJSON", ProcessPostJSON) - gou.RegisterProcessHandler("xiang.network.Put", ProcessPut) - gou.RegisterProcessHandler("xiang.network.PutJSON", ProcessPutJSON) - gou.RegisterProcessHandler("xiang.network.Send", ProcessSend) -} diff --git a/network/request.go b/network/request.go deleted file mode 100644 index de0f1c80..00000000 --- a/network/request.go +++ /dev/null @@ -1,168 +0,0 @@ -package network - -// ******************************************************* -// * DEPRECATED → http * -// ******************************************************* - -import ( - "bytes" - "crypto/tls" - "fmt" - "io/ioutil" - "net/http" - "strings" - - jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou/dns" -) - -// Response 请求响应结果 -type Response struct { - Status int `json:"status"` - Body string `json:"body"` - Data interface{} `json:"data"` - Headers map[string]interface{} `json:"headers"` -} - -// RequestGet 发送GET请求 -func RequestGet(url string, params map[string]interface{}, headers map[string]string) Response { - return RequestSend("GET", url, params, nil, headers) -} - -// RequestPost 发送POST请求 -func RequestPost(url string, data interface{}, headers map[string]string) Response { - return RequestSend("POST", url, map[string]interface{}{}, data, headers) -} - -// RequestPostJSON 发送POST请求 -func RequestPostJSON(url string, data interface{}, headers map[string]string) Response { - if headers == nil { - headers = map[string]string{} - } - headers["content-type"] = "application/json;charset=utf8" - return RequestSend("POST", url, map[string]interface{}{}, data, headers) -} - -// RequestPut 发送PUT请求 -func RequestPut(url string, data interface{}, headers map[string]string) Response { - return RequestSend("PUT", url, map[string]interface{}{}, data, headers) -} - -// RequestPutJSON 发送PUT请求 -func RequestPutJSON(url string, data interface{}, headers map[string]string) Response { - if headers == nil { - headers = map[string]string{} - } - headers["content-type"] = "application/json;charset=utf8" - return RequestSend("PUT", url, map[string]interface{}{}, data, headers) -} - -// RequestSend 发送Request请求 -func RequestSend(method string, url string, params map[string]interface{}, data interface{}, headers map[string]string) Response { - - var body []byte - var err error - if data != nil { - if strings.HasPrefix(strings.ToLower(headers["content-type"]), "application/json") { - body, err = jsoniter.Marshal(data) - if err != nil { - return Response{ - Status: 500, - Body: err.Error(), - Data: map[string]interface{}{"code": 500, "message": err.Error()}, - Headers: map[string]interface{}{ - "Content-Type": "application/json;charset=utf8", - }, - } - } - } else { - body = []byte(fmt.Sprintf("%v", data)) - } - } - - req, err := http.NewRequest(method, url, bytes.NewBuffer(body)) - if err != nil { - return Response{ - Status: 500, - Body: err.Error(), - Data: map[string]interface{}{"code": 500, "message": err.Error()}, - Headers: map[string]interface{}{ - "Content-Type": "application/json;charset=utf8", - }, - } - } - - // Request Header - if headers != nil { - for name, header := range headers { - req.Header.Set(name, header) - } - } - - // Force using system DSN resolver - // var dialer = &net.Dialer{Resolver: &net.Resolver{PreferGo: false}} - var dialContext = dns.DialContext() - var tr = &http.Transport{DialContext: dialContext} - var client *http.Client = &http.Client{Transport: tr} - - // Https SkipVerify false - if strings.HasPrefix(url, "https://") { - tr = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DialContext: dialContext, - } - client = &http.Client{Transport: tr} - } - defer tr.CloseIdleConnections() - - resp, err := client.Do(req) - if err != nil { - return Response{ - Status: 0, - Body: err.Error(), - Data: map[string]interface{}{"code": 500, "message": err.Error()}, - Headers: map[string]interface{}{ - "Content-Type": "application/json;charset=utf8", - }, - } - } - defer resp.Body.Close() - - body, err = ioutil.ReadAll(resp.Body) // response body is []byte - if err != nil { - return Response{ - Status: 500, - Body: err.Error(), - Data: map[string]interface{}{"code": resp.StatusCode, "message": err.Error()}, - Headers: map[string]interface{}{ - "Content-Type": "application/json;charset=utf8", - }, - } - } - - // JSON 解析 - var res interface{} - if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") { - err = jsoniter.Unmarshal(body, &res) - if err != nil { - return Response{ - Status: 500, - Body: err.Error(), - Data: map[string]interface{}{"code": resp.StatusCode, "message": err.Error()}, - Headers: map[string]interface{}{ - "Content-Type": "application/json;charset=utf8", - }, - } - } - } - respHeaders := map[string]interface{}{} - for name := range resp.Header { - respHeaders[name] = resp.Header.Get(name) - } - return Response{ - Status: resp.StatusCode, - Body: string(body), - Data: res, - Headers: respHeaders, - } -} diff --git a/network/request.process.go b/network/request.process.go deleted file mode 100644 index 0b28591f..00000000 --- a/network/request.process.go +++ /dev/null @@ -1,127 +0,0 @@ -package network - -import ( - "fmt" - - "github.com/yaoapp/gou" -) - -// ******************************************************* -// * DEPRECATED → http * -// ******************************************************* - -// ProcessPost xiang.helper.Post HTTP Post -func ProcessPost(process *gou.Process) interface{} { - process.ValidateArgNums(1) - var data interface{} - var headers = map[string]string{} - url := process.ArgsString(0) - if process.NumOfArgs() > 1 { - data = process.Args[1] - } - if process.NumOfArgs() > 2 { - inputHeaders := process.ArgsMap(2) - for name, value := range inputHeaders { - headers[name] = fmt.Sprintf("%v", value) - } - } - return RequestPost(url, data, headers) -} - -// ProcessPostJSON xiang.helper.PostJSON HTTP Post -func ProcessPostJSON(process *gou.Process) interface{} { - process.ValidateArgNums(1) - var data interface{} - var headers = map[string]string{} - url := process.ArgsString(0) - if process.NumOfArgs() > 1 { - data = process.Args[1] - } - if process.NumOfArgs() > 2 { - inputHeaders := process.ArgsMap(2) - for name, value := range inputHeaders { - headers[name] = fmt.Sprintf("%v", value) - } - } - return RequestPostJSON(url, data, headers) -} - -// ProcessPut xiang.helper.Put HTTP PUT -func ProcessPut(process *gou.Process) interface{} { - process.ValidateArgNums(1) - var data interface{} - var headers = map[string]string{} - url := process.ArgsString(0) - if process.NumOfArgs() > 1 { - data = process.Args[1] - } - if process.NumOfArgs() > 2 { - inputHeaders := process.ArgsMap(2) - for name, value := range inputHeaders { - headers[name] = fmt.Sprintf("%v", value) - } - } - return RequestPut(url, data, headers) -} - -// ProcessPutJSON xiang.helper.PutJSON HTTP PUT -func ProcessPutJSON(process *gou.Process) interface{} { - process.ValidateArgNums(1) - var data interface{} - var headers = map[string]string{} - url := process.ArgsString(0) - if process.NumOfArgs() > 1 { - data = process.Args[1] - } - if process.NumOfArgs() > 2 { - inputHeaders := process.ArgsMap(2) - for name, value := range inputHeaders { - headers[name] = fmt.Sprintf("%v", value) - } - } - return RequestPutJSON(url, data, headers) -} - -// ProcessSend xiang.helper.Send HTTP Send -func ProcessSend(process *gou.Process) interface{} { - process.ValidateArgNums(2) - var data interface{} - var params = map[string]interface{}{} - var headers = map[string]string{} - method := process.ArgsString(0) - url := process.ArgsString(1) - if process.NumOfArgs() > 2 { - params = process.ArgsMap(2) - } - - if process.NumOfArgs() > 3 { - data = process.Args[3] - } - - if process.NumOfArgs() > 4 { - inputHeaders := process.ArgsMap(4) - for name, value := range inputHeaders { - headers[name] = fmt.Sprintf("%v", value) - } - } - return RequestSend(method, url, params, data, headers) -} - -// ProcessGet xiang.helper.Get HTTP Get -func ProcessGet(process *gou.Process) interface{} { - process.ValidateArgNums(1) - var params = map[string]interface{}{} - var headers = map[string]string{} - url := process.ArgsString(0) - if process.NumOfArgs() > 1 { - params = process.ArgsMap(1) - } - if process.NumOfArgs() > 2 { - inputHeaders := process.ArgsMap(2) - for name, value := range inputHeaders { - headers[name] = fmt.Sprintf("%v", value) - } - } - - return RequestGet(url, params, headers) -} diff --git a/network/request_test.go b/network/request_test.go deleted file mode 100644 index 9e0d3ab6..00000000 --- a/network/request_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/any" -) - -func TestRequestGetHtml(t *testing.T) { - resp := RequestGet("https://api.github.com/zen", nil, nil) - assert.Equal(t, 200, resp.Status) - assert.Equal(t, nil, resp.Data) -} - -func TestRequestGetJSON(t *testing.T) { - resp := RequestGet("https://api.github.com/users/yaoapp", nil, nil) - assert.Equal(t, 200, resp.Status) - data := any.Of(resp.Data).MapStr().Dot() - assert.Equal(t, "https://github.com/YaoApp", data.Get("html_url")) -} - -func TestRequestPost(t *testing.T) { - resp := RequestPost("https://api.github.com/zen", map[string]interface{}{"foo": "bar"}, nil) - assert.Equal(t, 404, resp.Status) -} - -func TestRequestPostJSON(t *testing.T) { - resp := RequestPostJSON("https://api.github.com/zen", map[string]interface{}{"foo": "bar"}, nil) - assert.Equal(t, 404, resp.Status) -} - -func TestRequestProcessGet(t *testing.T) { - args := []interface{}{"https://api.github.com/zen"} - res := gou.NewProcess("xiang.network.Get", args...).Run() - resp, ok := res.(Response) - assert.True(t, ok) - assert.Equal(t, 200, resp.Status) - assert.Equal(t, nil, resp.Data) -} - -func TestRequestProcessPost(t *testing.T) { - args := []interface{}{"https://api.github.com/zen", map[string]interface{}{"foo": "bar"}, nil} - res := gou.NewProcess("xiang.network.Post", args...).Run() - resp, ok := res.(Response) - assert.True(t, ok) - assert.Equal(t, 404, resp.Status) -} - -func TestRequestProcessPostJSON(t *testing.T) { - args := []interface{}{"https://api.github.com/zen", map[string]interface{}{"foo": "bar"}, nil} - res := gou.NewProcess("xiang.network.PostJSON", args...).Run() - resp, ok := res.(Response) - assert.True(t, ok) - assert.Equal(t, 404, resp.Status) -} - -func TestRequestProcessSend(t *testing.T) { - args := []interface{}{"GET", "https://api.github.com/zen"} - res := gou.NewProcess("xiang.network.Send", args...).Run() - resp, ok := res.(Response) - assert.True(t, ok) - assert.Equal(t, 200, resp.Status) - assert.Equal(t, nil, resp.Data) -} diff --git a/page/REAME.md b/page/REAME.md deleted file mode 100644 index 82e08d54..00000000 --- a/page/REAME.md +++ /dev/null @@ -1 +0,0 @@ -# 数据页面 diff --git a/page/api.go b/page/api.go deleted file mode 100644 index 3f9c07dc..00000000 --- a/page/api.go +++ /dev/null @@ -1,60 +0,0 @@ -package page - -import ( - "github.com/yaoapp/yao/share" -) - -// SetupAPIs 设定API数据 -func (page *Page) SetupAPIs() { - - defaults := map[string]share.API{ - "data": apiDataDefault(), - "setting": apiSettingDefault(), - } - - // 开发者填写的规则 - for name := range page.APIs { - if _, has := defaults[name]; !has { - delete(page.APIs, name) - continue - } - - api := defaults[name] - api.Name = name - if page.APIs[name].Process != "" { - api.Process = page.APIs[name].Process - } - - if page.APIs[name].Guard != "" { - api.Guard = page.APIs[name].Guard - } - - if page.APIs[name].Default != nil { - api.Default = page.APIs[name].Default - } - - defaults[name] = api - } - - page.APIs = defaults -} - -// apiSearchDefault data 接口默认值 -func apiDataDefault() share.API { - param := map[string]interface{}{} - return share.API{ - Name: "data", - Guard: "bearer-jwt", - Process: "xiang.page.data", - Default: []interface{}{param}, - } -} - -// apiSettingDefault setting 接口默认值 -func apiSettingDefault() share.API { - return share.API{ - Name: "setting", - Guard: "bearer-jwt", - Process: "xiang.page.setting", - } -} diff --git a/page/lang.go b/page/lang.go deleted file mode 100644 index 9342eabe..00000000 --- a/page/lang.go +++ /dev/null @@ -1,125 +0,0 @@ -package page - -// Lang for applying a language pack -func (page *Page) Lang(trans func(widget string, inst string, value *string) bool) { - inst := page.Flow.Name - widget := "page" - - trans(widget, inst, &page.Name) - trans(widget, inst, &page.Label) - trans(widget, inst, &page.Description) - trans(widget, inst, &page.Page.Primary) - transMap(widget, inst, page.Page.Layout, trans) - page.Output = transAny(widget, inst, page.Output, trans) - - // Filters - for name, filter := range page.Filters { - new := name - trans(widget, inst, &new) - trans(widget, inst, &filter.Label) - delete(page.Filters, name) - - // Props - transMap(widget, inst, filter.Input.Props, trans) - page.Filters[new] = filter - } - -} - -func transAny(widget string, inst string, input interface{}, trans func(widget string, inst string, value *string) bool) interface{} { - switch input.(type) { - case []interface{}: - values := input.([]interface{}) - transArr(widget, inst, values, trans) - input = values - break - - case map[string]interface{}: - values := input.(map[string]interface{}) - for name, value := range values { - new := name - newValue := value - - switch value.(type) { - case string: - val := value.(string) - trans(widget, inst, &val) - newValue = val - break - - case []interface{}: - vals := value.([]interface{}) - transArr(widget, inst, vals, trans) - newValue = vals - break - - case map[string]interface{}: - vals := value.(map[string]interface{}) - transMap(widget, inst, vals, trans) - newValue = vals - break - } - - trans(widget, inst, &new) - delete(values, name) - values[new] = newValue - } - input = values - break - } - - return input -} - -func transMap(widget string, inst string, values map[string]interface{}, trans func(widget string, inst string, value *string) bool) { - for key, value := range values { - - switch value.(type) { - - case string: - v := value.(string) - trans(widget, inst, &v) - values[key] = v - break - - case []interface{}: - v := value.([]interface{}) - transArr(widget, inst, v, trans) - values[key] = v - break - - case map[string]interface{}: - v := value.(map[string]interface{}) - transMap(widget, inst, v, trans) - values[key] = v - break - } - - } -} - -func transArr(widget string, inst string, values []interface{}, trans func(widget string, inst string, value *string) bool) { - for key, value := range values { - switch value.(type) { - - case string: - v := value.(string) - trans(widget, inst, &v) - values[key] = v - break - - case []interface{}: - v := value.([]interface{}) - transArr(widget, inst, v, trans) - values[key] = v - break - - case map[string]interface{}: - v := value.(map[string]interface{}) - transMap(widget, inst, v, trans) - values[key] = v - break - } - - } -} diff --git a/page/page.go b/page/page.go deleted file mode 100644 index 98bbfe15..00000000 --- a/page/page.go +++ /dev/null @@ -1,121 +0,0 @@ -package page - -import ( - "fmt" - "path" - - jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/lang" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/kun/log" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/share" -) - -// Pages 已载入页面 -var Pages = map[string]*Page{} - -// Load 加载页面 -func Load(cfg config.Config) error { - - if share.BUILDIN { - return LoadBuildIn("pages", "") - } - - var errs error = nil - if err := LoadFrom(path.Join(cfg.Root, "/kanban"), ""); err != nil { - // log.Trace("load kanban error: %s ", err.Error()) - errs = err - } - if err := LoadFrom(path.Join(cfg.Root, "/screen"), ""); err != nil { - // log.Trace("load screen error: %s", err.Error()) - errs = err - } - if err := LoadFrom(path.Join(cfg.Root, "/pages"), ""); err != nil { - // log.Trace("load screen error: %s", err.Error()) - errs = err - } - - return errs -} - -// LoadBuildIn 从制品中读取 -func LoadBuildIn(dir string, prefix string) error { - return nil -} - -// LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := LoadPage(content, name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) - } - }) - if err != nil { - return err - } - - // Load Script - err = share.Walk(dir, ".js", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - page := Select(name) - if page != nil { - script := share.ScriptName(filename) - content := share.ReadFile(filename) - page.LoadScript(string(content), script) - } - }) - - return err - -} - -// LoadPage 载入页面 -func LoadPage(source []byte, name string) (*Page, error) { - page := &Page{ - Flow: gou.Flow{ - Name: name, - }, - } - err := jsoniter.Unmarshal(source, page) - if err != nil { - log.With(log.F{"name": name, "source": source}).Error(err.Error()) - return nil, err - } - page.Prepare() - page.SetupAPIs() - Pages[name] = page - - // Apply a language pack - if lang.Default != nil { - lang.Default.Apply(Pages[name]) - } - - return page, nil -} - -// Select 读取已加载页面 -func Select(name string) *Page { - page, has := Pages[name] - if !has { - exception.New( - fmt.Sprintf("Page:%s; 尚未加载", name), - 400, - ).Throw() - } - return page -} - -// GetData 运行 flow 返回数值 -func (page Page) GetData(params map[string]interface{}) interface{} { - return page.Flow.Exec(params) -} diff --git a/page/page_test.go b/page/page_test.go deleted file mode 100644 index 7446d849..00000000 --- a/page/page_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package page - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/query" - "github.com/yaoapp/yao/share" -) - -func TestLoad(t *testing.T) { - - share.DBConnect(config.Conf.DB) - model.Load(config.Conf) - query.Load(config.Conf) - - Load(config.Conf) - LoadFrom("not a path", "404.") - check(t) -} - -func check(t *testing.T) { - keys := []string{} - for key := range Pages { - keys = append(keys, key) - } - assert.Equal(t, 2, len(keys)) - _, err := i18n.Trans("zh-hk", []string{"chart.lang"}, Pages["lang"]) - if err != nil { - t.Fatal(err) - } - // lang := Pages["lang"] - // output := lang.Output.(map[string]interface{}) - // assert.Equal(t, "{{$in}}", output["參數"]) - - // filters := lang.Page.Layout["filters"].([]interface{}) - // begin := filters[0].(map[string]interface{}) - // assert.Equal(t, "開始時間", begin["name"]) - - // assert.Equal(t, "請選擇開始時間", lang.Filters["開始時間"].Input.Props["placeholder"]) -} diff --git a/page/process.go b/page/process.go deleted file mode 100644 index 21eb2e8e..00000000 --- a/page/process.go +++ /dev/null @@ -1,79 +0,0 @@ -package page - -import ( - "strings" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/maps" -) - -// 注册处理器 -func init() { - gou.RegisterProcessHandler("xiang.page.data", ProcessData) - gou.RegisterProcessHandler("xiang.page.setting", ProcessSetting) -} - -// ProcessData xiang.page.data -// 查询数据分析页面中定义的数据 -func ProcessData(process *gou.Process) interface{} { - - process.ValidateArgNums(2) - name := process.ArgsString(0) - params := process.ArgsMap(1) - page := Select(name) - api := page.APIs["data"] - - if len(api.Default) > 0 { - if defaults, ok := api.Default[0].(map[string]interface{}); ok { - for key, value := range defaults { - if !params.Has(key) { - params.Set(key, value) - } - } - } - } - - // with Session - page.Flow.WithGlobal(process.Global) - page.Flow.WithSID(process.Sid) - - return page.GetData(params) -} - -// ProcessSetting xiang.page.setting -// 查询数据分析页面中定义的数据 -func ProcessSetting(process *gou.Process) interface{} { - - process.ValidateArgNums(2) - name := process.ArgsString(0) - field := process.ArgsString(1) - page := Select(name) - - fields := strings.Split(field, ",") - setting := maps.Map{ - "name": page.Name, - "label": page.Label, - "version": page.Version, - "description": page.Description, - "filters": page.Filters, - "page": page.Page, - } - - if len(fields) == 1 && setting.Has(fields[0]) { - field := strings.TrimSpace(fields[0]) - return setting.Get(field) - } - - if len(fields) > 1 { - res := maps.Map{} - for _, field := range fields { - field = strings.TrimSpace(field) - if setting.Has(field) { - res.Set(field, setting.Get(field)) - } - } - return res - } - return setting - -} diff --git a/page/process_test.go b/page/process_test.go deleted file mode 100644 index 0fb76648..00000000 --- a/page/process_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package page - -import ( - "net/url" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/any" - "github.com/yaoapp/yao/config" - _ "github.com/yaoapp/yao/helper" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/query" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/share" -) - -func init() { - runtime.Load(config.Conf) - share.DBConnect(config.Conf.DB) - model.Load(config.Conf) - query.Load(config.Conf) - Load(config.Conf) -} -func TestProcessSetting(t *testing.T) { - - args := []interface{}{ - "service.compare", - nil, - &gin.Context{}, - } - process := gou.NewProcess("xiang.page.Setting", args...) - response := ProcessSetting(process) - assert.NotNil(t, response) - res := any.Of(response).Map() - assert.True(t, res.Has("name")) - assert.True(t, res.Has("label")) - assert.True(t, res.Has("description")) - assert.True(t, res.Has("page")) - assert.True(t, res.Has("version")) - - args = []interface{}{ - "service.compare", - "page,name", - &gin.Context{}, - } - process = gou.NewProcess("xiang.page.Setting", args...) - response = ProcessSetting(process) - assert.NotNil(t, response) - - res = any.Of(response).Map() - assert.True(t, res.Has("name")) - assert.True(t, res.Has("page")) - assert.False(t, res.Has("label")) -} - -func TestProcessData(t *testing.T) { - - params := url.Values{ - "from": []string{"1981-01-01", "1990-01-01"}, - } - params.Set("to", "2049-12-31") - - args := []interface{}{ - "service.compare", - params, - &gin.Context{}, - } - process := gou.NewProcess("xiang.page.Data", args...) - response := ProcessData(process) - assert.NotNil(t, response) - - res := any.Of(response).Map().Dot() - assert.Equal(t, "北京", res.Get("合并.0.城市")) -} diff --git a/page/types.go b/page/types.go deleted file mode 100644 index 327c9f39..00000000 --- a/page/types.go +++ /dev/null @@ -1,14 +0,0 @@ -package page - -import ( - "github.com/yaoapp/gou" - "github.com/yaoapp/yao/share" -) - -// Page 页面格式 -type Page struct { - gou.Flow - APIs map[string]share.API `json:"apis,omitempty"` - Filters map[string]share.Filter `json:"filters,omitempty"` - Page share.Page `json:"page,omitempty"` -} diff --git a/plugin/plugin.go b/plugin/plugin.go index fa254b35..511bdc25 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -1,33 +1,49 @@ package plugin import ( - "fmt" + "io/fs" "path/filepath" + "strings" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" + "github.com/yaoapp/gou/plugin" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) // Load 加载业务插件 func Load(cfg config.Config) error { - return LoadFrom(filepath.Join(cfg.Root, "plugins")) -} -// LoadFrom 从特定目录加载 -func LoadFrom(dir string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) + root, err := Root(cfg) + if err != nil { + return err } - err := share.Walk(dir, ".so", func(root, filename string) { - name := share.SpecName(root, filename) - _, err := gou.LoadPluginReturn(filename, name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + return filepath.Walk(root, func(file string, info fs.FileInfo, err error) error { + if info.IsDir() { + return nil } + + if !strings.HasSuffix(file, ".so") && !strings.HasSuffix(file, ".dll") { + return nil + } + + _, err = plugin.Load(file, share.ID(root, file)) + return err }) - return err + +} + +// Root return plugin root +func Root(cfg config.Config) (string, error) { + root := filepath.Join(cfg.ExtensionRoot, "plugins") + if cfg.ExtensionRoot == "" { + root = filepath.Join(cfg.Root, "plugins") + } + + root, err := filepath.Abs(root) + if err != nil { + return "", err + } + + return root, nil } diff --git a/plugin/plugin_test.go b/plugin/plugin_test.go index 69964a91..3fba0c41 100644 --- a/plugin/plugin_test.go +++ b/plugin/plugin_test.go @@ -5,21 +5,23 @@ import ( "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/plugin" "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - gou.Plugins = make(map[string]*gou.Plugin) + test.Prepare(t, config.Conf) + defer test.Clean() + Load(config.Conf) - LoadFrom("404") check(t) } func check(t *testing.T) { - keys := []string{} - for key := range gou.Plugins { - keys = append(keys, key) + ids := map[string]bool{} + for id := range plugin.Plugins { + ids[id] = true } - assert.Equal(t, 1, len(keys)) + assert.True(t, ids["user"]) } diff --git a/query/query.go b/query/query.go index 9039532c..485b9c42 100644 --- a/query/query.go +++ b/query/query.go @@ -1,7 +1,8 @@ package query import ( - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/connector" + "github.com/yaoapp/gou/model" "github.com/yaoapp/gou/query" dsl "github.com/yaoapp/gou/query/gou" "github.com/yaoapp/kun/log" @@ -10,22 +11,48 @@ import ( ) // Load 加载查询引擎 -func Load(cfg config.Config) { - DefaultQuery() +func Load(cfg config.Config) error { + + if _, has := query.Engines["default"]; !has { + registerDefault() + } + + // register connector + for id, conn := range connector.Connectors { + if _, has := query.Engines[id]; has { + continue + } + + if conn.Is(connector.DATABASE) { + qb, err := conn.Query() + if err != nil { + log.Error("[Query] load connector error %v", err.Error()) + continue + } + query.Register(id, &dsl.Query{ + Query: qb, + GetTableName: func(s string) string { return s }, + AESKey: config.Conf.DB.AESKey, + }) + } + } + + return nil } -// DefaultQuery register the default engine -func DefaultQuery() { - query.Register("default", &dsl.Query{ - Query: capsule.Query(), - GetTableName: func(s string) string { - if mod, has := gou.Models[s]; has { - return mod.MetaData.Table.Name - } - log.Error("%s model does not load", s) - return s - }, - AESKey: config.Conf.DB.AESKey, - }) - query.Alias("default", "xiang") +// registerDefaultQuery register the default engine +func registerDefault() { + if capsule.Global != nil { + query.Register("default", &dsl.Query{ + Query: capsule.Query(), + GetTableName: func(s string) string { + if mod, has := model.Models[s]; has { + return mod.MetaData.Table.Name + } + log.Error("%s model does not load", s) + return s + }, + AESKey: config.Conf.DB.AESKey, + }) + } } diff --git a/query/query_test.go b/query/query_test.go new file mode 100644 index 00000000..2a53d4b9 --- /dev/null +++ b/query/query_test.go @@ -0,0 +1,37 @@ +package query + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/gou/query" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/connector" + "github.com/yaoapp/yao/test" +) + +func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + loadConnectors(t) + + Load(config.Conf) + check(t) +} + +func check(t *testing.T) { + ids := map[string]bool{} + for id := range query.Engines { + ids[id] = true + } + assert.True(t, ids["default"]) + assert.True(t, ids["mysql"]) + assert.True(t, ids["sqlite"]) +} + +func loadConnectors(t *testing.T) { + err := connector.Load(config.Conf) + if err != nil { + t.Fatal(err) + } +} diff --git a/runtime/runtime.go b/runtime/runtime.go index 1038f8d1..cd6c75ce 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -1,18 +1,39 @@ package runtime import ( - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/runtime" + v8 "github.com/yaoapp/gou/runtime/v8" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/fs" ) -// Load runtime -func Load(cfg config.Config) error { +// Start v8 runtime +func Start(cfg config.Config) error { + dataRoot, err := fs.Root(cfg) if err != nil { return err } - gou.LoadRuntime(runtime.Option{FileRoot: dataRoot}) + + option := &v8.Option{ + MinSize: cfg.Runtime.MinSize, + MaxSize: cfg.Runtime.MaxSize, + HeapSizeLimit: cfg.Runtime.HeapSizeLimit, + HeapAvailableSize: cfg.Runtime.HeapAvailableSize, + HeapSizeRelease: cfg.Runtime.HeapSizeRelease, + Precompile: cfg.Runtime.Precompile, + DataRoot: dataRoot, + } + + err = v8.Start(option) + if err != nil { + return err + } + + return nil +} + +// Stop v8 runtime +func Stop() error { + v8.Stop() return nil } diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go new file mode 100644 index 00000000..3adf0664 --- /dev/null +++ b/runtime/runtime_test.go @@ -0,0 +1,15 @@ +package runtime + +import ( + "testing" + + "github.com/yaoapp/yao/config" +) + +func TestStart(t *testing.T) { + defer Stop() + err := Start(config.Conf) + if err != nil { + t.Fatal(err) + } +} diff --git a/schedule/schedule.go b/schedule/schedule.go index 70fe869e..5ef28cab 100644 --- a/schedule/schedule.go +++ b/schedule/schedule.go @@ -1,36 +1,20 @@ package schedule import ( - "fmt" - "path/filepath" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/schedule" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) // Load load schedule func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "schedules") - return LoadFrom(root, "") -} - -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadSchedule(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + exts := []string{"*.sch.yao", "*.sch.json", "*.sch.jsonc"} + return application.App.Walk("schedules", func(root, file string, isdir bool) error { + if isdir { + return nil } - }) - - return err + _, err := schedule.Load(file, share.ID(root, file)) + return err + }, exts...) } diff --git a/schedule/schedule_test.go b/schedule/schedule_test.go index d0f27e3e..af0fbd0d 100644 --- a/schedule/schedule_test.go +++ b/schedule/schedule_test.go @@ -4,18 +4,28 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/schedule" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/task" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - task.Load(config.Conf) + test.Prepare(t, config.Conf) + defer test.Clean() + err := task.Load(config.Conf) + if err != nil { + t.Fatal(err) + } Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } func check(t *testing.T) { - assert.Equal(t, 2, len(gou.Schedules)) + ids := map[string]bool{} + for id := range schedule.Schedules { + ids[id] = true + } + assert.True(t, ids["mail"]) + assert.True(t, ids["sendmail"]) } diff --git a/script/script.go b/script/script.go index f2547bd8..34c7e739 100644 --- a/script/script.go +++ b/script/script.go @@ -2,48 +2,34 @@ package script import ( "fmt" - "path/filepath" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" + "github.com/yaoapp/gou/application" + v8 "github.com/yaoapp/gou/runtime/v8" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) // Load 加载共享库 func Load(cfg config.Config) error { - if share.BUILDIN { - return LoadBuildIn("scripts") - } + exts := []string{"*.js"} + err := application.App.Walk("scripts", func(root, file string, isdir bool) error { + if isdir { + return nil + } + _, err := v8.Load(file, share.ID(root, file)) + return err + }, exts...) - err := LoadFrom(filepath.Join(cfg.Root, "scripts"), "") if err != nil { return err } - return LoadFrom(filepath.Join(cfg.Root, "services"), "__yao_service.") -} - -// LoadBuildIn 从制品中读取 -func LoadBuildIn(dir string) error { - return nil -} - -// LoadFrom 从特定目录加载共享库 -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - log.Error("%s does not exists", dir) - return nil - } - - // 加载共享脚本 - err := share.Walk(dir, ".js", func(root, filename string) { - name := share.SpecName(root, filename) - err := gou.Yao.Load(filename, fmt.Sprintf("%s%s", prefix, name)) - if err != nil { - log.Error("加载脚本失败 %s", err.Error()) + return application.App.Walk("services", func(root, file string, isdir bool) error { + if isdir { + return nil } - }) - return err + id := fmt.Sprintf("__yao_service.%s", share.ID(root, file)) + _, err := v8.Load(file, id) + return err + }, exts...) } diff --git a/script/script_test.go b/script/script_test.go index c16dfb35..b084a626 100644 --- a/script/script_test.go +++ b/script/script_test.go @@ -1,23 +1,30 @@ package script import ( - "os" - "path" "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + v8 "github.com/yaoapp/gou/runtime/v8" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/runtime" + "github.com/yaoapp/yao/test" ) -func init() { - runtime.Load(config.Conf) - rootLib := path.Join(os.Getenv("YAO_DEV"), "/tests/scripts") - LoadFrom(rootLib, "") +func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + + Load(config.Conf) + check(t) } -func TestScript(t *testing.T) { - res, err := gou.Yao.New("time", "hello").Call("world") - assert.Nil(t, err) - assert.Equal(t, "name:world", res) + +func check(t *testing.T) { + ids := map[string]bool{} + for id := range v8.Scripts { + ids[id] = true + } + assert.True(t, ids["tests.task.mail"]) + assert.True(t, ids["tests.api"]) + assert.True(t, ids["runtime.basic"]) + assert.True(t, ids["runtime.bridge"]) + assert.True(t, ids["__yao_service.foo"]) } diff --git a/service/guard.go b/service/guard.go index aa1ce27f..e7d3e8c3 100644 --- a/service/guard.go +++ b/service/guard.go @@ -5,7 +5,6 @@ import ( "github.com/gin-gonic/gin" "github.com/yaoapp/yao/helper" - table_v0 "github.com/yaoapp/yao/table" "github.com/yaoapp/yao/widgets/chart" "github.com/yaoapp/yao/widgets/dashboard" @@ -18,7 +17,6 @@ import ( var Guards = map[string]gin.HandlerFunc{ "bearer-jwt": guardBearerJWT, // Bearer JWT "cross-origin": guardCrossOrigin, // Cross-Origin Resource Sharing - "table-guard": table_v0.Guard, // Table Guard ( v0.9 table) "widget-table": table.Guard, // Widget Table Guard "widget-list": list.Guard, // Widget List Guard "widget-form": form.Guard, // Widget Form Guard diff --git a/service/middleware.go b/service/middleware.go index 6e3338f3..1554d061 100644 --- a/service/middleware.go +++ b/service/middleware.go @@ -4,11 +4,10 @@ import ( "strings" "github.com/gin-gonic/gin" - "github.com/yaoapp/yao/share" ) // Middlewares 服务中间件 -var Middlewares = []gin.HandlerFunc{ +var Middlewares = []func(c *gin.Context){ BinStatic, } @@ -22,30 +21,21 @@ func BinStatic(c *gin.Context) { c.Next() return - } else if share.App.XGen == "1.0" { + } - // Xgen 1.0 - if length >= AdminRootLen && c.Request.URL.Path[0:AdminRootLen] == AdminRoot { - c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, c.Request.URL.Path[0:AdminRootLen-1]) - XGenFileServerV1.ServeHTTP(c.Writer, c.Request) - c.Abort() - return - } - - if length >= 18 && c.Request.URL.Path[0:18] == "/__yao_admin_root/" { - c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, "/__yao_admin_root") - XGenFileServerV1.ServeHTTP(c.Writer, c.Request) - c.Abort() - return - } - - } else if share.App.XGen == "" && length >= 7 && c.Request.URL.Path[0:7] == "/xiang/" { - // Xgen 0.9 - c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, "/xiang") - XGenFileServerV0.ServeHTTP(c.Writer, c.Request) + // Xgen 1.0 + if length >= AdminRootLen && c.Request.URL.Path[0:AdminRootLen] == AdminRoot { + c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, c.Request.URL.Path[0:AdminRootLen-1]) + XGenFileServerV1.ServeHTTP(c.Writer, c.Request) c.Abort() return + } + if length >= 18 && c.Request.URL.Path[0:18] == "/__yao_admin_root/" { + c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, "/__yao_admin_root") + XGenFileServerV1.ServeHTTP(c.Writer, c.Request) + c.Abort() + return } // 应用内静态文件目录(/ui or public) diff --git a/service/service.go b/service/service.go index b5f24f00..6cbb6203 100644 --- a/service/service.go +++ b/service/service.go @@ -1,91 +1,69 @@ package service import ( - "context" + "time" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" + "github.com/gin-gonic/gin" + "github.com/yaoapp/gou/api" + "github.com/yaoapp/gou/server/http" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) -var shutdown = make(chan bool, 1) +// Start the yao service +func Start(cfg config.Config) (*http.Server, error) { -var shutdownComplete = make(chan bool, 1) - -// Start 启动服务 -func Start() error { + if cfg.AllowFrom == nil { + cfg.AllowFrom = []string{} + } err := prepare() if err != nil { - return err + return nil, err } - gou.SetHTTPGuards(Guards) - gou.ServeHTTP( - gou.Server{ - Host: config.Conf.Host, - Port: config.Conf.Port, - Root: "/api", - Allows: config.Conf.AllowFrom, - }, - shutdown, func(s gou.Server) { - shutdownComplete <- true - }, - Middlewares...) + router := gin.New() + router.Use(gin.Logger()) + api.SetGuards(Guards) + api.SetRoutes(router, "/api", cfg.AllowFrom...) + srv := http.New(router, http.Option{ + Host: cfg.Host, + Port: cfg.Port, + Root: "/api", + Allows: cfg.AllowFrom, + Timeout: 5 * time.Second, + }).With(Middlewares...) + + go func() { + err = srv.Start() + }() + + return srv, nil +} + +// Stop the yao service +func Stop(srv *http.Server) error { + err := srv.Stop() + if err != nil { + return err + } + <-srv.Event() return nil } -// StartWithouttSession 启动服务 -func StartWithouttSession() { - - gou.SetHTTPGuards(Guards) - gou.ServeHTTP( - gou.Server{ - Host: config.Conf.Host, - Port: config.Conf.Port, - Root: "/api", - Allows: config.Conf.AllowFrom, - }, - shutdown, func(s gou.Server) { - shutdownComplete <- true - }, - Middlewares...) -} - -// StopWithouttSession 关闭服务 -func StopWithouttSession(onComplete func()) { - shutdown <- true - select { - case <-shutdownComplete: - onComplete() - } -} - -// Stop 关闭服务 -func Stop(onComplete func()) { - shutdown <- true - select { - case <-shutdownComplete: - share.SessionStop() - share.DBClose() - onComplete() - } -} - // StopWithContext stop with timeout -func StopWithContext(ctx context.Context, onComplete func()) { - shutdown <- true - select { - case <-ctx.Done(): - log.Error("[STOP] canceled (%v)", ctx.Err()) - onComplete() - case <-shutdownComplete: - share.SessionStop() - onComplete() - } -} +// func StopWithContext(ctx context.Context, onComplete func()) { +// shutdown <- true +// select { +// case <-ctx.Done(): +// log.Error("[STOP] canceled (%v)", ctx.Err()) +// onComplete() +// case <-shutdownComplete: +// share.SessionStop() +// onComplete() +// } +// } func prepare() error { diff --git a/service/service_test.go b/service/service_test.go new file mode 100644 index 00000000..f86495fa --- /dev/null +++ b/service/service_test.go @@ -0,0 +1,74 @@ +package service + +import ( + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/engine" + "github.com/yaoapp/yao/test" +) + +func TestStartStop(t *testing.T) { + + gin.SetMode(gin.ReleaseMode) + + cfg := config.Conf + cfg.Port = 0 + err := engine.Load(cfg) + if err != nil { + t.Fatal(err) + } + + srv, err := Start(cfg) + if err != nil { + t.Fatal(err) + } + defer Stop(srv) + + <-srv.Event() + if !srv.Ready() { + t.Fatal("server not ready") + } + + port, err := srv.Port() + if err != nil { + t.Fatal(err) + } + + if port <= 0 { + t.Fatal("invalid port") + } + + // API Server + req := test.NewRequest(port).Route("/api/__yao/app/setting") + res, err := req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 200, res.Status()) + data, err := res.Map() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "Demo Application", data["name"]) + + // Public + req = test.NewRequest(port).Route("/") + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 200, res.Status()) + assert.Equal(t, "Hello World\n", res.Body()) + + // XGEN + req = test.NewRequest(port).Route("/admin/") + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 200, res.Status()) + assert.Contains(t, res.Body(), "ROOT /admin/") +} diff --git a/service/static.go b/service/static.go index a13be588..e58aaff4 100644 --- a/service/static.go +++ b/service/static.go @@ -18,9 +18,6 @@ import ( // AppFileServer static file server var AppFileServer http.Handler -// XGenFileServerV0 XGen v0.9 -var XGenFileServerV0 http.Handler = http.FileServer(data.XgenV0()) - // XGenFileServerV1 XGen v1.0 var XGenFileServerV1 http.Handler = http.FileServer(data.XgenV1()) @@ -41,9 +38,6 @@ func SetupStatic() error { // Static file server AppFileServer = http.FileServer(Dir(filepath.Join(config.Conf.Root, "public"))) - if share.App.XGen == "" || share.App.XGen == "0.9" { - AppFileServer = http.FileServer(Dir(filepath.Join(config.Conf.Root, "ui"))) - } return nil } diff --git a/service/watch.go b/service/watch.go index 6dd81a53..48a2f755 100644 --- a/service/watch.go +++ b/service/watch.go @@ -1,7 +1,6 @@ package service import ( - "context" "fmt" "io/fs" "io/ioutil" @@ -14,7 +13,6 @@ import ( "github.com/fatih/color" "github.com/fsnotify/fsnotify" - "github.com/yaoapp/gou" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/engine" @@ -207,44 +205,44 @@ func watchStart(cfg config.Config) error { } func watchModel(root string, file string, event string, cfg config.Config) { - name := share.SpecName(root, file) - switch event { - case "CREATE": - content, err := ioutil.ReadFile(file) - if err != nil { - fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error())) - return - } - _, err = gou.LoadModelReturn(string(content), name) - if err != nil { - fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error())) - return - } + // name := share.SpecName(root, file) + // switch event { + // case "CREATE": + // content, err := ioutil.ReadFile(file) + // if err != nil { + // fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error())) + // return + // } + // _, err = gou.LoadModelReturn(string(content), name) + // if err != nil { + // fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error())) + // return + // } - // mod.Migrate(true) - fmt.Println(color.GreenString("[Watch] Model: %s Created (Please run yao migrate manually)", name)) - break + // // mod.Migrate(true) + // fmt.Println(color.GreenString("[Watch] Model: %s Created (Please run yao migrate manually)", name)) + // break - case "WRITE": - content, err := ioutil.ReadFile(file) - if err != nil { - fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error())) - return - } - _, err = gou.LoadModelReturn(string(content), name) - if err != nil { - fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error())) - return - } - // mod.Migrate(false) - fmt.Println(color.GreenString("[Watch] Model: %s Reloaded (Please run yao migrate manually)", name)) - break + // case "WRITE": + // content, err := ioutil.ReadFile(file) + // if err != nil { + // fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error())) + // return + // } + // _, err = gou.LoadModelReturn(string(content), name) + // if err != nil { + // fmt.Println(color.RedString("[Watch] Model: %s %s", name, err.Error())) + // return + // } + // // mod.Migrate(false) + // fmt.Println(color.GreenString("[Watch] Model: %s Reloaded (Please run yao migrate manually)", name)) + // break - case "REMOVE", "RENAME": - delete(gou.Models, name) - fmt.Println(color.GreenString("[Watch] Model: %s Removed", name)) - break - } + // case "REMOVE", "RENAME": + // delete(gou.Models, name) + // fmt.Println(color.GreenString("[Watch] Model: %s Removed", name)) + // break + // } } func watchReload(root string, file string, event string, cfg config.Config) { @@ -263,12 +261,12 @@ func watchReload(root string, file string, event string, cfg config.Config) { } // Restart Server - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() + // ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + // defer cancel() - StopWithContext(ctx, func() { - go Start() - fmt.Println(color.GreenString("[Watch] Reload Completed")) - }) + // Stop(func() { + // go Start() + // fmt.Println(color.GreenString("[Watch] Reload Completed")) + // }) } } diff --git a/service/watch_test.go b/service/watch_test.go index e1cd9b37..aad54ec9 100644 --- a/service/watch_test.go +++ b/service/watch_test.go @@ -34,8 +34,8 @@ func TestWatch(t *testing.T) { } func TestWatchReload(t *testing.T) { - go Start() - defer Stop(func() {}) + go Start(config.Conf) + // defer Stop(func() {}) share.DBConnect(config.Conf.DB) watchReload("", "", "", config.Conf) } diff --git a/setup/check.go b/setup/check.go index 39219153..94c3a1b3 100644 --- a/setup/check.go +++ b/setup/check.go @@ -16,7 +16,7 @@ func Check() bool { root := appRoot() - appfile := filepath.Join(root, "app.json") + appfile := filepath.Join(root, "app.yao") if _, err := os.Stat(appfile); err != nil && os.IsNotExist(err) { return true } diff --git a/setup/install.go b/setup/install.go index 7f1522e0..b969e5ef 100644 --- a/setup/install.go +++ b/setup/install.go @@ -6,7 +6,8 @@ import ( "path/filepath" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/data" "github.com/yaoapp/yao/engine" @@ -276,7 +277,7 @@ func makeMigrate(root string) error { } // Do Stuff Here - for _, mod := range gou.Models { + for _, mod := range model.Models { has, err := mod.HasTable() if err != nil { return err @@ -316,17 +317,17 @@ func makeSetup(root string) error { return fmt.Errorf("setup studio script %s error", app.Setting.Setup) } - service := strings.Join(names[1:len(names)-1], ".") - method := names[len(names)-1] - req := gou.Yao.New(service, method) - _, err := req.RootCall(cfg) - if err != nil { - return err - } + // service := strings.Join(names[1:len(names)-1], ".") + // method := names[len(names)-1] + // req := gou.Yao.New(service, method) + // _, err := req.RootCall(cfg) + // if err != nil { + // return err + // } return nil } - p, err := gou.ProcessOf(app.Setting.Setup, cfg) + p, err := process.Of(app.Setting.Setup, cfg) if err != nil { return err } diff --git a/share/api.go b/share/api.go index 3034e3d4..f76978bc 100644 --- a/share/api.go +++ b/share/api.go @@ -1,13 +1,11 @@ package share import ( - "fmt" "strings" - "github.com/gin-gonic/gin" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/query/share" + "github.com/yaoapp/gou/helper" "github.com/yaoapp/gou/session" + "github.com/yaoapp/gou/types" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" @@ -15,26 +13,6 @@ import ( "github.com/yaoapp/kun/utils" ) -// IsAllow 鉴权处理程序 (废弃) -func (api API) IsAllow(v interface{}) bool { - c, ok := v.(*gin.Context) - if !ok { - return false - } - - guards := strings.Split(api.Guard, ",") - for _, guard := range guards { - guard = strings.TrimSpace(guard) - handler, has := gou.HTTPGuards[guard] - if has { - handler(c) - fmt.Println(api.Guard, c.IsAborted()) - return c.IsAborted() - } - } - return false -} - // ValidateLoop 循环引用校验 func (api API) ValidateLoop(name string) API { if strings.ToLower(api.Process) == strings.ToLower(name) { @@ -88,7 +66,7 @@ func (api API) DefaultString(i int, defaults ...string) string { } // MergeDefaultQueryParam 合并默认查询参数 -func (api API) MergeDefaultQueryParam(param gou.QueryParam, i int, sid string) gou.QueryParam { +func (api API) MergeDefaultQueryParam(param types.QueryParam, i int, sid string) types.QueryParam { if len(api.Default) > i && api.Default[i] != nil { defaults := GetQueryParam(api.Default[i], sid) @@ -104,7 +82,7 @@ func (api API) MergeDefaultQueryParam(param gou.QueryParam, i int, sid string) g if defaults.Wheres != nil { if param.Wheres == nil { - param.Wheres = []gou.QueryWhere{} + param.Wheres = []types.QueryWhere{} } param.Wheres = append(param.Wheres, defaults.Wheres...) } @@ -117,7 +95,7 @@ func (api API) MergeDefaultQueryParam(param gou.QueryParam, i int, sid string) g } // GetQueryParam 解析参数 -func GetQueryParam(v interface{}, sid string) gou.QueryParam { +func GetQueryParam(v interface{}, sid string) types.QueryParam { log.With(log.F{"sid": sid}).Trace("GetQueryParam Entry") data := map[string]interface{}{} if sid != "" { @@ -129,8 +107,8 @@ func GetQueryParam(v interface{}, sid string) gou.QueryParam { log.Error("读取会话信息出错 %s", err.Error()) } } - v = share.Bind(v, maps.Of(data).Dot()) - param, ok := gou.AnyToQueryParam(v) + v = helper.Bind(v, maps.Of(data).Dot()) + param, ok := types.AnyToQueryParam(v) if !ok { exception.New("参数默认值数据结构错误", 400).Ctx(v).Throw() } diff --git a/share/columns.go b/share/columns.go index aff7e54b..d7cda9e9 100644 --- a/share/columns.go +++ b/share/columns.go @@ -3,7 +3,7 @@ package share import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" ) var elms = map[string]Column{ @@ -54,7 +54,7 @@ var elms = map[string]Column{ // GetDefaultColumns 读取数据模型字段的呈现方式 func GetDefaultColumns(name string) map[string]Column { - mod := gou.Select(name) + mod := model.Select(name) cmap := mod.Columns columns := map[string]Column{} diff --git a/share/db.go b/share/db.go index 93992833..f5750cd7 100644 --- a/share/db.go +++ b/share/db.go @@ -11,7 +11,7 @@ import ( ) // DBConnect 建立数据库连接 -func DBConnect(dbconfig config.DBConfig) (err error) { +func DBConnect(dbconfig config.Database) (err error) { if dbconfig.Primary == nil { return fmt.Errorf("YAO_DB_PRIMARY was not set") diff --git a/share/filters.go b/share/filters.go index 7d5e1604..ae6791a6 100644 --- a/share/filters.go +++ b/share/filters.go @@ -3,13 +3,13 @@ package share import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" ) // GetDefaultFilters 读取数据模型索引字段的过滤器 func GetDefaultFilters(name string) map[string]Filter { - mod := gou.Select(name) + mod := model.Select(name) cmap := mod.Columns filters := map[string]Filter{} for _, index := range mod.MetaData.Indexes { diff --git a/share/importable.go b/share/importable.go index ce828a8b..b8c39ec2 100644 --- a/share/importable.go +++ b/share/importable.go @@ -5,8 +5,7 @@ import ( "path/filepath" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/query/share" + "github.com/yaoapp/gou/helper" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" "github.com/yaoapp/kun/maps" @@ -63,11 +62,11 @@ func LoadFrom(dir string) error { // 加载共享脚本 err = Walk(dir, ".js", func(root, filename string) { - name := SpecName(root, filename) - err := gou.Yao.Load(filename, name) - if err != nil { - log.Error("加载脚本失败 %s", err.Error()) - } + // name := SpecName(root, filename) + // err := gou.Yao.Load(filename, name) + // if err != nil { + // log.Error("加载脚本失败 %s", err.Error()) + // } }) return err } @@ -174,7 +173,7 @@ func ImportJSON(name string, in []interface{}, v interface{}) error { } data := maps.MapStrAny{"$in": in}.Dot() - content, err := jsoniter.Marshal(share.Bind(lib, data)) + content, err := jsoniter.Marshal(helper.Bind(lib, data)) if err != nil { return err } diff --git a/share/pack.go b/share/pack.go new file mode 100644 index 00000000..5ee5b6f0 --- /dev/null +++ b/share/pack.go @@ -0,0 +1,18 @@ +package share + +// ******************************************************************************** +// WARNING: DO NOT MODIFY THIS FILE. IT WILL BE REPLACED BY THE APPLICATION CODE. +// ********************************************************************************* + +// Pack the yao app package +type Pack struct{} + +// Decode the package decode method +func (pkg *Pack) Decode(data []byte) ([]byte, error) { + return data, nil +} + +// Encode the package encode method +func (pkg *Pack) Encode(data []byte) ([]byte, error) { + return data, nil +} diff --git a/share/utils.go b/share/utils.go index 1a6f9d7c..98ec8d25 100644 --- a/share/utils.go +++ b/share/utils.go @@ -1,6 +1,7 @@ package share import ( + "fmt" "io/ioutil" "os" "path" @@ -35,6 +36,13 @@ func ID(root string, file string) string { return SpecName(root, file) } +// File ID to file +func File(id string, ext string) string { + ext = strings.TrimLeft(ext, ".") + file := strings.ReplaceAll(id, ".", string(os.PathSeparator)) + return fmt.Sprintf("%s.%s", file, ext) +} + // SpecName 解析名称 root: "/tests/apis" file: "/tests/apis/foo/bar.http.json" func SpecName(root string, file string) string { filename := strings.TrimPrefix(file, root+"/") // "foo/bar.http.json" diff --git a/socket/socket.go b/socket/socket.go index 7a7e4723..4a9ab9f4 100644 --- a/socket/socket.go +++ b/socket/socket.go @@ -1,36 +1,31 @@ package socket import ( - "fmt" - "path/filepath" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/share" ) // Load 加载API func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "sockets") - return LoadFrom(root, "") + // var root = filepath.Join(cfg.Root, "sockets") + // return LoadFrom(root, "") + return nil } // LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) error { +// func LoadFrom(dir string, prefix string) error { - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } +// if share.DirNotExists(dir) { +// return fmt.Errorf("%s does not exists", dir) +// } - err := share.Walk(dir, ".sock.json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadSocket(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) - } - }) +// err := share.Walk(dir, ".sock.json", func(root, filename string) { +// name := prefix + share.SpecName(root, filename) +// content := share.ReadFile(filename) +// _, err := gou.LoadSocket(string(content), name) +// if err != nil { +// log.With(log.F{"root": root, "file": filename}).Error(err.Error()) +// } +// }) - return err -} +// return err +// } diff --git a/socket/socket_test.go b/socket/socket_test.go index 1f41a566..e7c142d1 100644 --- a/socket/socket_test.go +++ b/socket/socket_test.go @@ -4,20 +4,19 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/socket" "github.com/yaoapp/yao/config" ) func TestLoad(t *testing.T) { Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } func check(t *testing.T) { keys := []string{} - for key := range gou.Sockets { + for key := range socket.Sockets { keys = append(keys, key) } - assert.Equal(t, 2, len(keys)) + assert.Equal(t, 0, len(keys)) } diff --git a/store/store.go b/store/store.go index 4b5e3db6..35ae8313 100644 --- a/store/store.go +++ b/store/store.go @@ -1,36 +1,21 @@ package store import ( - "fmt" - "path/filepath" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/store" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) // Load load store func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "stores") - return LoadFrom(root, "") -} - -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadStore(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + exts := []string{"*.yao", "*.json", "*.jsonc"} + return application.App.Walk("stores", func(root, file string, isdir bool) error { + if isdir { + return nil } - }) - return err + _, err := store.Load(file, share.ID(root, file)) + return err + }, exts...) } diff --git a/store/store_test.go b/store/store_test.go index ce45a60c..4cb51b01 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -7,19 +7,31 @@ import ( "github.com/yaoapp/gou/store" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/connector" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - connector.Load(config.Conf) + test.Prepare(t, config.Conf) + defer test.Clean() + loadConnectors(t) + Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } func check(t *testing.T) { - keys := []string{} - for key := range store.Pools { - keys = append(keys, key) + ids := map[string]bool{} + for id := range store.Pools { + ids[id] = true + } + assert.True(t, ids["cache"]) + assert.True(t, ids["data"]) + assert.True(t, ids["share"]) +} + +func loadConnectors(t *testing.T) { + err := connector.Load(config.Conf) + if err != nil { + t.Fatal(err) } - assert.Equal(t, 3, len(keys)) } diff --git a/studio/router.go b/studio/router.go index 9ad83395..90ce469b 100644 --- a/studio/router.go +++ b/studio/router.go @@ -10,7 +10,7 @@ import ( "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + v8 "github.com/yaoapp/gou/runtime/v8" ) var regExcp = regexp.MustCompile("^Exception\\|([0-9]+):(.+)$") @@ -202,12 +202,18 @@ func setRouter(router *gin.Engine) { return } - req := gou.Yao.New(service, fun.Method) - if sid, has := c.Get("__sid"); has { - req.WithSid(fmt.Sprintf("%s", sid)) + sid, _ := c.Get("__sid") + script, err := v8.SelectRoot(service) + if err != nil { + throw(c, 500, err.Error()) } - res, err := req.RootCall(fun.Args...) + ctx, err := script.NewContext(fmt.Sprintf("%v", sid), nil) + if err != nil { + throw(c, 500, err.Error()) + } + res, err := ctx.Call(fun.Method, fun.Args...) + if err != nil { // parse Exception code := 500 diff --git a/studio/studio.go b/studio/studio.go index 4e3ab054..436951f7 100644 --- a/studio/studio.go +++ b/studio/studio.go @@ -11,9 +11,9 @@ import ( "syscall" "github.com/gin-gonic/gin" - "github.com/yaoapp/gou" "github.com/yaoapp/gou/fs" "github.com/yaoapp/gou/fs/dsl" + v8 "github.com/yaoapp/gou/runtime/v8" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" @@ -135,7 +135,7 @@ func loadScriptFrom(dir string) error { messages := []string{} err := share.Walk(dir, ".js", func(root, filename string) { name := share.SpecName(root, filename) - err := gou.Yao.RootLoad(filename, name) + _, err := v8.LoadRoot(filename, name) if err != nil { messages = append(messages, err.Error()) } diff --git a/studio/studio_test.go b/studio/studio_test.go index e3d08cd9..cc2a111e 100644 --- a/studio/studio_test.go +++ b/studio/studio_test.go @@ -10,30 +10,27 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/helper" - "github.com/yaoapp/yao/runtime" ) type kv map[string]interface{} type arr []interface{} func TestLoad(t *testing.T) { - runtime.Load(config.Conf) err := Load(config.Conf) if err != nil { t.Fatal(err) } assert.NotNil(t, dfs) - res, err := gou.Yao.Engine.RootCall(map[string]interface{}{}, "table", "Ping") - assert.Nil(t, err) - assert.Equal(t, "PONG", res) + // res, err := gou.Yao.Engine.RootCall(map[string]interface{}{}, "table", "Ping") + // assert.Nil(t, err) + // assert.Equal(t, "PONG", res) - _, err = gou.Yao.Engine.Call(map[string]interface{}{}, "table", "Ping") - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "The table does not loaded") + // _, err = gou.Yao.Engine.Call(map[string]interface{}{}, "table", "Ping") + // assert.NotNil(t, err) + // assert.Contains(t, err.Error(), "The table does not loaded") } func TestStartStop(t *testing.T) { diff --git a/system/process.go b/system/process.go deleted file mode 100644 index c1155c60..00000000 --- a/system/process.go +++ /dev/null @@ -1,33 +0,0 @@ -package system - -// func init() { -// // gou.RegisterProcessHandler("yao.system.Exec", processExec) -// } - -// // processExec execute the system command -// func processExec(process *gou.Process) interface{} { -// process.ValidateArgNums(1) -// cmd := process.ArgsString(0) - -// _, err := exec.LookPath(cmd) -// if err != nil { -// exception.New("command %s not found: %s", 400, cmd, err.Error()).Throw() -// return nil -// } - -// args := []string{} -// for i, arg := range process.Args { -// if i == 0 { -// continue -// } -// args = append(args, fmt.Sprintf("%v", arg)) -// } - -// res, err := exec.Command(cmd, args...).Output() -// if err != nil { -// exception.New("command %s error: %s", 500, cmd, err.Error()).Throw() -// return nil -// } - -// return strings.TrimRight(string(res), "\n") -// } diff --git a/system/process_test.go b/system/process_test.go deleted file mode 100644 index cb0bfbe9..00000000 --- a/system/process_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package system - -// func TestProcessReturn(t *testing.T) { -// output, err := gou.NewProcess("yao.system.Exec", "echo", "hello").Exec() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, "hello", output) -// } diff --git a/system/system.go b/system/system.go deleted file mode 100644 index bfd256e1..00000000 --- a/system/system.go +++ /dev/null @@ -1,4 +0,0 @@ -package system - -// Exec call system command -// func Exec(cmd string, args ...string) {} diff --git a/table/api.go b/table/api.go deleted file mode 100644 index 609b964d..00000000 --- a/table/api.go +++ /dev/null @@ -1,79 +0,0 @@ -package table - -import ( - "fmt" - - "github.com/yaoapp/gou" - "github.com/yaoapp/yao/share" -) - -// apiSearchDefault search 接口默认值 -func apiSearchDefault(model *gou.Model, withs map[string]gou.With) share.API { - query := gou.QueryParam{} - if model.MetaData.Option.Timestamps { - query.Orders = []gou.QueryOrder{ - {Column: "created_at", Option: "desc"}, - } - } - - if withs != nil { - query.Withs = withs - } - - return share.API{ - Name: "search", - // Guard: "bearer-jwt", - Process: fmt.Sprintf("models.%s.Paginate", model.Name), - Default: []interface{}{query, 1, 20}, - } -} - -// apiFindDefault find 接口默认值 -func apiFindDefault(model *gou.Model, withs map[string]gou.With) share.API { - - query := gou.QueryParam{} - if withs != nil { - query.Withs = withs - } - - return share.API{ - Name: "find", - // Guard: "bearer-jwt", - Process: fmt.Sprintf("models.%s.Find", model.Name), - Default: []interface{}{nil, query}, - } -} - -// apiDefault 接口默认值 -func apiDefault(model *gou.Model, name string, process string) share.API { - return share.API{ - Name: name, - // Guard: "bearer-jwt", - Process: fmt.Sprintf("models.%s.%s", model.Name, process), - } -} - -// apiFindDefault 接口默认值 -func apiDefaultWhere(model *gou.Model, withs map[string]gou.With, name string, process string) share.API { - - query := gou.QueryParam{} - if withs != nil { - query.Withs = withs - } - - return share.API{ - Name: name, - // Guard: "bearer-jwt", - Process: fmt.Sprintf("models.%s.%s", model.Name, process), - Default: []interface{}{query}, - } -} - -// apiDefaultSetting 数据表格配置默认值 -func apiDefaultSetting() share.API { - return share.API{ - Name: "setting", - // Guard: "bearer-jwt", - Process: fmt.Sprintf("xiang.table.setting"), - } -} diff --git a/table/json.go b/table/json.go deleted file mode 100644 index 4861f272..00000000 --- a/table/json.go +++ /dev/null @@ -1,42 +0,0 @@ -package table - -// // UnmarshalJSON for json marshalJSON -// func (table *Table) UnmarshalJSON(data []byte) error { - -// var v interface{} -// err := jsoniter.Unmarshal(data, &v) -// if err != nil { -// return err -// } - -// // values := []interface{}{} -// // switch v.(type) { -// // case string: // "kind rollup 所有类型, city" -// // strarr := strings.Split(v.(string), ",") -// // for _, str := range strarr { -// // groups.PushString(str) -// // } -// // break -// // case []interface{}: // ["name", {"field":"foo"}, "id rollup 所有类型"] -// // values = v.([]interface{}) -// // break -// // } - -// // for _, value := range values { -// // switch value.(type) { -// // case string: // "kind rollup 所有类型" -// // groups.PushString(value.(string)) -// // break -// // case map[string]interface{}: // {"field":"foo"} -// // groups.PushMap(value.(map[string]interface{})) -// // break -// // } -// // } - -// return nil -// } - -// // MarshalJSON for json marshalJSON -// func (table Table) MarshalJSON() ([]byte, error) { -// return jsoniter.Marshal(nil) -// } diff --git a/table/lang.go b/table/lang.go deleted file mode 100644 index 4b23f071..00000000 --- a/table/lang.go +++ /dev/null @@ -1,98 +0,0 @@ -package table - -// Lang for applying a language pack -func (table *Table) Lang(trans func(widget string, inst string, value *string) bool) { - inst := table.Table - widget := "table" - - trans(widget, inst, &table.Name) - trans(widget, inst, &table.Decription) - - // Columns - for name, column := range table.Columns { - new := name - trans(widget, inst, &new) - trans(widget, inst, &column.Label) - - // Props - transMap(widget, inst, column.Edit.Props, trans) - transMap(widget, inst, column.View.Props, trans) - transMap(widget, inst, column.Form.Props, trans) - - delete(table.Columns, name) - table.Columns[new] = column - } - - // Filters - for name, filter := range table.Filters { - new := name - trans(widget, inst, &new) - trans(widget, inst, &filter.Label) - delete(table.Filters, name) - - // Props - transMap(widget, inst, filter.Input.Props, trans) - table.Filters[new] = filter - } - - // List - transMap(widget, inst, table.List.Layout, trans) - transMap(widget, inst, table.List.Option, trans) - - // Edit - transMap(widget, inst, table.Edit.Layout, trans) - transMap(widget, inst, table.Edit.Option, trans) -} - -func transMap(widget string, inst string, values map[string]interface{}, trans func(widget string, inst string, value *string) bool) { - for key, value := range values { - - switch value.(type) { - - case string: - v := value.(string) - trans(widget, inst, &v) - values[key] = v - break - - case []interface{}: - v := value.([]interface{}) - transArr(widget, inst, v, trans) - values[key] = v - break - - case map[string]interface{}: - v := value.(map[string]interface{}) - transMap(widget, inst, v, trans) - values[key] = v - break - } - - } -} - -func transArr(widget string, inst string, values []interface{}, trans func(widget string, inst string, value *string) bool) { - for key, value := range values { - switch value.(type) { - - case string: - v := value.(string) - trans(widget, inst, &v) - values[key] = v - break - - case []interface{}: - v := value.([]interface{}) - transArr(widget, inst, v, trans) - values[key] = v - break - - case map[string]interface{}: - v := value.(map[string]interface{}) - transMap(widget, inst, v, trans) - values[key] = v - break - } - - } -} diff --git a/table/process.go b/table/process.go deleted file mode 100644 index da08c436..00000000 --- a/table/process.go +++ /dev/null @@ -1,445 +0,0 @@ -package table - -import ( - "crypto/md5" - "encoding/hex" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/any" - "github.com/yaoapp/kun/log" - "github.com/yaoapp/kun/maps" - "github.com/yaoapp/yao/xfs" -) - -func init() { - // 注册处理器 - gou.RegisterProcessHandler("xiang.table.Search", ProcessSearch) - gou.RegisterProcessHandler("xiang.table.Find", ProcessFind) - gou.RegisterProcessHandler("xiang.table.Select", ProcessSelect) - gou.RegisterProcessHandler("xiang.table.Save", ProcessSave) - gou.RegisterProcessHandler("xiang.table.Delete", ProcessDelete) - gou.RegisterProcessHandler("xiang.table.Insert", ProcessInsert) - gou.RegisterProcessHandler("xiang.table.UpdateWhere", ProcessUpdateWhere) - gou.RegisterProcessHandler("xiang.table.DeleteWhere", ProcessDeleteWhere) - gou.RegisterProcessHandler("xiang.table.QuickSave", ProcessQuickSave) - gou.RegisterProcessHandler("xiang.table.UpdateIn", ProcessUpdateIn) - gou.RegisterProcessHandler("xiang.table.DeleteIn", ProcessDeleteIn) - gou.RegisterProcessHandler("xiang.table.Export", ProcessExport) - gou.RegisterProcessHandler("xiang.table.Setting", ProcessSetting) -} - -// ProcessSearch xiang.table.Search -// 按条件查询数据记录, 请求成功返回符合查询条件带有分页信息的数据对象 -func ProcessSearch(process *gou.Process) interface{} { - - // 读取表格名称 - process.ValidateArgNums(1) - name := process.ArgsString(0) - table := Select(name) - - api := table.APIs["search"].ValidateLoop("xiang.table.search") - - // Before Hook - process.Args = table.Before(table.Hooks.BeforeSearch, process.Args, process.Sid) - - // 参数表 - process.ValidateArgNums(4) - param := api.MergeDefaultQueryParam(process.ArgsQueryParams(1), 0, process.Sid) - log.With(log.F{"param": param}).Trace("==== ProcessSearch ============= SID: %s", process.Sid) - page := process.ArgsInt(2, api.DefaultInt(1)) - pagesize := process.ArgsInt(3, api.DefaultInt(2)) - - // 查询数据 - response := gou.NewProcess(api.Process, param, page, pagesize). - WithGlobal(process.Global). - WithSID(process.Sid). - Run() - - // After Hook - return table.After(table.Hooks.AfterSearch, response, []interface{}{param, page, pagesize}, process.Sid) -} - -// ProcessFind xiang.table.Find -// 按主键值查询单条数据, 请求成功返回对应主键的数据记录 -func ProcessFind(process *gou.Process) interface{} { - - process.ValidateArgNums(1) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["find"].ValidateLoop("xiang.table.find") - - // Before Hook - process.Args = table.Before(table.Hooks.BeforeFind, process.Args, process.Sid) - - // 参数表 - process.ValidateArgNums(2) - id := process.Args[1] - queryParam := gou.QueryParam{} - if process.NumOfArgs() == 3 { - queryParam = process.ArgsQueryParams(2) - } - param := api.MergeDefaultQueryParam(queryParam, 1, process.Sid) - - // 查询数据 - response := gou.NewProcess(api.Process, id, param). - WithGlobal(process.Global). - WithSID(process.Sid). - Run() - - // After Hook - return table.After(table.Hooks.AfterFind, response, []interface{}{id, param}, process.Sid) -} - -// ProcessSave xiang.table.Save -// 保存单条记录。如数据记录中包含主键字段则更新,不包含主键字段则创建记录;返回创建或更新的记录主键值 -func ProcessSave(process *gou.Process) interface{} { - - // 读取表格名称 - process.ValidateArgNums(1) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["save"].ValidateLoop("xiang.table.save") - - // Before Hook - process.Args = table.Before(table.Hooks.BeforeSave, process.Args, process.Sid) - - // 参数处理 - process.ValidateArgNums(2) - - // 查询数据 - response := gou.NewProcess(api.Process, process.Args[1]). - WithGlobal(process.Global). - WithSID(process.Sid). - Run() - - // After Hook - return table.After(table.Hooks.AfterSave, response, []interface{}{process.Args[1]}, process.Sid) -} - -// ProcessDelete xiang.table.Delete -// 删除指定主键值的数据记录, 请求成功返回null -func ProcessDelete(process *gou.Process) interface{} { - process.ValidateArgNums(2) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["delete"].ValidateLoop("xiang.table.delete") - - // Before Hook - process.Args = table.Before(table.Hooks.BeforeDelete, process.Args, process.Sid) - - id := process.Args[1] - response := gou.NewProcess(api.Process, id). - WithGlobal(process.Global). - WithSID(process.Sid). - Run() - - // After Hook - return table.After(table.Hooks.AfterDelete, response, []interface{}{id}, process.Sid) -} - -// ProcessDeleteWhere xiang.table.DeleteWhere -// 按条件批量删除数据, 请求成功返回删除行数 -func ProcessDeleteWhere(process *gou.Process) interface{} { - - process.ValidateArgNums(2) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["delete-where"].ValidateLoop("xiang.table.DeleteWhere") - - // 批量删除 - param := api.MergeDefaultQueryParam(process.ArgsQueryParams(1), 0, process.Sid) - if param.Limit == 0 { // 限定删除行 - param.Limit = 10 - } - - return gou.NewProcess(api.Process, param). - WithGlobal(process.Global). - WithSID(process.Sid). - Run() -} - -// ProcessDeleteIn xiang.table.DeleteIn -// 按条件批量删除数据, 请求成功返回删除行数 -func ProcessDeleteIn(process *gou.Process) interface{} { - - process.ValidateArgNums(3) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["delete-in"].ValidateLoop("xiang.table.DeleteIn") - - // 批量删除 - ids := strings.Split(process.ArgsString(1), ",") - primary := process.ArgsString(2, "id") - param := gou.QueryParam{ - Wheres: []gou.QueryWhere{ - {Column: primary, OP: "in", Value: ids}, - }, - } - - return gou.NewProcess(api.Process, param). - WithGlobal(process.Global). - WithSID(process.Sid). - Run() -} - -// ProcessUpdateWhere xiang.table.UpdateWhere -// 按条件批量更新数据, 请求成功返回更新行数 -func ProcessUpdateWhere(process *gou.Process) interface{} { - - process.ValidateArgNums(3) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["update-where"].ValidateLoop("xiang.table.UpdateWhere") - - // 批量更新 - param := api.MergeDefaultQueryParam(process.ArgsQueryParams(1), 0, process.Sid) - if param.Limit == 0 { // 限定删除行 - param.Limit = 10 - } - return gou.NewProcess(api.Process, param, process.Args[2]). - WithGlobal(process.Global). - WithSID(process.Sid). - Run() -} - -// ProcessUpdateIn xiang.table.UpdateWhere -// 按条件批量更新数据, 请求成功返回更新行数 -func ProcessUpdateIn(process *gou.Process) interface{} { - - process.ValidateArgNums(4) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["update-in"].ValidateLoop("xiang.table.UpdateIn") - - // 批量删除 - ids := strings.Split(process.ArgsString(1), ",") - primary := process.ArgsString(2, "id") - param := gou.QueryParam{ - Wheres: []gou.QueryWhere{ - {Column: primary, OP: "in", Value: ids}, - }, - } - return gou.NewProcess(api.Process, param, process.Args[3]). - WithGlobal(process.Global). - WithSID(process.Sid). - Run() -} - -// ProcessInsert xiang.table.Insert -// 插入多条数据记录,请求成功返回插入行数 -func ProcessInsert(process *gou.Process) interface{} { - process.ValidateArgNums(3) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["insert"].ValidateLoop("xiang.table.Insert") - - return gou.NewProcess(api.Process, process.Args[1:]...). - WithGlobal(process.Global). - WithSID(process.Sid).Run() -} - -// ProcessSetting xiang.table.Setting -// 读取数据表格配置信息, 请求成功返回配置信息对象 -func ProcessSetting(process *gou.Process) interface{} { - process.ValidateArgNums(2) - name := process.ArgsString(0) - field := process.ArgsString(1) - table := Select(name) - api := table.APIs["setting"] - - fields := strings.Split(field, ",") - if api.ProcessIs("xiang.table.Setting") { - - setting := maps.Map{ - "name": table.Name, - "title": table.Title, - "decription": table.Decription, - "columns": table.Columns, - "filters": table.Filters, - "list": table.List, - "edit": table.Edit, - "view": table.View, - "insert": table.Insert, - } - - if len(fields) == 1 && setting.Has(fields[0]) { - field := strings.TrimSpace(fields[0]) - return setting.Get(field) - } - - if len(fields) > 1 { - res := maps.Map{} - for _, field := range fields { - field = strings.TrimSpace(field) - if setting.Has(field) { - res.Set(field, setting.Get(field)) - } - } - return res - } - return setting - } - - return gou.NewProcess(api.Process, fields). - WithGlobal(process.Global). - WithSID(process.Sid).Run() -} - -// ProcessQuickSave xiang.table.QuickSave -// 保存多条记录。如数据记录中包含主键字段则更新,不包含主键字段则创建记录;返回创建或更新的记录主键值 -func ProcessQuickSave(process *gou.Process) interface{} { - process.ValidateArgNums(2) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["quicksave"].ValidateLoop("xiang.table.quicksave") - - args := []interface{}{} - payload := process.ArgsMap(1) - ids := []int{} - if payload.Has("delete") { - if v, ok := payload["delete"].([]int); ok { - ids = v - } else if vany, ok := payload["delete"].([]interface{}); ok { - for _, v := range vany { - ids = append(ids, any.Of(v).CInt()) - } - } - } - args = append(args, ids) - args = append(args, payload.Get("data")) - if payload.Has("query") { - args = append(args, payload.Get("query")) - } - - return gou.NewProcess(api.Process, args...). - WithGlobal(process.Global). - WithSID(process.Sid).Run() -} - -// ProcessSelect xiang.table.Select -// 单表数据查询,一般用于下拉菜单检索 -func ProcessSelect(process *gou.Process) interface{} { - process.ValidateArgNums(1) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["select"].ValidateLoop("xiang.table.select") - - // Before Hook - process.Args = table.Before(table.Hooks.BeforeSelect, process.Args, process.Sid) - - response := gou.NewProcess(api.Process, process.Args[1:]...). - WithGlobal(process.Global). - WithSID(process.Sid).Run() - - // After Hook - return table.After(table.Hooks.AfterSelect, response, process.Args[1:], process.Sid) -} - -// ProcessExport xiang.table.Export (:table, :queryParam, :chunkSize) -// Export query result to Excel -func ProcessExport(process *gou.Process) interface{} { - // var testDataM = map[string]string{} - - debug := os.Getenv("YAO_EXPORT_DEBUG") != "" - process.ValidateArgNums(1) - name := process.ArgsString(0) - table := Select(name) - api := table.APIs["search"].ValidateLoop("xiang.table.search") - - // Make DIR - hash := md5.Sum([]byte(time.Now().Format("20060102-15:04:05"))) - fingerprint := string(hex.EncodeToString(hash[:])) - fingerprint = strings.ToUpper(fingerprint) - dir := time.Now().Format("20060102") - ext := filepath.Ext("export.xlsx") - filename := filepath.Join(dir, fmt.Sprintf("%s%s", fingerprint, ext)) - xfs.Stor.MustMkdirAll(dir, os.ModePerm) - - page := 1 - for page > 0 { - - // Before Hook - process.Args = table.Before(table.Hooks.BeforeSearch, process.Args, process.Sid) - - // 参数表 - process.ValidateArgNums(3) - param := api.MergeDefaultQueryParam(process.ArgsQueryParams(1), 0, process.Sid) - pagesize := process.ArgsInt(2, api.DefaultInt(2)) - if process.NumOfArgs() == 4 { // for search hook - pagesize = process.ArgsInt(3, api.DefaultInt(1)) - } - - // fmt.Printf("\n%d/%d\n", page, pagesize) - - if debug { - bytes, _ := jsoniter.Marshal(param) - log.Info("[Export] %s %s %d %d Params: %s", api.Process, filename, page, pagesize, string(bytes)) - } - - // 查询数据 - response := gou.NewProcess(api.Process, param, page, pagesize). - WithGlobal(process.Global). - WithSID(process.Sid). - Run() - - // for i, v := range response.(maps.MapStrAny)["data"].([]maps.MapStrAny) { - // fmt.Println("i=", i, "id=", v["id"], v["status"]) - // } - - if debug { - bytes, _ := jsoniter.Marshal(response) - log.Info("[Export] %s %d %d Prepare: %s", filename, page, pagesize, string(bytes)) - } - - // After Hook - respAfterHook := table.After(table.Hooks.AfterSearch, response, []interface{}{param, page, pagesize}, process.Sid) - - if debug { - bytes, _ := jsoniter.Marshal(respAfterHook) - log.Info("[Export] %s %d %d Prepare After: %s", filename, page, pagesize, string(bytes)) - } - - // utils.Dump(respAfterHook) - // for _, v := range respAfterHook.(map[string]interface{})["data"].([]interface{}) { - // name := v.(map[string]interface{})["name"].(string) - // if _, has := testDataM[name]; has { - // fmt.Printf("**** %s Has\n\n", testDataM[name]) - // } else { - // testDataM[name] = fmt.Sprintf("%s %d/%d", name, page, pagesize) - // } - // fmt.Println("i=", i, "id=", v.(map[string]interface{})["id"], v.(map[string]interface{})["status"]) - // } - - // fmt.Println("---") - // fmt.Println("") - - res, ok := respAfterHook.(map[string]interface{}) - if !ok { - res, ok = respAfterHook.(maps.MapStrAny) - if !ok { - page = -1 - continue - } - } - - if _, ok := res["next"]; !ok { - page = -1 - continue - } - - err := table.Export(filename, res["data"], page, pagesize) - if err != nil { - log.Error("Export %s %s", table.Table, err.Error()) - } - - page = any.Of(res["next"]).CInt() - } - - return filename -} diff --git a/table/process_test.go b/table/process_test.go deleted file mode 100644 index 669d0d20..00000000 --- a/table/process_test.go +++ /dev/null @@ -1,547 +0,0 @@ -package table - -import ( - "fmt" - "path/filepath" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/any" - "github.com/yaoapp/kun/maps" - "github.com/yaoapp/xun/capsule" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/flow" - _ "github.com/yaoapp/yao/helper" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/query" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/script" - "github.com/yaoapp/yao/share" -) - -func init() { - runtime.Load(config.Conf) - share.DBConnect(config.Conf.DB) - model.Load(config.Conf) - share.Load(config.Conf) - query.Load(config.Conf) - script.Load(config.Conf) - flow.LoadFrom(filepath.Join(config.Conf.Root, "flows", "hooks"), "hooks.") - Load(config.Conf) -} -func TestTableProcessSearch(t *testing.T) { - - args := []interface{}{ - "service", - gou.QueryParam{ - Wheres: []gou.QueryWhere{ - {Column: "status", Value: "enabled"}, - }, - }, - 1, - 2, - &gin.Context{}, - } - response := gou.NewProcess("xiang.table.Search", args...).Run() - assert.NotNil(t, response) - res := any.Of(response).Map() - assert.True(t, res.Has("data")) - assert.True(t, res.Has("next")) - assert.True(t, res.Has("page")) - assert.True(t, res.Has("pagecnt")) - assert.True(t, res.Has("pagesize")) - assert.True(t, res.Has("prev")) - assert.True(t, res.Has("total")) - assert.Equal(t, 1, res.Get("page")) - assert.Equal(t, 2, res.Get("pagesize")) -} - -func TestTableProcessSearchWithHook(t *testing.T) { - - args := []interface{}{"hooks.search"} - response := gou.NewProcess("xiang.table.Search", args...).Run() - // utils.Dump(response) - - assert.NotNil(t, response) - res := any.Of(response).Map() - assert.True(t, res.Has("data")) - assert.True(t, res.Has("next")) - assert.True(t, res.Has("page")) - assert.True(t, res.Has("pagecnt")) - assert.True(t, res.Has("pagesize")) - assert.True(t, res.Has("prev")) - assert.True(t, res.Has("total")) - assert.Equal(t, 1, res.Get("page")) - assert.Equal(t, 2, res.Get("pagesize")) - assert.Equal(t, float64(100), res.Get("after")) -} - -func TestTableProcessFind(t *testing.T) { - args := []interface{}{ - "service", - 1, - &gin.Context{}, - } - response := gou.NewProcess("xiang.table.Find", args...).Run() - assert.NotNil(t, response) - res := any.Of(response).Map() - assert.Equal(t, 1, any.Of(res.Get("id")).CInt()) -} - -func TestTableProcessFindWithHook(t *testing.T) { - args := []interface{}{"hooks.find"} - response := gou.NewProcess("xiang.table.Find", args...).Run() - assert.NotNil(t, response) - res := any.Of(response).Map() - assert.Equal(t, 1, any.Of(res.Get("id")).CInt()) - assert.Equal(t, float64(100), res.Get("after")) -} - -func TestTableProcessSave(t *testing.T) { - args := []interface{}{ - "service", - map[string]interface{}{ - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - } - response := gou.NewProcess("xiang.table.Save", args...).Run() - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - - id := any.Of(response).CInt() - - // 清空数据 - capsule.Query().Table("service").Where("id", id).Delete() -} - -func TestTableProcessSaveWithHook(t *testing.T) { - args := []interface{}{"hooks.save"} - response := gou.NewProcess("xiang.table.Save", args...).Run() - assert.NotNil(t, response) - res := any.Of(response).Map() - assert.True(t, any.Of(res.Get("id")).IsInt()) - assert.Equal(t, float64(100), res.Get("after")) - - id := any.Of(res.Get("id")).CInt() - - // 清空数据 - capsule.Query().Table("service").Where("id", id).Delete() -} - -func TestTableProcessDelete(t *testing.T) { - args := []interface{}{ - "service", - map[string]interface{}{ - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - } - process := gou.NewProcess("xiang.table.Save", args...) - response := ProcessSave(process) - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - - id := any.Of(response).CInt() - args = []interface{}{ - "service", - id, - } - process = gou.NewProcess("xiang.table.Delete", args...) - response = ProcessDelete(process) - assert.Nil(t, response) - - // 清空数据 - capsule.Query().Table("service").Where("id", id).Delete() -} - -func TestTableProcessInsert(t *testing.T) { - args := []interface{}{ - "service", - []string{"name", "short_name", "kind_id", "manu_id", "price_options"}, - [][]interface{}{ - {"I腾讯云主机I1", "高性能云主机", 3, 1, []string{"按月订阅"}}, - {"I腾讯云主机I2", "高性能云主机", 3, 1, []string{"按月订阅"}}, - }, - } - process := gou.NewProcess("xiang.table.Insert", args...) - response := ProcessInsert(process) - assert.Nil(t, response) - - // 清空数据 - capsule.Query().Table("service").Where("name", "like", "I腾讯云主机I%").Delete() -} - -func TestTableProcessDeleteWhere(t *testing.T) { - args := []interface{}{ - "service", - map[string]interface{}{ - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - } - process := gou.NewProcess("xiang.table.Save", args...) - response := ProcessSave(process) - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - - id := any.Of(response).CInt() - args = []interface{}{ - "service", - gou.QueryParam{ - Wheres: []gou.QueryWhere{ - {Column: "id", Value: id}, - }, - }, - } - process = gou.NewProcess("xiang.table.DeleteWhere", args...) - response = ProcessDeleteWhere(process) - - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - assert.Equal(t, any.Of(response).CInt(), 1) - - // 清空数据 - capsule.Query().Table("service").Where("id", id).Delete() -} - -func TestTableProcessDeleteIn(t *testing.T) { - args := []interface{}{ - "service", - map[string]interface{}{ - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - } - process := gou.NewProcess("xiang.table.Save", args...) - id := ProcessSave(process) - assert.NotNil(t, id) - assert.True(t, any.Of(id).IsNumber()) - - // id := any.Of(response).CInt() - args = []interface{}{"service", id, "id"} - process = gou.NewProcess("xiang.table.DeleteIn", args...) - id = ProcessDeleteIn(process) - - assert.NotNil(t, id) - assert.True(t, any.Of(id).IsNumber()) - assert.Equal(t, id, 1) - - // 清空数据 - capsule.Query().Table("service").Where("id", id).Delete() -} - -func TestTableProcessUpdateWhere(t *testing.T) { - args := []interface{}{ - "service", - map[string]interface{}{ - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - } - process := gou.NewProcess("xiang.table.Save", args...) - response := ProcessSave(process) - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - - id := any.Of(response).CInt() - args = []interface{}{ - "service", - gou.QueryParam{ - Wheres: []gou.QueryWhere{ - {Column: "id", Value: id}, - }, - }, - map[string]interface{}{ - "name": "腾讯黑岩云主机UP", - }, - } - process = gou.NewProcess("xiang.table.UpdateWhere", args...) - response = ProcessUpdateWhere(process) - - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - assert.Equal(t, any.Of(response).CInt(), 1) - - // 清空数据 - capsule.Query().Table("service").Where("id", id).Delete() -} - -func TestTableProcessUpdateIn(t *testing.T) { - args := []interface{}{ - "service", - map[string]interface{}{ - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - } - process := gou.NewProcess("xiang.table.Save", args...) - response := ProcessSave(process) - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - - id := any.Of(response).CInt() - args = []interface{}{ - "service", - id, - "id", - map[string]interface{}{ - "name": "腾讯黑岩云主机UP", - }, - } - process = gou.NewProcess("xiang.table.UpdateIn", args...) - response = ProcessUpdateIn(process) - - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - assert.Equal(t, any.Of(response).CInt(), 1) - - // 清空数据 - capsule.Query().Table("service").Where("id", id).Delete() -} - -func TestTableProcessSetting(t *testing.T) { - args := []interface{}{"service", ""} - process := gou.NewProcess("xiang.table.Setting", args...) - response := ProcessSetting(process) - assert.NotNil(t, response) - res := any.Of(response).Map() - assert.Equal(t, res.Get("name"), "云服务库") - assert.True(t, res.Has("title")) - assert.True(t, res.Has("decription")) - assert.True(t, res.Has("columns")) - assert.True(t, res.Has("filters")) - assert.True(t, res.Has("list")) - assert.True(t, res.Has("edit")) - assert.True(t, res.Has("view")) - assert.True(t, res.Has("insert")) -} -func TestTableProcessSettingList(t *testing.T) { - args := []interface{}{"service", "list"} - process := gou.NewProcess("xiang.table.Setting", args...) - response := ProcessSetting(process) - assert.NotNil(t, response) - res := any.Of(response).Map() - assert.True(t, res.Has("actions")) - assert.True(t, res.Has("layout")) - assert.True(t, res.Has("primary")) -} - -func TestTableProcessSettingListEdit(t *testing.T) { - args := []interface{}{"service", "list, edit"} - process := gou.NewProcess("xiang.table.Setting", args...) - response := ProcessSetting(process) - assert.NotNil(t, response) - res := any.Of(response).MapStr().Dot() - assert.True(t, res.Has("list.actions")) - assert.True(t, res.Has("list.layout")) - assert.True(t, res.Has("list.primary")) - assert.True(t, res.Has("edit.actions")) - assert.True(t, res.Has("edit.layout")) - assert.True(t, res.Has("edit.primary")) -} - -func TestTableProcessQuickSave(t *testing.T) { - args := []interface{}{ - "service", - map[string]interface{}{ - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - } - process := gou.NewProcess("xiang.table.Save", args...) - response := ProcessSave(process) - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - - id := any.Of(response).CInt() - args = []interface{}{ - "service", - id, - } - - process = gou.NewProcess("xiang.table.QuickSave", - "service", - map[string]interface{}{ - "delete": []int{id}, - "data": []map[string]interface{}{ - { - "name": "腾讯黑岩云主机2", - "short_name": "高性能云主机2", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - }, - "query": map[string]interface{}{"manu_id": 1}, - }) - res := process.Run() - newID, ok := res.([]interface{}) - assert.True(t, ok) - assert.NotEqual(t, id, newID[0]) - - // 清空数据 - capsule.Query().Table("service").WhereIn("id", []interface{}{id, newID[0]}).Delete() -} - -func TestTableProcessSelect(t *testing.T) { - args := []interface{}{ - "service", - map[string]interface{}{ - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - } - process := gou.NewProcess("xiang.table.Save", args...) - response := ProcessSave(process) - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - - id := any.Of(response).CInt() - - args = []interface{}{"service", "腾讯", "name", "id"} - - data := gou.NewProcess("xiang.table.select", args...).Run().([]maps.MapStrAny) - assert.Greater(t, len(data), 1) - for _, row := range data { - assert.Contains(t, row.Get("name"), "腾讯") - } - - // 清空数据 - capsule.Query().Table("service").WhereIn("id", []int{id}).Delete() -} - -func TestTableProcessSelectWithHook(t *testing.T) { - args := []interface{}{ - "service", - map[string]interface{}{ - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": []string{"按月订阅"}, - }, - } - process := gou.NewProcess("xiang.table.Save", args...) - response := ProcessSave(process) - assert.NotNil(t, response) - assert.True(t, any.Of(response).IsInt()) - - id := any.Of(response).CInt() - - args = []interface{}{"hooks.select"} - - data := gou.NewProcess("xiang.table.select", args...).Run().([]maps.MapStrAny) - assert.Greater(t, len(data), 1) - for _, row := range data { - assert.Contains(t, row.Get("name"), "腾讯") - assert.Equal(t, float64(100), row.Get("after")) - } - - // 清空数据 - capsule.Query().Table("service").WhereIn("id", []int{id}).Delete() -} - -func TestTableProcessExport(t *testing.T) { - - args := []interface{}{ - "service", - gou.QueryParam{Wheres: []gou.QueryWhere{{Column: "status", Value: "enabled"}}}, - 2, - } - response := gou.NewProcess("xiang.table.Export", args...).Run() - assert.NotNil(t, response) - // fmt.Println(response) - // res := any.Of(response).Map() - // assert.True(t, res.Has("data")) - // assert.True(t, res.Has("next")) - // assert.True(t, res.Has("page")) - // assert.True(t, res.Has("pagecnt")) - // assert.True(t, res.Has("pagesize")) - // assert.True(t, res.Has("prev")) - // assert.True(t, res.Has("total")) - // assert.Equal(t, 1, res.Get("page")) - // assert.Equal(t, 2, res.Get("pagesize")) -} - -func TestTableProcessExportWithHook(t *testing.T) { - - args := []interface{}{"hooks.search"} - response := gou.NewProcess("xiang.table.Export", args...).Run() - - assert.NotNil(t, response) - // res := any.Of(response).Map() - // assert.True(t, res.Has("data")) - // assert.True(t, res.Has("next")) - // assert.True(t, res.Has("page")) - // assert.True(t, res.Has("pagecnt")) - // assert.True(t, res.Has("pagesize")) - // assert.True(t, res.Has("prev")) - // assert.True(t, res.Has("total")) - // assert.Equal(t, 1, res.Get("page")) - // assert.Equal(t, 2, res.Get("pagesize")) - // assert.Equal(t, float64(100), res.Get("after")) -} - -func TestTableProcessExportWithScriptHook(t *testing.T) { - // testData() - args := []interface{}{"hooks.search_script"} - response := gou.NewProcess("xiang.table.Export", args...).Run() - // utils.Dump(response) - - assert.NotNil(t, response) - // res := any.Of(response).Map() - // assert.True(t, res.Has("data")) - // assert.True(t, res.Has("next")) - // assert.True(t, res.Has("page")) - // assert.True(t, res.Has("pagecnt")) - // assert.True(t, res.Has("pagesize")) - // assert.True(t, res.Has("prev")) - // assert.True(t, res.Has("total")) - // assert.Equal(t, 1, res.Get("page")) - // assert.Equal(t, 2, res.Get("pagesize")) - // assert.Equal(t, float64(100), res.Get("after")) -} - -func testData() { - m := gou.Select("service") - data := [][]interface{}{} - for i := 0; i < 2000; i++ { - col := []interface{}{fmt.Sprintf("NAME-%d", i), 1, 1} - data = append(data, col) - } - - m.DestroyWhere(gou.QueryParam{ - Wheres: []gou.QueryWhere{ - {Column: "id", OP: "ge", Value: 0}, - }, - }) - err := m.Insert([]string{"name", "kind_id", "manu_id"}, data) - fmt.Println(err) -} diff --git a/table/table.go b/table/table.go deleted file mode 100644 index 452abd71..00000000 --- a/table/table.go +++ /dev/null @@ -1,486 +0,0 @@ -package table - -import ( - "errors" - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "github.com/gin-gonic/gin" - jsoniter "github.com/json-iterator/go" - "github.com/xuri/excelize/v2" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/helper" - "github.com/yaoapp/gou/lang" - "github.com/yaoapp/kun/any" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/kun/log" - "github.com/yaoapp/kun/maps" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/share" - "github.com/yaoapp/yao/xfs" -) - -// Tables 已载入模型 -var Tables = map[string]*Table{} - -// apiNames mapping -var apiNames = map[string]string{ - "/:name/search": "search", - "/:name/find/:id": "find", - "/:name/save": "save", - "/:name/delete/:id": "delete", - "/:name/insert": "insert", - "/:name/delete/in": "delete-in", - "/:name/delete/where": "delete-where", - "/:name/update/in": "update-in", - "/:name/update/where": "update-where", - "/:name/quicksave": "quicksave", - "/:name/select": "select", -} - -// Guard Table guard -func Guard(c *gin.Context) { - - if !strings.HasPrefix(c.FullPath(), "/api/xiang/table") { - c.Next() - return - } - - log.Trace("Table Guard FullPath: %s", c.FullPath()) - - routes := strings.Split(c.FullPath(), "/") - if len(routes) < 4 { - log.Trace("Table Guard Routes: %v", routes) - c.Next() - return - } - - path := "/" + strings.Join(routes[4:], "/") - apiName, has := apiNames[path] - if !has { - log.Trace("Table Guard API Name: %v", path) - c.Next() - return - } - - name, has := c.Params.Get("name") - if !has { - log.Trace("Table Guard Name: %v", c.Params) - c.Next() - return - } - - table, has := Tables[name] - if !has { - log.Trace("Table Guard Table: %s", name) - c.Next() - return - } - - api, has := table.APIs[apiName] - if !has { - log.Trace("Table Guard API: %s", apiName) - c.Next() - return - } - - log.Trace("Table Guard: %s %s %s %s", name, api.Guard, path, apiName) - - if api.Guard == "-" { - c.Next() - return - } - - if api.Guard == "" { - api.Guard = "bearer-jwt" - } - - guards := strings.Split(api.Guard, ",") - for _, guard := range guards { - guard = strings.TrimSpace(guard) - log.Trace("Table Guard: %s %s", name, guard) - if guard != "" { - if middleware, has := gou.HTTPGuards[guard]; has { - middleware(c) - continue - } - gou.ProcessGuard(guard)(c) - } - } - -} - -// Load 加载数据表格 -func Load(cfg config.Config) error { - if share.BUILDIN { - return LoadBuildIn("tables", "") - } - return LoadFrom(filepath.Join(cfg.Root, "tables"), "") -} - -// LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - messages := []string{} - err := share.Walk(dir, ".json", func(root, filename string) { - name := share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := LoadTable(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) - messages = append(messages, fmt.Sprintf("%s %s", name, err.Error())) - } - }) - - if len(messages) > 0 { - return fmt.Errorf("%s", strings.Join(messages, ";")) - } - - return err -} - -// LoadBuildIn 从制品中读取 -func LoadBuildIn(dir string, prefix string) error { - return nil -} - -// LoadTable 载入数据表格 -func LoadTable(source string, name string) (*Table, error) { - var input io.Reader = nil - if strings.HasPrefix(source, "file://") || strings.HasPrefix(source, "fs://") { - filename := strings.TrimPrefix(source, "file://") - filename = strings.TrimPrefix(filename, "fs://") - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - input = file - } else { - input = strings.NewReader(source) - } - - table := Table{ - Source: source, - Table: name, - } - err := helper.UnmarshalFile(input, &table) - if err != nil { - // exception.Err(err, 400).Ctx(maps.Map{"name": name}).Throw() - return &table, err - } - - table.loadColumns() - table.loadFilters() - table.loadAPIs() - - err = table.Validate() - if err != nil { - log.Error("[Table] %s is not valid: %s ", table.Table, err.Error()) - return nil, err - } - - Tables[name] = &table - - // Apply a language pack - if lang.Default != nil { - lang.Default.Apply(Tables[name]) - } - - return Tables[name], nil -} - -// Select 读取已加载表格配置 -func Select(name string) *Table { - tab, has := Tables[name] - if !has { - exception.New( - fmt.Sprintf("Table:%s; 尚未加载", name), - 400, - ).Throw() - } - return tab -} - -// Reload 更新数据表格配置 -func (table *Table) Reload() (*Table, error) { - new, err := LoadTable(table.Source, table.Name) - if err != nil { - return nil, err - } - *table = *new - return table, nil -} - -// loadAPIs 加载数据管理 API -func (table *Table) loadAPIs() { - if table.Bind.Model == "" { - return - } - defaults := getDefaultAPIs(table.Bind) - defaults["setting"] = apiDefaultSetting() - - for name := range table.APIs { - if _, has := defaults[name]; !has { - delete(table.APIs, name) - continue - } - - api := defaults[name] - api.Name = name - if table.APIs[name].Process != "" { - api.Process = table.APIs[name].Process - } - - api.Guard = "bearer-jwt" - if table.APIs[name].Guard != "" { - api.Guard = table.APIs[name].Guard - } - - if table.APIs[name].Default != nil { - // fmt.Printf("\n%s.APIs[%s].Default: entry\n", table.Table, name) - if len(table.APIs[name].Default) == len(api.Default) { - for i := range table.APIs[name].Default { - // fmt.Printf("%s.APIs[%s].Default[%d]:%v\n", table.Table, name, i, table.APIs[name].Default[i]) - if table.APIs[name].Default[i] != nil { - api.Default[i] = table.APIs[name].Default[i] - } - } - } - } - - defaults[name] = api - } - - table.APIs = defaults -} - -// getDefaultAPIs 读取数据模型绑定的APIs -func getDefaultAPIs(bind Bind) map[string]share.API { - name := bind.Model - model := gou.Select(name) - apis := map[string]share.API{ - "search": apiSearchDefault(model, bind.Withs), - "find": apiFindDefault(model, bind.Withs), - "save": apiDefault(model, "save", "Save"), - "delete": apiDefault(model, "delete", "Delete"), - "insert": apiDefault(model, "insert", "Insert"), - "delete-in": apiDefault(model, "delete-in", "DeleteWhere"), - "delete-where": apiDefaultWhere(model, bind.Withs, "delete-where", "DeleteWhere"), - "update-in": apiDefault(model, "update-in", "UpdateWhere"), - "update-where": apiDefaultWhere(model, bind.Withs, "update-where", "UpdateWhere"), - "quicksave": apiDefault(model, "quicksave", "EachSaveAfterDelete"), // 批量保存 - "select": apiDefault(model, "select", "SelectOption"), // 选择 - } - - return apis -} - -// Before 运行 Before hook -func (table *Table) Before(process string, processArgs []interface{}, sid string) []interface{} { - if process == "" { - return processArgs - } - args := []interface{}{} - res := []interface{}{} - if len(processArgs) > 0 { - args = processArgs[1:] - res = append(res, processArgs[0]) - } - - response, err := gou.NewProcess(process, args...).WithSID(sid).Exec() - if err != nil { - log.With(log.F{"process": process, "args": args}).Warn("Hook执行失败: %s", err.Error()) - return processArgs - } - - if fixedArgs, ok := response.([]interface{}); ok { - res = append(res, fixedArgs...) - return res - } - - log.With(log.F{"process": process, "args": args}).Warn("Hook执行失败: 无效的处理器") - return processArgs -} - -// After 运行 After hook -func (table *Table) After(process string, data interface{}, args []interface{}, sid string) interface{} { - if process == "" { - return data - } - args = append([]interface{}{data}, args...) - response, err := gou.NewProcess(process, args...).WithSID(sid).Exec() - if err != nil { - log.With(log.F{"process": process, "args": args}).Warn("Hook执行失败: %s", err.Error()) - return data - } - return response -} - -// loadFilters 加载查询过滤器 -func (table *Table) loadFilters() { - if table.Bind.Model == "" { - return - } - defaults := share.GetDefaultFilters(table.Bind.Model) - for name, filter := range table.Filters { - defaults[name] = filter - } - table.Filters = defaults -} - -// loadColumns 加载字段呈现方式 -func (table *Table) loadColumns() { - if table.Bind.Model == "" { - return - } - defaults := share.GetDefaultColumns(table.Bind.Model) - for name, column := range table.Columns { - defaults[name] = column - } - table.Columns = defaults -} - -// Export Export query result to Excel -func (table *Table) Export(filename string, data interface{}, page int, chunkSize int) error { - - debug := os.Getenv("YAO_EXPORT_DEBUG") != "" - - if debug { - bytes, _ := jsoniter.Marshal(data) - log.Info("[Export] %s %d %d Before: %s", filename, page, chunkSize, string(bytes)) - } - - rows := []maps.MapStr{} - if values, ok := data.([]maps.MapStrAny); ok { - for _, row := range values { - rows = append(rows, row.Dot()) - } - } else if values, ok := data.([]map[string]interface{}); ok { - for _, row := range values { - rows = append(rows, maps.Of(row).Dot()) - } - } else if values, ok := data.([]interface{}); ok { - for _, row := range values { - rows = append(rows, any.Of(row).MapStr().Dot()) - } - } - - if debug { - bytes, _ := jsoniter.Marshal(rows) - log.Info("[Export] %s %d %d After: %s", filename, page, chunkSize, string(bytes)) - } - - columns, err := table.exportSetting() - if err != nil { - return err - } - - if len(columns) == 0 { - return fmt.Errorf("the table does not support export") - } - - filename = filepath.Join(xfs.Stor.Root, filename) - if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) { - f := excelize.NewFile() - index := f.GetActiveSheetIndex() - name := f.GetSheetName(index) - f.SetSheetName(name, table.Name) - for i, column := range columns { - axis, err := excelize.CoordinatesToCellName(i+1, 1) - if err != nil { - return err - } - f.SetCellValue(table.Name, axis, column["name"]) - } - if err := f.SaveAs(filename); err != nil { - fmt.Println(err) - return err - } - } - - f, err := excelize.OpenFile(filename) - if err != nil { - return err - } - - defer f.Close() - offset := (page-1)*chunkSize + 2 - for line, row := range rows { - for i, column := range columns { - v := row.Get(column["field"]) - if v != nil { - axis, err := excelize.CoordinatesToCellName(i+1, line+offset) - if err != nil { - return err - } - f.SetCellValue(table.Name, axis, v) - } - } - // fmt.Println("--", line, page, offset, "--") - } - - return f.Save() -} - -func (table *Table) exportSetting() ([]map[string]string, error) { - // Validate params - if table.List.Layout == nil { - return nil, fmt.Errorf("the table layout does not found") - } - - columns, has := table.List.Layout["columns"] - if !has { - return nil, fmt.Errorf("the columns table layout does not found") - } - - fields, ok := columns.([]interface{}) - if !ok { - return nil, fmt.Errorf("Table Layout columns format error") - } - - setting := []map[string]string{} - for _, field := range fields { - f, ok := field.(map[string]interface{}) - if !ok { - continue - } - - n, has := f["name"] - if !has { - continue - } - - name, ok := n.(string) - if !ok { - continue - } - - column, has := table.Columns[name] - if !has { - continue - } - - field := column.Export - if field == "" { - if value, has := column.View.Props["value"]; has { - if valueStr, ok := value.(string); ok { - field = strings.TrimPrefix(valueStr, ":") - } - } - } - - if field != "" && name != "" { - setting = append(setting, map[string]string{"name": name, "field": field}) - } - } - - return setting, nil -} diff --git a/table/table_test.go b/table/table_test.go deleted file mode 100644 index 566ceb59..00000000 --- a/table/table_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package table - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/share" -) - -func TestLoad(t *testing.T) { - - share.DBConnect(config.Conf.DB) - share.Load(config.Conf) - model.Load(config.Conf) - - Tables = make(map[string]*Table) - Load(config.Conf) - LoadFrom("not a path", "404.") - check(t) - - table, has := Tables["service"] - assert.Equal(t, table.Table, "service") - assert.True(t, has) - - _, has = table.Columns["id"] - assert.True(t, has) - - price, has := table.Columns["计费方式"] - assert.True(t, has) - if has { - assert.True(t, price.Edit.Props["multiple"].(bool)) - } - - _, has = table.Filters["id"] - assert.True(t, has) - - keywords, has := table.Filters["关键词"] - assert.True(t, has) - if has { - assert.True(t, keywords.Bind == "where.name.like") - } -} - -func check(t *testing.T) { - keys := []string{} - for key := range Tables { - keys = append(keys, key) - } - assert.Equal(t, 11, len(keys)) - - // demo := Select("demo") - // utils.Dump(demo) - - // assert.NotNil(t, demo.Columns["類型"]) - // assert.NotNil(t, demo.Columns["類型"].Edit.Props["options"]) - - // options := demo.Columns["類型"].Edit.Props["options"].([]interface{}) - // opt1 := options[0].(map[string]interface{}) - // assert.Equal(t, "貓", opt1["label"]) - -} diff --git a/table/types.go b/table/types.go deleted file mode 100644 index 1df50c15..00000000 --- a/table/types.go +++ /dev/null @@ -1,58 +0,0 @@ -package table - -import ( - "github.com/yaoapp/gou" - "github.com/yaoapp/yao/share" -) - -// Table 数据表格配置结构 -type Table struct { - Table string `json:"-"` - Source string `json:"-"` - Guard string `json:"guard,omitempty"` - Name string `json:"name"` - Version string `json:"version"` - Title string `json:"title,omitempty"` - Decription string `json:"decription,omitempty"` - Bind Bind `json:"bind,omitempty"` - Hooks Hooks `json:"hooks,omitempty"` - APIs map[string]share.API `json:"apis,omitempty"` - Columns map[string]share.Column `json:"columns,omitempty"` - Filters map[string]share.Filter `json:"filters,omitempty"` - List share.Page `json:"list,omitempty"` - Edit share.Page `json:"edit,omitempty"` - View share.Page `json:"view,omitempty"` - Insert share.Page `json:"insert,omitempty"` -} - -// Bind 绑定数据模型 -type Bind struct { - Model string `json:"model"` - Withs map[string]gou.With `json:"withs,omitempty"` -} - -// Hooks 表格数据模型 -type Hooks struct { - BeforeFind string `json:"before:find,omitempty"` - AfterFind string `json:"after:find,omitempty"` - BeforeSearch string `json:"before:search,omitempty"` - AfterSearch string `json:"after:search,omitempty"` - BeforeSave string `json:"before:save,omitempty"` - AfterSave string `json:"after:save,omitempty"` - BeforeDelete string `json:"before:delete,omitempty"` - AfterDelete string `json:"after:delete,omitempty"` - BeforeInsert string `json:"before:insert,omitempty"` - AfterInsert string `json:"after:insert,omitempty"` - BeforeDeleteIn string `json:"before:delete-in,omitempty"` - AfterDeleteIn string `json:"after:delete-in,omitempty"` - BeforeDeleteWhere string `json:"before:delete-where,omitempty"` - AfterDeleteWhere string `json:"after:delete-where,omitempty"` - BeforeUpdateIn string `json:"before:update-in,omitempty"` - AfterUpdateIn string `json:"after:update-in,omitempty"` - BeforeUpdateWhere string `json:"before:update-where,omitempty"` - AfterUpdateWhere string `json:"after:update-where,omitempty"` - BeforeQuicksave string `json:"before:quicksave,omitempty"` - AfterQuicksave string `json:"after:quicksave,omitempty"` - BeforeSelect string `json:"before:select,omitempty"` - AfterSelect string `json:"after:select,omitempty"` -} diff --git a/table/validate.go b/table/validate.go deleted file mode 100644 index 6f5be474..00000000 --- a/table/validate.go +++ /dev/null @@ -1,132 +0,0 @@ -package table - -import "fmt" - -// Validate 校验表格格式 -func (table Table) Validate() error { - err := table.validateList() - if err != nil { - return err - } - - err = table.validateEdit() - if err != nil { - return err - } - - return nil -} - -func (table Table) validateEdit() error { - if table.Edit.Layout == nil { - return nil - } - - fieldset, ok := table.Edit.Layout["fieldset"].([]interface{}) - if !ok { - return fmt.Errorf("edit.layout.columns is required") - } - - for idx, set := range fieldset { - set, ok := set.(map[string]interface{}) - if !ok { - return fmt.Errorf("edit.layout.fieldset.%d format error", idx) - } - - columns, ok := set["columns"].([]interface{}) - if !ok { - return fmt.Errorf("edit.layout.fieldset.%d.columns format error", idx) - } - - for cidx, column := range columns { - col, ismap := column.(map[string]interface{}) - name, isstr := column.(string) - if !ismap && !isstr { - return fmt.Errorf("edit.layout.fieldset.%d.columns.%d format error", idx, cidx) - } - - if ismap { - v, has := col["name"] - if !has { - return fmt.Errorf("edit.layout.fieldset.%d.columns.%d.name is required", idx, cidx) - } - namestr, ok := v.(string) - if !ok { - return fmt.Errorf("edit.layout.fieldset.%d.columns.%d.name format error", idx, cidx) - } - name = namestr - } - - if _, has := table.Columns[name]; !has { - return fmt.Errorf("edit.layout.fieldset.%d.columns.%d.name %s is not found in columns", idx, cidx, name) - } - } - - } - - return nil -} - -func (table Table) validateList() error { - - if table.List.Layout == nil { - return nil - } - - // table.List.Layout - columns, ok := table.List.Layout["columns"].([]interface{}) - if !ok { - return fmt.Errorf("list.layout.columns is required") - } - - for idx, column := range columns { - - col, ismap := column.(map[string]interface{}) - name, isstr := column.(string) - if !ismap && !isstr { - return fmt.Errorf("list.layout.columns.%d format error", idx) - } - - if ismap { - v, has := col["name"] - if !has { - return fmt.Errorf("list.layout.columns.%d.name is required", idx) - } - namestr, ok := v.(string) - if !ok { - return fmt.Errorf("list.layout.columns.%d.name format error", idx) - } - name = namestr - } - - if _, has := table.Columns[name]; !has { - return fmt.Errorf("list.layout.columns.%d.name %s is not found in columns", idx, name) - } - } - - filters, ok := table.List.Layout["filters"].([]interface{}) - if ok { - for idx, filter := range filters { - fli, ok := filter.(map[string]interface{}) - if !ok { - return fmt.Errorf("list.layout.filters.%d format error", idx) - } - - name, has := fli["name"] - if !has { - return fmt.Errorf("list.layout.filters.%d.name is required", idx) - } - - namestr, ok := name.(string) - if !ok { - return fmt.Errorf("list.layout.filters.%d.name format error", idx) - } - - if _, has := table.Filters[namestr]; !has { - return fmt.Errorf("list.layout.filters.%d.name %s is not found in filters", idx, namestr) - } - } - } - - return nil -} diff --git a/task/task.go b/task/task.go index 07d36e7e..53c12fae 100644 --- a/task/task.go +++ b/task/task.go @@ -1,36 +1,20 @@ package task import ( - "fmt" - "path/filepath" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/task" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) // Load load task func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "tasks") - return LoadFrom(root, "") -} - -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - err := share.Walk(dir, ".json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadTask(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + exts := []string{"*.yao", "*.json", "*.jsonc"} + return application.App.Walk("tasks", func(root, file string, isdir bool) error { + if isdir { + return nil } - }) - - return err + _, err := task.Load(file, share.ID(root, file)) + return err + }, exts...) } diff --git a/task/task_test.go b/task/task_test.go index 71f178a4..1cb0cc16 100644 --- a/task/task_test.go +++ b/task/task_test.go @@ -6,14 +6,21 @@ import ( "github.com/stretchr/testify/assert" "github.com/yaoapp/gou/task" "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } func check(t *testing.T) { - assert.Equal(t, 1, len(task.Tasks)) + ids := map[string]bool{} + for id := range task.Tasks { + ids[id] = true + } + assert.True(t, ids["mail"]) } diff --git a/widgets/test/request.go b/test/request.go similarity index 91% rename from widgets/test/request.go rename to test/request.go index fe413358..a1494ddc 100644 --- a/widgets/test/request.go +++ b/test/request.go @@ -9,7 +9,8 @@ import ( "time" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" "github.com/yaoapp/yao/helper" ) @@ -189,8 +190,8 @@ func (p *Response) To(v interface{}) error { // AutoLogin auto login func AutoLogin(id int) (map[string]interface{}, error) { - user := gou.Select("xiang.user") - row, err := user.Find(id, gou.QueryParam{Select: []interface{}{"id", "name", "type", "email", "mobile", "extra", "status"}}) + user := model.Select("admin.user") + row, err := user.Find(id, model.QueryParam(model.QueryParam{Select: []interface{}{"id", "name", "type", "email", "mobile", "extra", "status"}})) if err != nil { return nil, err } @@ -199,13 +200,13 @@ func AutoLogin(id int) (map[string]interface{}, error) { token := helper.JwtMake(id, map[string]interface{}{}, map[string]interface{}{ "expires_at": expiresAt, "sid": sid, - "issuer": "xiang", + "issuer": "admin", }) session.Global().Expire(time.Duration(token.ExpiresAt)*time.Second).ID(sid).Set("user_id", id) session.Global().ID(sid).Set("user", row) - session.Global().ID(sid).Set("issuer", "xiang") + session.Global().ID(sid).Set("issuer", "admin") - p, err := gou.ProcessOf("yao.app.menu") + p, err := process.Of("yao.app.menu") if err != nil { return nil, err } diff --git a/test/utils.go b/test/utils.go new file mode 100644 index 00000000..3c3430cf --- /dev/null +++ b/test/utils.go @@ -0,0 +1,217 @@ +package test + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/gin-gonic/gin" + "github.com/yaoapp/gou/api" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/query" + "github.com/yaoapp/gou/query/gou" + v8 "github.com/yaoapp/gou/runtime/v8" + "github.com/yaoapp/gou/server/http" + "github.com/yaoapp/kun/exception" + "github.com/yaoapp/xun/capsule" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/fs" + "github.com/yaoapp/yao/helper" + "github.com/yaoapp/yao/runtime" + "github.com/yaoapp/yao/share" +) + +var testServer *http.Server = nil + +// Prepare test environment +func Prepare(t *testing.T, cfg config.Config) { + root := os.Getenv("YAO_TEST_APPLICATION") + var app application.Application + var err error + + if root == "bin:application.pkg" { + app, err = application.OpenFromBin(root, &share.Pack{}) // Load app from Bin + if err != nil { + t.Fatal(err) + } + application.Load(app) + return + } + + app, err = application.OpenFromDisk(root) // Load app from Disk + if err != nil { + t.Fatal(err) + } + application.Load(app) + + if cfg.DataRoot == "" { + cfg.DataRoot = filepath.Join(root, "data") + } + + dbconnect(t, cfg) + load(t, cfg) + startRuntime(t, cfg) +} + +// Clean the test environment +func Clean() { + dbclose() + runtime.Stop() +} + +// Start the test server +func Start(t *testing.T, guards map[string]gin.HandlerFunc, cfg config.Config) { + + var err error + option := http.Option{Port: 0, Root: "/", Timeout: 2 * time.Second} + gin.SetMode(gin.ReleaseMode) + + router := gin.New() + api.SetGuards(guards) + api.SetRoutes(router, "api") + + testServer = http.New(router, option) + go func() { err = testServer.Start() }() + + <-testServer.Event() + if err != nil { + t.Fatal(err) + } +} + +// Stop the test server +func Stop() { + if testServer != nil { + testServer.Stop() + <-testServer.Event() + } + + dbclose() + runtime.Stop() +} + +// Port Get the test server port +func Port(t *testing.T) int { + if testServer == nil { + t.Fatal(fmt.Errorf("server not started")) + } + port, err := testServer.Port() + if err != nil { + t.Fatal(err) + } + return port +} + +func dbclose() { + if capsule.Global != nil { + capsule.Global.Connections.Range(func(key, value any) bool { + if conn, ok := value.(*capsule.Connection); ok { + conn.Close() + } + return true + }) + } +} + +func dbconnect(t *testing.T, cfg config.Config) { + + // connect db + switch cfg.DB.Driver { + case "sqlite3": + capsule.AddConn("primary", "sqlite3", cfg.DB.Primary[0]).SetAsGlobal() + break + default: + capsule.AddConn("primary", "mysql", cfg.DB.Primary[0]).SetAsGlobal() + break + } + +} + +func startRuntime(t *testing.T, cfg config.Config) { + err := runtime.Start(cfg) + if err != nil { + t.Fatal(err) + } +} + +func load(t *testing.T, cfg config.Config) { + loadFS(t, cfg) + loadScript(t, cfg) + loadModel(t, cfg) + loadQuery(t, cfg) +} + +func loadFS(t *testing.T, cfg config.Config) { + err := fs.Load(cfg) + if err != nil { + t.Fatal(err) + } +} + +func loadScript(t *testing.T, cfg config.Config) { + exts := []string{"*.js"} + err := application.App.Walk("scripts", func(root, file string, isdir bool) error { + if isdir { + return nil + } + _, err := v8.Load(file, share.ID(root, file)) + return err + }, exts...) + + if err != nil { + t.Fatal(err) + } +} + +func loadModel(t *testing.T, cfg config.Config) { + model.WithCrypt([]byte(fmt.Sprintf(`{"key":"%s"}`, cfg.DB.AESKey)), "AES") + model.WithCrypt([]byte(`{}`), "PASSWORD") + + exts := []string{"*.mod.yao", "*.mod.json", "*.mod.jsonc"} + err := application.App.Walk("models", func(root, file string, isdir bool) error { + if isdir { + return nil + } + _, err := model.Load(file, share.ID(root, file)) + return err + }, exts...) + + if err != nil { + t.Fatal(err) + } +} + +func loadQuery(t *testing.T, cfg config.Config) { + + // query engine + query.Register("query-test", &gou.Query{ + Query: capsule.Query(), + GetTableName: func(s string) string { + if mod, has := model.Models[s]; has { + return mod.MetaData.Table.Name + } + exception.New("[query] %s not found", 404, s).Throw() + return s + }, + AESKey: cfg.DB.AESKey, + }) +} + +// GuardBearerJWT test guard +func GuardBearerJWT(c *gin.Context) { + tokenString := c.Request.Header.Get("Authorization") + tokenString = strings.TrimSpace(strings.TrimPrefix(tokenString, "Bearer ")) + + if tokenString == "" { + c.JSON(403, gin.H{"code": 403, "message": "No permission"}) + c.Abort() + return + } + + claims := helper.JwtValidate(tokenString) + c.Set("__sid", claims.SID) +} diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 059a177f..00000000 --- a/tests/README.md +++ /dev/null @@ -1 +0,0 @@ -# 测试用应用 diff --git a/tests/apis/benchmark.http.json b/tests/apis/benchmark.http.json deleted file mode 100644 index 6f91acce..00000000 --- a/tests/apis/benchmark.http.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "用户接口", - "version": "1.0.0", - "description": "用户API", - "group": "benchmark", - "guard": "bearer-jwt", - "paths": [ - { - "path": "/ping", - "method": "GET", - "guard": "-", - "process": "xiang.helper.Return", - "in": [":query"], - "out": { - "status": 200, - "type": "application/json" - } - }, - { - "path": "/flows/pure", - "method": "GET", - "guard": "-", - "process": "flows.benchmark.pure", - "in": [], - "out": { - "status": 200, - "type": "application/json" - } - }, - { - "path": "/flows/script", - "method": "GET", - "guard": "-", - "process": "flows.benchmark.script", - "in": [], - "out": { - "status": 200, - "type": "application/json" - } - } - ] -} diff --git a/tests/apis/chat.ws.json b/tests/apis/chat.ws.json deleted file mode 100644 index eb94daed..00000000 --- a/tests/apis/chat.ws.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "A Chat WebSocket server", - "description": "A Chat WebSocket serverr", - "version": "0.9.2", - "protocols": ["yao-chat-01"], - "guard": "bearer-jwt", - "buffer": { "read": 1024, "write": 1024 }, - "limit": { "read-wait": 10, "pong-wait": 20, "max-message": 1024 }, - "timeout": 5, - "process": "flows.websocket.chat" -} diff --git a/tests/apis/demo.http.json b/tests/apis/demo.http.json deleted file mode 100644 index b04f3239..00000000 --- a/tests/apis/demo.http.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "Demo", - "version": "1.0.0", - "description": "demo", - "group": "demo", - "guard": "-", - "paths": [ - { - "path": "/user", - "method": "GET", - "process": "flows.demo.user", - "in": ["$query.id"], - "out": { - "status": 200, - "type": "application/json" - } - }, - { - "path": "/admin", - "method": "GET", - "process": "flows.demo.admin", - "in": ["$query.id"], - "out": { - "status": 200, - "type": "application/json" - } - }, - { - "path": "/token", - "method": "GET", - "process": "flows.demo.token", - "in": ["$query.id"], - "out": { - "status": 200, - "type": "application/json" - } - } - ] -} diff --git a/tests/apis/group/team.http.json b/tests/apis/group/team.http.json deleted file mode 100644 index ee8cc4b5..00000000 --- a/tests/apis/group/team.http.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "厂商接口", - "version": "1.0.0", - "description": "厂商API", - "group": "manu", - "guard": "bearer-jwt", - "paths": [] -} diff --git a/tests/apis/manu.http.json b/tests/apis/manu.http.json deleted file mode 100644 index ee8cc4b5..00000000 --- a/tests/apis/manu.http.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "厂商接口", - "version": "1.0.0", - "description": "厂商API", - "group": "manu", - "guard": "bearer-jwt", - "paths": [] -} diff --git a/tests/apis/user.http.json b/tests/apis/user.http.json deleted file mode 100644 index 25350168..00000000 --- a/tests/apis/user.http.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "用户接口", - "version": "1.0.0", - "description": "用户API", - "group": "user", - "guard": "bearer-jwt", - "paths": [ - { - "path": "/login", - "method": "POST", - "guard": "-", - "process": "plugins.user.Login", - "in": ["$payload.mobile", "$payload.password", "$payload.captcha"], - "out": { - "status": 200, - "type": "application/json" - } - }, - { - "path": "/ping", - "method": "GET", - "guard": "-", - "process": "plugins.user.Login", - "in": ["$payload.mobile", "$payload.password", "$payload.captcha"], - "out": { - "status": 200, - "type": "application/json" - } - }, - { - "path": "/find/:id", - "method": "GET", - "guard": "-", - "process": "models.user.Find", - "in": ["$param.id", ":params"], - "out": { - "status": 200, - "type": "application/json" - } - }, - { - "path": "/search", - "method": "GET", - "guard": "-", - "process": "models.user.Paginate", - "in": [":params", "$query.page", "$query.pagesize"], - "out": { - "status": 200, - "type": "application/json" - } - }, - { - "path": "/info/:id", - "method": "GET", - "process": "models.user.Find", - "in": ["$param.id", ":params"], - "out": { - "status": 200, - "type": "application/json" - } - } - ] -} diff --git a/tests/app.json b/tests/app.json deleted file mode 100644 index 56437a1e..00000000 --- a/tests/app.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "象传应用", - "short": "象传", - "description": "象传应用后台", - "lang": "en-US", - "option": { - "index": "/table/service" - }, - "storage": { - "default": "oss", - "oss": { - "comment": "https://www.alibabacloud.com/help/zh/doc-detail/100624.htm", - "endpoint": "oss-cn-hangzhou.aliyuncs.com", - "id": "LTAI5tHXfWAMpcsobfqfeXsm", - "secret": "4NeGNw97nFrfmgBWZTlFE0XtYQdMQO", - "roleArn": "acs:ram::31524094:role/ramosstest", - "sessionName": "SessionTest" - }, - "cos": {}, - "s3": {} - } -} diff --git a/tests/backup/paginate_test.sql b/tests/backup/paginate_test.sql deleted file mode 100644 index d106f007..00000000 --- a/tests/backup/paginate_test.sql +++ /dev/null @@ -1,9 +0,0 @@ - -## ERROR -select `service`.`id` as `service_id`, `service`.`short_name` as `service_short_name`, `service`.`name` as `service_name`, `service`.`city` as `service_city`, `service`.`kind_id` as `service_kind_id`, `service`.`manu_id` as `service_manu_id`, `service`.`registed_at` as `service_registed_at`, `service`.`link` as `service_link`, `service`.`contact_name` as `service_contact_name`, `service`.`contact_mobile` as `service_contact_mobile`, `service`.`fields` as `service_fields`, `service`.`price_options` as `service_price_options`, `service`.`industries` as `service_industries`, `service`.`rank` as `service_rank`, `service`.`status` as `service_status`, `service`.`deleted_at` as `service_deleted_at`, `service`.`created_at` as `service_created_at`, `service`.`updated_at` as `service_updated_at` from `service` as `service` where `service`.`deleted_at` is null order by `service`.`created_at` limit 1 offset 907 -select `service`.`id` as `service_id`, `service`.`short_name` as `service_short_name`, `service`.`name` as `service_name`, `service`.`city` as `service_city`, `service`.`kind_id` as `service_kind_id`, `service`.`manu_id` as `service_manu_id`, `service`.`registed_at` as `service_registed_at`, `service`.`link` as `service_link`, `service`.`contact_name` as `service_contact_name`, `service`.`contact_mobile` as `service_contact_mobile`, `service`.`fields` as `service_fields`, `service`.`price_options` as `service_price_options`, `service`.`industries` as `service_industries`, `service`.`rank` as `service_rank`, `service`.`status` as `service_status`, `service`.`deleted_at` as `service_deleted_at`, `service`.`created_at` as `service_created_at`, `service`.`updated_at` as `service_updated_at` from `service` as `service` where `service`.`deleted_at` is null order by `service`.`created_at` limit 1 offset 1740 - - -## CORRECT -select `service`.`id` as `service_id`, `service`.`short_name` as `service_short_name`, `service`.`name` as `service_name`, `service`.`city` as `service_city`, `service`.`kind_id` as `service_kind_id`, `service`.`manu_id` as `service_manu_id`, `service`.`registed_at` as `service_registed_at`, `service`.`link` as `service_link`, `service`.`contact_name` as `service_contact_name`, `service`.`contact_mobile` as `service_contact_mobile`, `service`.`fields` as `service_fields`, `service`.`price_options` as `service_price_options`, `service`.`industries` as `service_industries`, `service`.`rank` as `service_rank`, `service`.`status` as `service_status`, `service`.`deleted_at` as `service_deleted_at`, `service`.`created_at` as `service_created_at`, `service`.`updated_at` as `service_updated_at` from `service` as `service` where `service`.`deleted_at` is null order by `service`.`created_at`, `service`.`id` limit 1 offset 907 -select `service`.`id` as `service_id`, `service`.`short_name` as `service_short_name`, `service`.`name` as `service_name`, `service`.`city` as `service_city`, `service`.`kind_id` as `service_kind_id`, `service`.`manu_id` as `service_manu_id`, `service`.`registed_at` as `service_registed_at`, `service`.`link` as `service_link`, `service`.`contact_name` as `service_contact_name`, `service`.`contact_mobile` as `service_contact_mobile`, `service`.`fields` as `service_fields`, `service`.`price_options` as `service_price_options`, `service`.`industries` as `service_industries`, `service`.`rank` as `service_rank`, `service`.`status` as `service_status`, `service`.`deleted_at` as `service_deleted_at`, `service`.`created_at` as `service_created_at`, `service`.`updated_at` as `service_updated_at` from `service` as `service` where `service`.`deleted_at` is null order by `service`.`created_at`, `service`.`id` limit 1 offset 1740 \ No newline at end of file diff --git a/tests/certs/cert.pem b/tests/certs/cert.pem deleted file mode 100644 index 18ae9586..00000000 --- a/tests/certs/cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDCjCCAfICCQDIMbr1hVnv2TANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJD -TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHSGFpZGlhbjEUMBIGA1UECgwL -WWFvQXBwcy5jb20wHhcNMjIwNzE2MDY0ODI5WhcNMzIwNzEzMDY0ODI5WjBHMQsw -CQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHSGFpZGlhbjEU -MBIGA1UECgwLWWFvQXBwcy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQCqLwF385x1w0umsYpUb6LfSl/jUtny2T4hrVAAfzKcPDYyKT/gwJKJAm5v -PPLhsqjJLD27eRVNfzqCYOOJPIK5D+YAJigEb7/n+s96P7m0bzLZla3w/2GZRqZb -8m48xOQIHm/jVDC45Q3q/zdFUwgBtrOJxBBSs75eLRhj04CCPjdqoZcr0RdoyzSG -KvGiNGMNjfWEsdTnGYOcGNUa0UOsIpk+gS+QXD6EMlilM8GWQA+cDw6Owk5ydD0b -mPAiDD+mQn4wqCqruRuDziFe/cqWlY7etETT5Ox1D8eiLTTGRnj8p0TEro/eZ4Pc -JsVhH/t9yOvLgze3pgaPbbY6enTTAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAp9 -7QuYnZF6YKp4zZp4VQkikAyYMvNnvQC9Fgah0RaX/ZY8hG7QQqiLDjcMPVaR13jT -oVumWJ225xO3HCi38AGHsJW8Uu7ZR4LZzI59MHenv10EIiTpc3HYAv+bH5bVtmgX -R7CEUMhNiJBKr6TwxiKBWkfjvO7sJrX4ZxKe6SKcOoKUeIMRyZCqstlSMlfu1xwr -DX83fyOyOaDJkUPIJNhVIbwqQxV0X47Bxdj9IdGF26ut4Dh0YNVxtArVr5va1VQo -Rsa9r63lhmnqbjeFcbY8TlO1Pm+hWgOmgcrjGNdP8DYi3KFMFiwY+VSUtwPnSZQS -OANAV1mlEv6+7UMZHRs= ------END CERTIFICATE----- diff --git a/tests/certs/private.pem b/tests/certs/private.pem deleted file mode 100644 index ae8df512..00000000 --- a/tests/certs/private.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCqLwF385x1w0um -sYpUb6LfSl/jUtny2T4hrVAAfzKcPDYyKT/gwJKJAm5vPPLhsqjJLD27eRVNfzqC -YOOJPIK5D+YAJigEb7/n+s96P7m0bzLZla3w/2GZRqZb8m48xOQIHm/jVDC45Q3q -/zdFUwgBtrOJxBBSs75eLRhj04CCPjdqoZcr0RdoyzSGKvGiNGMNjfWEsdTnGYOc -GNUa0UOsIpk+gS+QXD6EMlilM8GWQA+cDw6Owk5ydD0bmPAiDD+mQn4wqCqruRuD -ziFe/cqWlY7etETT5Ox1D8eiLTTGRnj8p0TEro/eZ4PcJsVhH/t9yOvLgze3pgaP -bbY6enTTAgMBAAECggEAGgj65ojkRLfN2QCMMwKpHhPYu4QvR5Goiapj9M+AxJoJ -iH77AL01LbSrhGUEmzajz7Lu9sE9Ww+9ubhs1s/lB77A5wDvWk5mQ20BVKhvFoV0 -eAkIBThsi/Bc/vAFoVJVuFA/ub1lfsO771l/8SEUtn5m1QqjnKNW2j133725iTMP -iwRlcEOEfFGa5bVQ/eEnQ5Fbk0OmIwfoSiFBTIsi2ZO7qTXaN5O1QzJKtSq0Jqbi -7cB0Z2cUi0QJ11jP4OcoawdVmx9InVwLkBfgR/AydmzClfnV7F8lk+m9UZmDV6/M -Wf+nAkijvYHvtmbb38qsHigE34agkvcy2YWoIstdcQKBgQDYi6fUsNu8RF2JC2NH -YWSnDS4JhYVP6pRugaH/RMe8+uX64JK1h85EXqf+rIWfmdkEN43p95lUxjObpEHE -pt2irrei8ARYqb7W0VkDfcEJnWoHMPlY/j7wunoIUFP1l8WWsrDyF3l+XnFLfRSj -rJ8/q/pPqJT2caYyOfF4EGltBQKBgQDJMOOVuoNAsZNttK9JwbORME+aK9OTXriT -VzHbqbMiyWBvqtnpRbwr6gHm+1nJgL4Fz1pT7vUxyXKzDIrRjx92qWp2sIFoYmAs -y0pdKChREbPAZUFIG5VJHqSgpStJq74Cq7Rrpmyg/rgKTNNCx7CwJ7Lap4OSr1tY -u5tZ/ARB9wKBgQCrXhn7X9sFKrU69yAiJRkaenusXVYa4HGG4l/pO+01yPiniYGQ -12CNCru395/npkDv7WcJeUCGrjO5QEwr7EIJtGrvLzLBV9QG/LWM5cAWc9SszD0I -CGyW5XwdiDRpbyqXD+KoM5uaPfM64kmrHbkJEniNAcShI1968BAADDziYQKBgHIG -UkXu0iqdOdgoWlwr+vBjeAoBosTYHzXqaih7sjoZ9Zz0MNKDolUlJ/TYKZgJbiju -ztvZB4AzEt2lUxvXyNZAyEUCXiVh8+4PPVM0yjzAEqcM/AW5FE+/nUAqvDDhtCvh -UrzcMp7AHmBAEE+9y0bbCQPhgrFkzxmoIjDKo0MbAoGAK58GTcH85twPkxdcskmv -wGzaUNXK4ggUvvXHbHDv0oVgx9aZR5dNclBkYcED0bQQL/c8aUk8hQkDj7TEvU6I -VDZ9iuJl2HRiVxRgl+cREqRhVhIoGbHCpZveymCIUjVWQqZtsWrtyI6R5gt+8LbW -eNJGBYZxsCZuBtBdhiFIlfI= ------END PRIVATE KEY----- diff --git a/tests/certs/public.pem b/tests/certs/public.pem deleted file mode 100644 index 10edc968..00000000 --- a/tests/certs/public.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqi8Bd/OcdcNLprGKVG+i -30pf41LZ8tk+Ia1QAH8ynDw2Mik/4MCSiQJubzzy4bKoySw9u3kVTX86gmDjiTyC -uQ/mACYoBG+/5/rPej+5tG8y2ZWt8P9hmUamW/JuPMTkCB5v41QwuOUN6v83RVMI -AbazicQQUrO+Xi0YY9OAgj43aqGXK9EXaMs0hirxojRjDY31hLHU5xmDnBjVGtFD -rCKZPoEvkFw+hDJYpTPBlkAPnA8OjsJOcnQ9G5jwIgw/pkJ+MKgqq7kbg84hXv3K -lpWO3rRE0+TsdQ/Hoi00xkZ4/KdExK6P3meD3CbFYR/7fcjry4M3t6YGj222Onp0 -0wIDAQAB ------END PUBLIC KEY----- diff --git a/tests/charts/lang.chart.json b/tests/charts/lang.chart.json deleted file mode 100644 index f2553516..00000000 --- a/tests/charts/lang.chart.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "label": "::Demo", - "version": "1.0.0", - "description": "::Demo", - "nodes": [ - { - "name": "行业", - "engine": "xiang", - "query": { - "select": [ - "city", - ":COUNT(id) as 数量", - "industries[*](string 50) as industry" - ], - "from": "$service", - "wheres": [ - { "field": "created_at", ">": "?:$from.0" }, - { "field": "created_at", "<": "?:$to" } - ], - "orders": "数量 desc", - "limit": 100 - } - }, - { - "name": "计费", - "engine": "xiang", - "query": { - "select": ["city", ":COUNT(id) as 数量", "price_options[*] as option"], - "from": "$service", - "wheres": [ - { "field": "created_at", ">": "?:$from.0" }, - { "field": "created_at", "<": "?:$to" } - ], - "orders": "数量 desc", - "limit": 100 - } - }, - { - "name": "合并结果", - "process": "xiang.helper.ArrayPluck", - "args": [ - ["城市", "行业", "计费"], - { - "行业": { "key": "city", "value": "数量", "items": "{{$res.行业}}" }, - "计费": { "key": "city", "value": "数量", "items": "{{$res.计费}}" } - } - ] - } - ], - "output": { - "::Merge": "{{$res.合并结果}}", - "::Billing": "{{$res.计费}}", - "::Industry": "{{$res.行业}}", - "::Parameter": "{{$in}}" - }, - "apis": { - "data": { - "disable": false, - "guard": "-", - "default": [{ "from": "1980-01-02", "to": "2050-12-31" }] - }, - "setting": { "disable": false, "guard": "-" } - }, - "filters": { - "::Start": { - "label": "::Start", - "bind": "from", - "input": { - "type": "date", - "props": { - "placeholder": "::Please select a start time" - } - } - }, - "::End": { - "label": "::End", - "bind": "to", - "input": { - "type": "date", - "props": { - "placeholder": "::Please select an end time" - } - } - } - }, - "page": { - "primary": "::City", - "layout": { - "filters": [ - { "name": "::Start", "width": 6 }, - { "name": "::End", "width": 6 } - ], - "charts": [ - { "type": "line", "props": {} }, - { "type": "bar", "props": {} } - ] - }, - "actions": {} - } -} diff --git a/tests/charts/service/compare.chart.json b/tests/charts/service/compare.chart.json deleted file mode 100644 index 0c66d6fb..00000000 --- a/tests/charts/service/compare.chart.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "label": "指标对比", - "version": "1.0.0", - "description": "指标对比用于测试", - "nodes": [ - { - "name": "行业", - "engine": "xiang", - "query": { - "debug": false, - "select": [ - "city", - ":COUNT(id) as 数量", - "industries[*](string 50) as industry" - ], - "from": "$service", - "wheres": [ - { "field": "created_at", ">": "?:$from.0" }, - { "field": "created_at", "<": "?:$to" } - ], - "orders": "数量 desc", - "limit": 100 - } - }, - { - "name": "计费", - "engine": "xiang", - "query": { - "select": ["city", ":COUNT(id) as 数量", "price_options[*] as option"], - "from": "$service", - "wheres": [ - { "field": "created_at", ">": "?:$from.0" }, - { "field": "created_at", "<": "?:$to" } - ], - "orders": "数量 desc", - "limit": 100 - } - }, - { - "name": "合并结果", - "process": "xiang.helper.ArrayPluck", - "args": [ - ["城市", "行业", "计费"], - { - "行业": { "key": "city", "value": "数量", "items": "{{$res.行业}}" }, - "计费": { "key": "city", "value": "数量", "items": "{{$res.计费}}" } - } - ] - } - ], - "output": { - "合并": "{{$res.合并结果}}", - "计费": "{{$res.计费}}", - "行业": "{{$res.行业}}", - "参数": "{{$in}}" - }, - "apis": { - "data": { - "disable": false, - "guard": "-", - "default": [{ "from": "1980-01-02", "to": "2050-12-31" }] - }, - "setting": { - "disable": false, - "guard": "-" - } - }, - "filters": { - "开始时间": { - "label": "开始时间", - "bind": "from", - "input": { - "type": "date", - "props": { - "placeholder": "请选择开始时间" - } - } - }, - "结束时间": { - "label": "结束时间", - "bind": "to", - "input": { - "type": "date", - "props": { - "placeholder": "请选择结束时间" - } - } - } - }, - "page": { - "primary": "城市", - "layout": { - "filters": [ - { "name": "开始时间", "width": 6 }, - { "name": "结束时间", "width": 6 } - ], - "charts": [ - { "type": "line", "props": {} }, - { "type": "bar", "props": {} } - ] - }, - "actions": {} - } -} diff --git a/tests/charts/service/compare.data.js b/tests/charts/service/compare.data.js deleted file mode 100644 index 20b71204..00000000 --- a/tests/charts/service/compare.data.js +++ /dev/null @@ -1,4 +0,0 @@ -function main(args, out, res) { - console.log(args, out, res); - return out; -} diff --git a/tests/charts/service/service.sql b/tests/charts/service/service.sql deleted file mode 100644 index 97299a6b..00000000 --- a/tests/charts/service/service.sql +++ /dev/null @@ -1,8 +0,0 @@ -SELECT * FROM service as service -JOIN JSON_TABLE(service.price_options, '$[*]' columns (price varchar(50) path '$') ) as t group by price - -/** -[mysqld] -sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION" - */ - \ No newline at end of file diff --git a/tests/charts/session.json b/tests/charts/session.json deleted file mode 100644 index f806316d..00000000 --- a/tests/charts/session.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "label": "指标对比", - "version": "1.0.0", - "description": "指标对比用于测试", - "nodes": [{ "name": "ID", "process": "session.Get", "args": ["id"] }], - "output": { - "ID": "{{$res.ID}}" - }, - "apis": { - "data": { - "disable": false, - "guard": "-", - "default": [{ "from": "1980-01-02", "to": "2050-12-31" }] - }, - "setting": { - "disable": false, - "guard": "-" - } - }, - "filters": { - "开始时间": { - "label": "开始时间", - "bind": "from", - "input": { - "type": "date", - "props": { - "placeholder": "请选择开始时间" - } - } - }, - "结束时间": { - "label": "结束时间", - "bind": "to", - "input": { - "type": "date", - "props": { - "placeholder": "请选择结束时间" - } - } - } - }, - "page": { - "primary": "城市", - "layout": { - "filters": [ - { "name": "开始时间", "width": 6 }, - { "name": "结束时间", "width": 6 } - ], - "charts": [ - { "type": "line", "props": {} }, - { "type": "bar", "props": {} } - ] - }, - "actions": {} - } -} diff --git a/tests/connectors/mongo.conn.json b/tests/connectors/mongo.conn.json deleted file mode 100644 index 24a83408..00000000 --- a/tests/connectors/mongo.conn.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "LANG": "1.0.0", - "VERSION": "1.0.0", - "label": "Mongo TEST", - "type": "mongo", - "options": { - "db": "test", - "hosts": [ - { - "host": "$ENV.MONGO_TEST_HOST", - "port": "$ENV.MONGO_TEST_PORT", - "user": "$ENV.MONGO_TEST_USER", - "pass": "$ENV.MONGO_TEST_PASS" - } - ], - "params": { "maxPoolSize": 20, "w": "majority" } - } -} diff --git a/tests/connectors/mysql.conn.json b/tests/connectors/mysql.conn.json deleted file mode 100644 index 9c540bbe..00000000 --- a/tests/connectors/mysql.conn.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "LANG": "1.0.0", - "VERSION": "1.0.0", - "label": "MySQL 8.0 TEST", - "type": "mysql", - "version": "8.0.26", - "options": { - "db": "test", - "charset": "utf8mb4", - "parseTime": true, - "hosts": [ - { - "host": "$ENV.MYSQL_TEST_HOST", - "port": "$ENV.MYSQL_TEST_PORT", - "user": "$ENV.MYSQL_TEST_USER", - "pass": "$ENV.MYSQL_TEST_PASS", - "primary": true - }, - { - "host": "$ENV.MYSQL_TEST_HOST", - "port": "$ENV.MYSQL_TEST_PORT", - "user": "$ENV.MYSQL_TEST_USER", - "pass": "$ENV.MYSQL_TEST_PASS" - } - ] - } -} diff --git a/tests/connectors/redis.conn.json b/tests/connectors/redis.conn.json deleted file mode 100644 index 4c721edf..00000000 --- a/tests/connectors/redis.conn.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "LANG": "1.0.0", - "VERSION": "1.0.0", - "label": "Redis TEST", - "type": "redis", - "options": { - "host": "$ENV.REDIS_TEST_HOST", - "port": "$ENV.REDIS_TEST_PORT", - "user": "$ENV.REDIS_TEST_USER", - "pass": "$ENV.REDIS_TEST_PASS", - "db": "1" - } -} diff --git a/tests/connectors/sqlite.conn.json b/tests/connectors/sqlite.conn.json deleted file mode 100644 index 9dff7052..00000000 --- a/tests/connectors/sqlite.conn.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "LANG": "1.0.0", - "VERSION": "1.0.0", - "label": "SQLite TEST", - "type": "sqlite3", - "options": { - "file": "$ENV.SQLITE_DB" - } -} diff --git a/tests/data/20211012/0B8DD0DF69EEF7191709FA98BC126AF4.zip b/tests/data/20211012/0B8DD0DF69EEF7191709FA98BC126AF4.zip deleted file mode 100755 index 23948ac3..00000000 Binary files a/tests/data/20211012/0B8DD0DF69EEF7191709FA98BC126AF4.zip and /dev/null differ diff --git a/tests/data/20211015/FEC98553A0AE60792BD67F74F998DEBD.png b/tests/data/20211015/FEC98553A0AE60792BD67F74F998DEBD.png deleted file mode 100755 index 6cae97c4..00000000 Binary files a/tests/data/20211015/FEC98553A0AE60792BD67F74F998DEBD.png and /dev/null differ diff --git a/tests/data/20211018/30E9C82A556D0560FFF6CE9972D52FD0.png b/tests/data/20211018/30E9C82A556D0560FFF6CE9972D52FD0.png deleted file mode 100755 index f352f551..00000000 Binary files a/tests/data/20211018/30E9C82A556D0560FFF6CE9972D52FD0.png and /dev/null differ diff --git a/tests/data/20211018/44CE2C3D93EB16264DA2A7E86881EA08.png b/tests/data/20211018/44CE2C3D93EB16264DA2A7E86881EA08.png deleted file mode 100755 index 7c849adf..00000000 Binary files a/tests/data/20211018/44CE2C3D93EB16264DA2A7E86881EA08.png and /dev/null differ diff --git a/tests/data/20211018/4B78671E7D8DF4B48CD57C68F3ED2889.jpg b/tests/data/20211018/4B78671E7D8DF4B48CD57C68F3ED2889.jpg deleted file mode 100755 index 4234c2c9..00000000 Binary files a/tests/data/20211018/4B78671E7D8DF4B48CD57C68F3ED2889.jpg and /dev/null differ diff --git a/tests/data/20211018/AC26C0925105C58E2BAD0BF54D396CA8.png b/tests/data/20211018/AC26C0925105C58E2BAD0BF54D396CA8.png deleted file mode 100755 index 63c0b874..00000000 Binary files a/tests/data/20211018/AC26C0925105C58E2BAD0BF54D396CA8.png and /dev/null differ diff --git a/tests/data/20211018/C1BEBE27A1E455CF9A90025955F6BBEF.jpg b/tests/data/20211018/C1BEBE27A1E455CF9A90025955F6BBEF.jpg deleted file mode 100755 index c0758ee3..00000000 Binary files a/tests/data/20211018/C1BEBE27A1E455CF9A90025955F6BBEF.jpg and /dev/null differ diff --git a/tests/data/20211018/D5A2EDFC0B52B31F41DF3506478BA0B4.png b/tests/data/20211018/D5A2EDFC0B52B31F41DF3506478BA0B4.png deleted file mode 100755 index 6cae97c4..00000000 Binary files a/tests/data/20211018/D5A2EDFC0B52B31F41DF3506478BA0B4.png and /dev/null differ diff --git a/tests/data/20211018/E4E757C3DF5F1F8AFC024AED03278307.jpg b/tests/data/20211018/E4E757C3DF5F1F8AFC024AED03278307.jpg deleted file mode 100755 index c0758ee3..00000000 Binary files a/tests/data/20211018/E4E757C3DF5F1F8AFC024AED03278307.jpg and /dev/null differ diff --git a/tests/data/20211018/FCE1D41056C8261DF4A9886733DA7FEA.png b/tests/data/20211018/FCE1D41056C8261DF4A9886733DA7FEA.png deleted file mode 100755 index 7c849adf..00000000 Binary files a/tests/data/20211018/FCE1D41056C8261DF4A9886733DA7FEA.png and /dev/null differ diff --git a/tests/data/README.md b/tests/data/README.md deleted file mode 100644 index be45d957..00000000 --- a/tests/data/README.md +++ /dev/null @@ -1 +0,0 @@ -文件上传 diff --git a/tests/data/assets/multiple-columns.xlsx b/tests/data/assets/multiple-columns.xlsx deleted file mode 100644 index fa6ad9c8..00000000 Binary files a/tests/data/assets/multiple-columns.xlsx and /dev/null differ diff --git a/tests/data/assets/multiple.xlsx b/tests/data/assets/multiple.xlsx deleted file mode 100644 index b8f0456c..00000000 Binary files a/tests/data/assets/multiple.xlsx and /dev/null differ diff --git a/tests/data/assets/simple.csv b/tests/data/assets/simple.csv deleted file mode 100644 index 61c45121..00000000 --- a/tests/data/assets/simple.csv +++ /dev/null @@ -1,5 +0,0 @@ -订单号,姓名,手机,性别,年龄,商品名称,数量,单价,总价,备注 -SN202101120018,张三,13211000011,男,26,彩绘湖北地图,3,65.5,196.5, -,李四,13211000011,男,42,景祐遁甲符应经,1,34.8,34.8, -SN202101120019,王二麻子,13410001998,男,51,太极先天后天三图说,1,24.35,72.85,孔夫子二手交易 -SN202101120019,王二麻子,13410001998,男,51,五要奇书,1,48.5,72.85,孔夫子二手交易 diff --git a/tests/data/assets/simple.xlsx b/tests/data/assets/simple.xlsx deleted file mode 100644 index 279a7ee0..00000000 Binary files a/tests/data/assets/simple.xlsx and /dev/null differ diff --git a/tests/dyforms/app.form.json b/tests/dyforms/app.form.json deleted file mode 100644 index 4e2b288d..00000000 --- a/tests/dyforms/app.form.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "guard": "bearer-jwt", - "title": "应用表单", - "sn": "AP1024", - "orign": "手机应用", - "columns": [ - { - "name": "门店", - "field": "store_id", - "searchable": true, - "props": { - "value": ":store_id", - "type": "SelectStore", - "title": "门店", - "placeholder": "请选择门店", - "validation": [ - { "type": "Required" }, - { "type": "StoreStatus", "args": [":store_id"] } - ] - } - }, - { - "name": "订单数量", - "field": "orders_amount", - "searchable": true, - "props": { - "value": ":orders_amount", - "type": "Number", - "title": "订单数量", - "placeholder": "今日订单数量", - "computed": "OrderAmount", - "validation": [ - { "type": "Required" }, - { "type": "IsNumber", "args": [":orders_amount"] } - ] - } - }, - { - "name": "账单地址", - "field": "address", - "props": { - "value": ":address", - "type": "Text", - "title": "账单地址", - "placeholder": "填写账单地址", - "validation": [ - { "type": "Required" }, - { "type": "MaxLength", "args": [":address", 200] } - ] - } - } - ], - "filters": [], - "list-layout": { - "columns": [{ "name": "store_id" }, { "name": "orders_amount" }], - "filters": [{ "name": "订单数量" }] - } -} diff --git a/tests/dyforms/website.form.json b/tests/dyforms/website.form.json deleted file mode 100644 index 4dc5be25..00000000 --- a/tests/dyforms/website.form.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "guard": "bearer-jwt", - "title": "官网表单", - "sn": "AP2048", - "orign": "官方网站", - "columns": [ - { - "name": "门店", - "field": "store_id", - "searchable": true, - "props": { - "value": ":store_id", - "type": "SelectStore", - "title": "门店", - "placeholder": "请选择门店", - "validation": [ - { "type": "Required" }, - { "type": "StoreStatus", "args": [":store_id"] } - ] - } - }, - { - "name": "订单数量", - "field": "orders_amount", - "searchable": true, - "props": { - "value": ":orders_amount", - "type": "Number", - "title": "订单数量", - "placeholder": "今日订单数量", - "computed": "OrderAmount", - "validation": [ - { "type": "Required" }, - { "type": "IsNumber", "args": [":orders_amount"] } - ] - } - }, - { - "name": "账单地址", - "field": "address", - "props": { - "value": ":address", - "type": "Text", - "title": "账单地址", - "placeholder": "填写账单地址", - "validation": [ - { "type": "Required" }, - { "type": "MaxLength", "args": [":address", 200] } - ] - } - } - ], - "filters": [ - { - "订单数量": { - "label": "订单数量超过", - "bind": "where.orders_amount.ge", - "type": "Select", - "props": { - "placeholder": "订单数量", - "options": [ - { "label": "大于100", "value": 100 }, - { "label": "大于50", "value": 50 } - ] - } - } - } - ], - "list-layout": { - "columns": [ - { "name": "store_id" }, - { "name": "orders_amount" }, - { "name": "address" } - ], - "filters": [{ "name": "订单数量" }] - } -} diff --git a/tests/flows/assign/reject/reset.flow.json b/tests/flows/assign/reject/reset.flow.json deleted file mode 100644 index 4b4088cd..00000000 --- a/tests/flows/assign/reject/reset.flow.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "label": "审批驳回并重置状态", - "version": "1.0.0", - "description": "审批驳回并重置状态", - "nodes": [ - { - "name": "当前用户", - "process": "session.Get", - "args": ["user"] - }, - { - "name": "工作流", - "process": "xiang.workflow.Save", - "args": [ - "{{$res.当前用户.id}}", - "{{$name}}", - "{{$node}}", - "{{$id}}", - "{{$input}}" - ] - }, - { - "name": "审批驳回", - "process": "xiang.workflow.Next", - "args": [ - "{{$res.当前用户.id}}", - "{{$res.工作流.id}}", - { "审批状态": "驳回", "驳回原因": "{{$input.reason}}" } - ] - }, - { - "name": "重置工作流", - "process": "xiang.workflow.Goto", - "args": [ - "{{$res.当前用户.id}}", - "{{$res.工作流.id}}", - "选择商务负责人", - {} - ] - } - ], - "output": "{{$res.重置工作流}}" -} diff --git a/tests/flows/assign/reject/terminal.flow.json b/tests/flows/assign/reject/terminal.flow.json deleted file mode 100644 index 3145ab88..00000000 --- a/tests/flows/assign/reject/terminal.flow.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "label": "审批驳回并关闭流程", - "version": "1.0.0", - "description": "审批驳回并关闭流程", - "nodes": [ - { - "name": "当前用户", - "process": "session.Get", - "args": ["user"] - }, - { - "name": "工作流", - "process": "xiang.workflow.Save", - "args": [ - "{{$res.当前用户.id}}", - "{{$name}}", - "{{$node}}", - "{{$id}}", - "{{$input}}" - ] - }, - { - "name": "审批驳回", - "process": "xiang.workflow.Next", - "args": [ - "{{$res.当前用户.id}}", - "{{$res.工作流.id}}", - { "审批状态": "驳回", "驳回原因": "{{$input.reason}}" } - ] - }, - { - "name": "关闭流程", - "process": "xiang.workflow.Status", - "args": ["{{$res.工作流.id}}", "已关闭"] - } - ], - "output": "{{$res.关闭流程}}" -} diff --git a/tests/flows/assign/resolve.flow.json b/tests/flows/assign/resolve.flow.json deleted file mode 100644 index d6b203a0..00000000 --- a/tests/flows/assign/resolve.flow.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "label": "指派信息审批通过", - "version": "1.0.0", - "description": "指派信息审批通过", - "nodes": [ - { - "name": "当前用户", - "process": "session.Get", - "args": ["user"] - }, - { - "name": "工作流", - "process": "xiang.workflow.Save", - "args": [ - "{{$res.当前用户.id}}", - "{{$name}}", - "{{$node}}", - "{{$id}}", - "{{$input}}" - ] - }, - { - "name": "审批通过", - "process": "xiang.workflow.Next", - "args": [ - "{{$res.当前用户.id}}", - "{{$res.工作流.id}}", - { "审批状态": "通过" } - ] - } - ], - "output": "{{$res.审批通过}}" -} diff --git a/tests/flows/assign/save.flow.json b/tests/flows/assign/save.flow.json deleted file mode 100644 index 38173521..00000000 --- a/tests/flows/assign/save.flow.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "label": "保存指派信息", - "version": "1.0.0", - "description": "保存指派信息", - "nodes": [ - { - "name": "当前用户", - "process": "session.Get", - "args": ["user"] - }, - { - "name": "工作流", - "process": "xiang.workflow.Save", - "args": [ - "{{$res.当前用户.id}}", - "{{$name}}", - "{{$node}}", - "{{$id}}", - "{{$input}}" - ] - } - ], - "output": "{{$res.工作流}}" -} diff --git a/tests/flows/assign/submit.flow.json b/tests/flows/assign/submit.flow.json deleted file mode 100644 index 294d1009..00000000 --- a/tests/flows/assign/submit.flow.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "label": "提交指派信息", - "version": "1.0.0", - "description": "提交指派信息", - "nodes": [ - { - "name": "当前用户", - "process": "session.Get", - "args": ["user"] - }, - { - "name": "工作流", - "process": "xiang.workflow.Save", - "args": [ - "{{$res.当前用户.id}}", - "{{$name}}", - "{{$node}}", - "{{$id}}", - "{{$input}}" - ] - }, - { - "name": "提交审批", - "process": "xiang.workflow.Next", - "args": ["{{$res.当前用户.id}}", "{{$res.工作流.id}}"] - } - ], - "output": "{{$res.关闭流程}}" -} diff --git a/tests/flows/assign/terminal.flow.json b/tests/flows/assign/terminal.flow.json deleted file mode 100644 index 9bc4a82d..00000000 --- a/tests/flows/assign/terminal.flow.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "label": "关闭流程", - "version": "1.0.0", - "description": "关闭流程", - "nodes": [ - { - "name": "当前用户", - "process": "session.Get", - "args": ["user"] - }, - { - "name": "工作流", - "process": "xiang.workflow.Save", - "args": [ - "{{$res.当前用户.id}}", - "{{$name}}", - "{{$node}}", - "{{$id}}", - "{{$input}}" - ] - }, - { - "name": "关闭流程", - "process": "xiang.workflow.Status", - "args": ["{{$res.当前用户.id}}", "{{$res.工作流.id}}", "已关闭"] - } - ], - "output": "{{$res.关闭流程}}" -} diff --git a/tests/flows/benchmark/pure.flow.json b/tests/flows/benchmark/pure.flow.json deleted file mode 100644 index bf744b95..00000000 --- a/tests/flows/benchmark/pure.flow.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "label": "性能测试", - "version": "1.0.0", - "description": "性能测试", - "nodes": [ - { - "name": "性能测试", - "process": "xiang.helper.Return", - "args": [1, 2, 3] - } - ], - "output": "{{$res.性能测试}}" -} diff --git a/tests/flows/benchmark/script.flow.json b/tests/flows/benchmark/script.flow.json deleted file mode 100644 index e98c5c6f..00000000 --- a/tests/flows/benchmark/script.flow.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "label": "性能测试", - "version": "1.0.0", - "description": "性能测试", - "nodes": [ - { - "name": "性能测试", - "process": "scripts.time.now", - "args": [1, 2, 3] - } - ], - "output": "{{$res.性能测试}}" -} diff --git a/tests/flows/current/user.flow.json b/tests/flows/current/user.flow.json deleted file mode 100644 index 61b16df9..00000000 --- a/tests/flows/current/user.flow.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "label": "当前用户", - "version": "1.0.0", - "description": "读取当前用户", - "nodes": [ - { - "name": "用户", - "process": "session.Get", - "args": ["user"] - }, - { - "name": "ID", - "process": "xiang.helper.MapGet", - "args": ["{{$res.用户}}", "ID"] - } - ], - "output": 1 -} diff --git a/tests/flows/demo/admin.flow.json b/tests/flows/demo/admin.flow.json deleted file mode 100644 index 9fb64aa6..00000000 --- a/tests/flows/demo/admin.flow.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "label": "Auto Login", - "version": "1.0.0", - "description": "Auto Login for demo", - "nodes": [ - { - "name": "User", - "process": "models.xiang.user.Get", - "args": [ - { - "select": ["id", "email", "name", "type"], - "wheres": [{ "column": "id", "value": 1 }], - "limit": 1 - } - ] - }, - { - "name": "Menu", - "process": "flows.xiang.menu", - "args": ["{{$res.User.0.id}}"] - }, - { - "name": "SID", - "process": "session.start" - }, - { - "name": "JWT", - "process": "xiang.helper.JWTMake", - "args": [ - "{{$res.User.0.id}}", - {}, - { - "timeout": 3600, - "sid": "{{$res.SID}}" - } - ] - }, - { - "name": "Set User", - "process": "session.set", - "args": ["user", "{{$res.User.0}}"] - }, - { - "name": "Set issuer", - "process": "session.set", - "args": ["issuer", "xiang"] - }, - { - "name": "Set User ID", - "process": "session.set", - "args": ["user_id", "{{$res.User.0.id}}"] - } - ], - "output": { - "sid": "{{$res.SID}}", - "user": "{{$res.User.0}}", - "menus": "{{$res.Menu}}", - "token": "{{$res.JWT.token}}", - "expires_at": "{{$res.JWT.expires_at}}" - } -} diff --git a/tests/flows/demo/token.flow.json b/tests/flows/demo/token.flow.json deleted file mode 100644 index ca9002b2..00000000 --- a/tests/flows/demo/token.flow.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "label": "Auto Login", - "version": "1.0.0", - "description": "Auto Login for demo", - "nodes": [ - { - "name": "User", - "process": "flows.demo.admin", - "args": [] - } - ], - "output": "{{$res.User.token}}" -} diff --git a/tests/flows/foo/bar.flow.json b/tests/flows/foo/bar.flow.json deleted file mode 100644 index 4c69978f..00000000 --- a/tests/flows/foo/bar.flow.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "label": "最新信息", - "version": "1.0.0", - "description": "最新信息", - "nodes": [ - { - "name": "user", - "engine": "xiang", - "query": { - "select": ["id", "name"], - "from": "$user", - "first": true - } - } - ], - "output": { - "params": "{{$in}}" - } -} diff --git a/tests/flows/helper/hextostring.flow.json b/tests/flows/helper/hextostring.flow.json deleted file mode 100644 index 48fc2375..00000000 --- a/tests/flows/helper/hextostring.flow.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "label": "HexToString", - "version": "1.0.0", - "description": "HexToString", - "nodes": [ - { - "name": "Value", - "process": "xiang.helper.HexToString", - "args": ["ab"] - } - ], - "output": "{{$res.Value}}" -} diff --git a/tests/flows/hooks/after_find.flow.json b/tests/flows/hooks/after_find.flow.json deleted file mode 100644 index 59a161ba..00000000 --- a/tests/flows/hooks/after_find.flow.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "label": "After:Find", - "version": "1.0.0", - "description": "After:Find", - "nodes": [ - { - "name": "结果", - "process": "xiang.helper.MapSet", - "args": ["{{$in.0}}", "after", 100] - } - ], - "output": "{{$res.结果}}" -} diff --git a/tests/flows/hooks/after_save.flow.json b/tests/flows/hooks/after_save.flow.json deleted file mode 100644 index 9488fcd4..00000000 --- a/tests/flows/hooks/after_save.flow.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "After:Save", - "version": "1.0.0", - "description": "After:Save", - "nodes": [], - "output": { "id": "{{$in.0}}", "after": 100 } -} diff --git a/tests/flows/hooks/after_search.flow.json b/tests/flows/hooks/after_search.flow.json deleted file mode 100644 index eb47c663..00000000 --- a/tests/flows/hooks/after_search.flow.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "label": "After:Search", - "version": "1.0.0", - "description": "After:Search", - "nodes": [ - { - "name": "结果", - "process": "xiang.helper.MapSet", - "args": ["{{$in.0}}", "after", 100] - } - ], - "output": "{{$res.结果}}" -} diff --git a/tests/flows/hooks/after_select.flow.json b/tests/flows/hooks/after_select.flow.json deleted file mode 100644 index edef02d0..00000000 --- a/tests/flows/hooks/after_select.flow.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "label": "After:Select", - "version": "1.0.0", - "description": "After:Select", - "nodes": [ - { - "name": "结果", - "process": "xiang.helper.ArrayMapSet", - "args": ["{{$in.0}}", "after", 100] - } - ], - "output": "{{$res.结果}}" -} diff --git a/tests/flows/hooks/before_find.flow.json b/tests/flows/hooks/before_find.flow.json deleted file mode 100644 index 18359cd1..00000000 --- a/tests/flows/hooks/before_find.flow.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "Before:Find", - "version": "1.0.0", - "description": "Before:Find", - "nodes": [], - "output": [1, {}] -} diff --git a/tests/flows/hooks/before_save.flow.json b/tests/flows/hooks/before_save.flow.json deleted file mode 100644 index 0d66c10c..00000000 --- a/tests/flows/hooks/before_save.flow.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "label": "Before:Save", - "version": "1.0.0", - "description": "Before:Save", - "nodes": [], - "output": [ - { - "name": "腾讯黑岩云主机", - "short_name": "高性能云主机", - "kind_id": 3, - "manu_id": 1, - "price_options": "按月订阅" - } - ] -} diff --git a/tests/flows/hooks/before_search.flow.json b/tests/flows/hooks/before_search.flow.json deleted file mode 100644 index 6567dff0..00000000 --- a/tests/flows/hooks/before_search.flow.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "Before:Search", - "version": "1.0.0", - "description": "Before:Search", - "nodes": [], - "output": [{ "wheres": [{ "column": "status", "value": "enabled" }] }, 1, 2] -} diff --git a/tests/flows/hooks/before_select.flow.json b/tests/flows/hooks/before_select.flow.json deleted file mode 100644 index f04cd736..00000000 --- a/tests/flows/hooks/before_select.flow.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "Before:Select", - "version": "1.0.0", - "description": "Before:Select", - "nodes": [], - "output": ["腾讯", "name", "id"] -} diff --git a/tests/flows/hooks/session.flow.json b/tests/flows/hooks/session.flow.json deleted file mode 100644 index c2e219e3..00000000 --- a/tests/flows/hooks/session.flow.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "label": "会话测试", - "version": "1.0.0", - "description": "Before:Select", - "nodes": [ - { - "name": "会话数据", - "process": "session.Dump" - }, - { - "name": "打印数据", - "process": "xiang.helper.Print", - "args": ["会话数据:", "{{$res.会话数据}}"] - } - ], - "output": "{{$in}}" -} diff --git a/tests/flows/latest.count.js b/tests/flows/latest.count.js deleted file mode 100644 index 90d1d3d0..00000000 --- a/tests/flows/latest.count.js +++ /dev/null @@ -1,6 +0,0 @@ -function main(args, out, res) { - return { - args: args, - plugin: res.github.name, - }; -} diff --git a/tests/flows/latest.flow.json b/tests/flows/latest.flow.json deleted file mode 100644 index a2197fce..00000000 --- a/tests/flows/latest.flow.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "label": "最新信息", - "version": "1.0.0", - "description": "最新信息", - "nodes": [ - { - "name": "manus", - "process": "models.manu.get", - "args": [ - { - "select": ["id", "name", "short_name"], - "limit": 20, - "orders": [{ "column": "created_at", "option": "desc" }], - "wheres": [ - { "column": "status", "value": "enabled" }, - { "column": "name", "value": "{{$in.0}}", "op": "like" } - ] - } - ], - "script": "rank", - "outs": ["{{$out.manus}}", "{{$out.manu_ids}}"] - }, - { - "name": "users", - "process": "models.user.paginate", - "args": [ - { - "select": ["id", "name", "extra", "resume"], - "withs": { - "manu": { "query": { "select": ["name", "short_name"] } }, - "addresses": { - "query": { "select": ["province", "city", "location"] } - } - }, - "orders": [{ "column": "created_at", "option": "desc" }], - "wheres": [ - { "column": "status", "value": "enabled" }, - { "column": "manu_id", "value": "{{$res.manus.1}}", "op": "in" } - ] - }, - 1, - 20 - ], - "outs": ["{{$out.data}}"] - }, - { - "name": "github", - "process": "plugins.user.github", - "args": [ - "{{$res.users.0}}", - "{{$res.manus}}", - "{{$in.0}}", - "{{$in.1}}", - "{{$in.2}}", - "{{pluck(:$res.users, 'id', 0.618, 10)}}", - "foo", - 1 - ] - }, - { - "name": "count", - "script": "count" - } - ], - "output": { - "params": "{{$in}}", - "data": { - "manus": "{{$res.manus.0}}", - "users": "{{$res.users.0}}", - "count": "{{$res.count}}" - } - } -} diff --git a/tests/flows/latest.rank.js b/tests/flows/latest.rank.js deleted file mode 100644 index eef2ba7f..00000000 --- a/tests/flows/latest.rank.js +++ /dev/null @@ -1,18 +0,0 @@ -// 结果集处理脚本 -function main(args, manus, data) { - // 解析厂商ID - var manu_ids = []; - for (var i in manus) { - var id = manus[i].id; - if (id) { - manu_ids.push(id); - } - } - - // 处理返回结果 - var res = { - manus: manus, - manu_ids: manu_ids, - }; - return res; -} diff --git a/tests/flows/rfid/read.flow.json b/tests/flows/rfid/read.flow.json deleted file mode 100644 index 8c8030b8..00000000 --- a/tests/flows/rfid/read.flow.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "读取RFID标签", - "version": "1.0.0", - "description": "读取RFID标签", - "nodes": [{ "script": "print" }], - "output": null -} diff --git a/tests/flows/rfid/read.print.js b/tests/flows/rfid/read.print.js deleted file mode 100644 index 8db5c50a..00000000 --- a/tests/flows/rfid/read.print.js +++ /dev/null @@ -1,11 +0,0 @@ -function main(args, out, res) { - // 100000012021128798321101 15 2d 02 f2 96 72 1e 52 b9 cd - var idstr = args[0] || ""; - var len = idstr.length; - - if (len != 36) { - return; - } - id = BigInt("0x" + idstr.substring(8, len - 8).toUpperCase()).toString(10); - console.log(id); -} diff --git a/tests/flows/select/manager.flow.json b/tests/flows/select/manager.flow.json deleted file mode 100644 index 38004b58..00000000 --- a/tests/flows/select/manager.flow.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "label": "选择管理者", - "version": "1.0.0", - "description": "选择管理者", - "nodes": [ - { - "name": "负责人", - "engine": "xiang", - "query": { - "select": ["id"], - "from": "$xiang.user", - "wheres": [{ ":type": "类型", "=": "staff" }], - "first": true - }, - "orders": [":RAND()"] - } - ], - "output": "{{$in.0}}" -} diff --git a/tests/flows/websocket/chat.flow.json b/tests/flows/websocket/chat.flow.json deleted file mode 100644 index e1dedde2..00000000 --- a/tests/flows/websocket/chat.flow.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "a chat Websocket process", - "version": "1.0.0", - "description": "a chat Websocket process", - "nodes": [], - "output": "{{$in.0}}" -} diff --git a/tests/import/manu.csv b/tests/import/manu.csv deleted file mode 100644 index c02a85a8..00000000 --- a/tests/import/manu.csv +++ /dev/null @@ -1,1229 +0,0 @@ -"id","aid","name","slug","logo","home_page_logo","home_page_banner","home_page_url","phone_number","page_view","benchmark_userid","summary","markdown","service_cnt","benchmark_cnt","status","priority","is_deleted","create_at","update_at","smalltitle" -1,0,"tes","tes4263","/upload/20150530/14330011618311.png","/upload/20150530/14330011645447.png","/upload/20150530/14330011663856.png","aaa.com","19939",1999,991,"jjsj","E#ikks",0,0,"draft",9999,1,"2015-05-30 23:53:04","0000-00-00 00:00:00","" -2,0,"aaaa","aaaa6074","upload/20150608/14337678207179.jpg","upload/20150608/14337678264885.png","upload/20150608/14337678293434.png","aaa.com","399292929391",2199,199,"199aa","#===aasdfasfd -#-a-asdf",0,0,"published",9999,1,"2015-06-08 20:50:48","2015-06-08 20:50:59","" -23,8,"阿里云计算有限公司","aliyun9166","202009/1599199254511794422.png","upload/20150630/14356636641763.png","upload/20150630/14356636355919.png","http://www.aliyun.com","321",3899,9,"阿里云计算有限公司成立于2009年9月10日,在杭州、北京和硅谷等地设有研发中心和运营机构。阿里云的目标是打造全球领先的云计算服务平台。","#阿里云介绍 - -阿里云计算有限公司成立于2009年9月10日,在杭州、北京和硅谷等地设有研发中心和运营机构。阿里云的目标是打造全球领先的云计算服务平台。 - -在未来的互联网中,云计算将会成为一种随时、随地,并根据需要而提供的公共服务。高效的绿色数据中心以及能支持不同互联网应用的大规模分布式存储和计算是营造下一代互联网服务平台最基本的核心技术。 - -阿里云致力于打造公共、开放的云计算服务平台。我们将借助技术的创新,不断提升计算能力与规模效益,将云计算变成真正意义上的公共服务。与此同时,我们将通过aliyun.com,用互联网的方式使得大家可以便捷的按需获取阿里云的云计算产品与服务。 - -我们希望更多的合作伙伴、中小企业、开发者能够受益于云计算带来的便利和价值,从而促进云生态系统的健康发展。 - -地址:杭州市余杭区文一西路969号淘宝城 - -电话:0571-85025885",7,0,"published",1,0,"2020-03-23 12:33:19","2021-08-26 16:57:41","阿里云" -24,51,"世纪互联蓝云","lanyun6300","202004/1587453989610285240.jpg","upload/20150726/14379179046264.png","upload/20150718/14371946519832.png","https://www.21vbluecloud.com/","4000890365",0,0,"上海蓝云网络科技有限公司是世纪互联成立的全资子公司,专门运营微软Microsoft Azure,Office 365, PowerBI和 Microsoft Dynamics 365云服务以及相关云应用解决方案。公司拥有一支400多人的具备国际一流服务保障水准的云计算运维团队,为客户提供包括IaaS、PaaS、SaaS在内的全方位云服务。作为国内领先的本土云服务提供商,世纪互联蓝云为广大用户提供了中国首家正式商用的国际云计算服务平台,并为用户提供国内最具安全保障和成熟的云计算服务。 - -世纪互联蓝云一直致力于推动中国云计算生态系统的成熟与繁荣。2015年4月,世纪互联蓝云率先引入了国际上应用成熟、并得到广泛认可的4款云应用解决方案。","上海蓝云网络科技有限公司(暨世纪互联蓝云事业部,以下简称世纪互联蓝云)是世纪互联成立的全资子公司,专门运营微软Windows Azure和 Office 365云服务以及相关云应用解决方案。公司拥有一支近400人的具备国际一流服务保障水准的云计算运维团队,为客户提供包括IaaS、PaaS、SaaS在内的全方位云服务。作为国内领先的本土云服务提供商,世纪互联蓝云为广大用户提供了中国首家正式商用的国际云计算服务平台,并为用户提供国内最具安全保障和成熟的云计算服务。 - -世纪互联蓝云一直致力于推动中国云计算生态系统的成熟与繁荣。2015年4月,世纪互联蓝云率先引入了国际上应用成熟、并得到广泛认可的4款云应用解决方案,引领中国云计算行业进入了重体验、重应用的时代。这些云应用分别是:GigaTrust智能权限管理解决方案、BitTitan全方位一站式数据迁移平台、让协同更加便利的AvePoint解决方案、Gartner新技术成熟度曲线(Hype Cycle)中存储技术排名第一的蓝云终端备份解决方案。随着中国云应用的深入,世纪互联蓝云还将不断推出更多国内外优秀的云计算解决方案,助力中国用户高效、安全、便利地应用云。 - -国际一流水准的服务保障体验 - 率先在国内推行国际标准的服务品质协议(SLA),给客户提供有财务保障的月度99.9% SLA服务等级协议保证 - 提供国内云计算行业唯一的公开服务仪表盘,实时查看运维信息,为客户提供国内最透明的云运维服务 - 业内最高效的故障响应时间,保证用户的云服务运行稳定 - 分别位于北京和上海两个T4 标准数据中心,实现6份异地数据备份,为客户提供国内最具安全、可靠的云服务 - 拥有业内最多的可信云服务认证(5项):虚拟云主机、云存储、数据库、云引擎、全网负载均衡 - 通过ISO20000、ISO27001 认证,以及国家信息安全等级保护认证 - -国际一流水平的运维服务 - 提供数据中心支持、云平台运维、客户支持、合规咨询等服务,全方位保障用户最佳体验 - 具备全球视野的本土专业云计算运维团队,服务品质全球领先 - 7X24全年无休技术支持 - 5 X8商务支持",11,0,"published",9999,0,"2015-07-02 15:16:23","2020-06-01 12:04:49","上海蓝云" -25,25,"京东智联云","jingdong7597","202007/1595483865199992904.png","upload/20150718/14372258133951.png","upload/20150718/14372258171100.jpg","http://www.jdcloud.com/","400-623-0183",0,0,"京东智联云(JD Cloud & AI)是京东集团旗下的智能技术提供商,依托京东集团在人工智能、大数据、云计算、物联网等方面的业务实践和技术积淀,形成了从基础平台搭建、业务咨询规划,业务平台建设到运营维护的科技生态格局,拥有领先的数字化、智能化产品,打造服务于数字企业、数字政府的多维场景解决方案,为客户创造价值。 - -京东智联云,致力于成为您最值得信赖的智能技术提供者。","##简介 -京东云是京东旗下的综合云计算服务商,京东云以京东“技术产业化”为使命,依托京东成熟、稳定的全产业链电商信息系统,以及京东在云计算、大数据、物联网和移动互联网方面的多年技术积淀,致力于打造社会化的云平台,培育开放的云生态,向整个电商行业及全社会提供稳定、安全、便捷的云服务。 - -为了更好的满足卖家和买家的电商应用和IT服务需求,京东电商云提供围绕电商应用全生命周期的云服务,与广大商家、用户、ISV和应用开发者,共同培育电商应用生态。继2012年5月推出京东宙斯后,京东云于2013年6月推出了京东云鼎、京东云擎、京东云汇、京东云峰、京东服务市场等[1] 共六大解决方案,向合作ISV和个人开发者,提供京东系统开放接口、电商应用开发云平台、电商应用托管平台、应用与服务交易市场、社区生态环境等电商云服务,初步形成了一个完整的电商云服务链条。2014年1月9日,京东云面向传统企业和本地生活服务企业正式发布了电商云解决方案,此举将助力传统企业快速、高效、低成本的拓展电商业务[2] 。 - - -##电商云 -依托京东作为中国领先的综合网络零售企业,在电商领域中积累超过十年的宝贵经验,京东电商云解决方案根据企业的需求差异将电商云解决方案分成了面向传统零售企业和品牌商的E-commerce版和面向本地生活服务商的Local-commerce版,助力传统企业快速完成电商化进程,目前有超过3万家传统企业正在使用京东电商云服务。 - -京东电商云E-commerce版能够为传统零售企业以及品牌商提供自主品牌商城、全网营销体系、多平台运营等服务。其中,“自主品牌商城”可为企业快速打造属于自己的WEB商城和APP商城;“全网营销体系”可整合全网营销资源为企业带来优质用户;“多平台运营服务”可统一管理多电商平台的订单与仓储配送,实现电商业务的高效运营。同时,还能为客户提供伙伴式的电商咨询服务,包括需求分析、产品定位、用户挖掘、模式创新、品牌重塑等,帮助企业制定完整的电商蓝图和实施策略,满足企业的个性化需求。 - -京东电商云Local-commerce版是面向本地生活服务商提供的移动互联网CRM云解决方案,可打造商家自主品牌APP。商家可以在云端快速便捷地进行会员及商品管理,为消费者提供个性化服务,提升消费者黏性及满意度。 - -京东电商云可为企业带来7大价值,第一,可为企业快速部署自有品牌的电子商城及平台,实现线上线下一体化销售,建立自己的用户体系;第二,提供全流程、全产业链的电商解决方案,客户可共享京东平台资源,灵活配置、使用各种电商经营管理模块;第三,实现一站式多电商平台的统一管理与运营;第四,以京东自主研发的信息系统以及云计算、大数据等创新技术的强大支撑,以云服务的方式为企业搭建信息化所需要的所有网络基础设施及软件、硬件平台;第五,开放的服务模式既可以与企业原有的ERP等系统实现对接,同时也将引入众多优秀的ISV满足企业更多的个性化需求;第六,为企业提供互联网金融等衍生服务,实现商家信贷和消费者信用消费;第七,结合京东十年电商平台经营管理经验,为客户全程提供电子商务诊断、流量管理、客户管理等咨询服务[3] 。 - - -##京东云战略 -京东目前已经确立了以技术为驱动的自营电商业务,以技术为驱动的开放服务业务以及以技术为驱动的数据金融业务三大发展方向,并提出了“电商云”的建设规划,全面发力云计算,力求用IT技术驱动购物体验,并规划了长远的发展战略。 - -京东云战略目标:2015年开放京东电商业务全部资源和能力,以云的形式向全产业链各环节提供京东云服务。成为中国第一位的电商云提供商。2017年面向各行各业提供全面的云计算服务,成为云服务运营商和云计算解决方案提供商,跻身全球领先的云计算服务商行列。 - -京东电商云发展步骤:第一步是京东内部各种电商资源和能力的云化;第二步是将云化后的电商资源对外开放,构建电商云平台,培育电商应用生态;第三步京东电商云将整合更多的外部电商资源,向全行业和全社会提供更广泛、更有价值的电商云服务。 - -京东云发展现状:目前,京东IT资源的内部云化已完成,对京东业务平稳、系统优化和效能提升的作用已经显现,系统在618店庆、沙漠风暴等大规模促销活动中经受住了巨大流量的考验。京东电商云已走入第二阶段,目前已推出京东电商云平台,努力培育京东电商应用生态。",10,0,"published",160,0,"2015-07-02 15:18:30","2020-07-23 13:57:45","京东" -26,7,"优刻得科技股份有限公司","ucloud8865","upload/20150718/14371953215560.png","upload/20150726/14379178112286.png","upload/20150726/14379164332002.png","http://www.ucloud.cn","4000188113",0,7,"UCloud云计算(上海优刻得信息科技有限公司)是国内技术与服务最为顶尖的基础云服务商,公司专注于基础云计算的产品研发与运营,是上海市云计算基地重点企业,国家工信部首批认证通过的“可信云计算企业”。 -我们围绕用户的需求持续创新,通过整合优秀的合作伙伴资源,深耕互联网领域,打造全天候90秒内快速响应的服务体系,在国内云计算领域构筑了独树一帜的竞争优势。","UCloud云计算平台已经为游戏、移动互联网、大数据、电子商务、SaaS等多个领域提供IT基础架构支撑,得益于业内领先的云计算技术和技术团队的专业经验,UCloud已成功为国内外上万家企业用户提供服务,大幅降低了用户使用IT基础设施的成本及技术门槛。 -我们的团队吸纳了来自腾讯、百度、盛大等国内外云计算以及互联网领域的顶尖人才,具有丰富的云计算研发和运营经验。我们崇尚专注、快速的互联网文化,坚持深入了解用户需求,具有非常强烈的用户服务意识。UCloud总部设在上海,北京、成都、广州均已设立分公司。",9,0,"published",30,0,"2019-12-30 00:15:19","2021-08-13 11:17:21","UCloud" -27,0,"苏宁","suning3685","upload/20150718/14371963226412.png","upload/20150727/14379748943219.png","upload/20150727/14379748974657.png","http://www.suningcloud.com/","025-66996699-880211",0,8,"苏宁创办于1990年12月26日,是中国商业企业的领先者,经营商品涵盖传统家电、消费电子、百货、日用品、图书、虚拟产品等综合品类,线下实体门店1600多家,线上苏宁易购位居国内B2C前三,线上线下的融合发展引领零售发展新趋势。正品行货、品质服务、便捷购物、舒适体验。苏宁云商集团股份有限公司(SUNING COMMERCE GROUP CO., LTD.)原为苏宁电器股份有限公司(SUNING APPLIANCE CO., LTD.),2013年2月19日,公告称由于企业经营形态的变化而拟将更名。","苏宁创办于1990年12月26日,是中国商业企业的领先者,经营商品涵盖传统家电、消费电子、百货、日用品、图书、虚拟产品等综合品类,线下实体门店1600多家,线上苏宁易购位居国内B2C前三,线上线下的融合发展引领零售发展新趋势。正品行货、品质服务、便捷购物、舒适体验。苏宁云商集团股份有限公司(SUNING COMMERCE GROUP CO., LTD.)原为苏宁电器股份有限公司(SUNING APPLIANCE CO., LTD.),2013年2月19日,公告称由于企业经营形态的变化而拟将更名。",10,0,"published",100,1,"2020-03-23 11:58:10","2016-09-09 16:11:13","" -28,0,"阿里巴巴","alibaba4756","upload/20150702/14358216936167.png","upload/20150702/14358217003919.png","upload/20150702/14358217024938.png","http://www.aliyun.com","40010302",0,0,"阿里巴巴","##阿里巴巴",0,0,"published",9999,1,"2015-07-02 15:22:26","0000-00-00 00:00:00","" -29,6,"腾讯云计算(北京)有限责任公司","tengxunyun6275","201912/1577634226594032032.png","upload/20150726/14379177832092.png","upload/20160201/14543117853590.png","https://cloud.tencent.com/","4009100100",0,10,"腾讯云基于腾讯自身支撑海量业务的技术架构和运营经验,为广大企业和开发者提供云计算、云数据、云运营等一体化云端服务能力,助力企业建立灵活高效的IT架构、轻松连接未来。腾讯云提供安全可靠、稳定易用的云服务器、云存储、云数据库、CDN、域名注册等基础云服务以及腾讯云分析(MTA)、腾讯云推送(信鸽)等大数据运营服务。针对不同领域需求,推出一系列行业解决方案,例如游戏解决方案、移动应用解决方案、视频解决方案、金融解决方案等。腾讯云始终致力于打造最高质量、最值得信赖的公有云服务平台。","腾讯云致力于打造最高质量、最值得信赖的公有云服务平台。基于QQ、微信、QQ空间、腾讯游戏等海量业务的技术架构和精细化互联网运营经验,腾讯云为广大企业和开发者提供云计算、云数据、云运营等一体化云端服务能力,助力企业建立灵活高效的IT架构,轻松连接未来。腾讯云提供的产品安全可靠、稳定易用,包括云服务器、云存储、云数据库、CDN、域名注册(DNSPod)等基础云计算服务以及腾讯云分析(MTA)、腾讯云推送(信鸽)等大数据运营服务。针对不同领域独特需求,腾讯云还推出一系列行业解决方案,例如游戏解决方案、移动应用解决方案、视频解决方案、金融解决方案等等。",10,0,"published",2,0,"2015-07-02 15:23:32","2021-08-13 11:13:22","腾讯云" -30,24,"新浪","xinlang1947","upload/20150718/14371994733647.png","upload/20150718/14371995092744.png","upload/20150726/14379173193982.png","http://www.sinacloud.com/","010-62675882",0,0,"SAE作为国内的公有云计算,从开发伊始借鉴吸纳Google、Amazon等国外公司的公有云计算的成功技术经验,并很快推出不同于他们的具有自身特色的云计算平台。SAE选择在国内流行最广的Web开发语言PHP作为首选的支持语言,Web开发者可以在Linux/Mac/Windows上通过SDK或者Web版在线SDK进行开发、部署、调试,团队开发时还可以进行成员协作,不同的角色将对代码、项目拥有不同的权限;SAE提供了一系列分布式计算、存储服务供开发者使用.","SAE作为国内的公有云计算,从开发伊始借鉴吸纳Google、Amazon等国外公司的公有云计算的成功技术经验,并很快推出不同于他们的具有自身特色的云计算平台。SAE选择在国内流行最广的Web开发语言PHP作为首选的支持语言,Web开发者可以在Linux/Mac/Windows上通过SDK或者Web版在线SDK进行开发、部署、调试,团队开发时还可以进行成员协作,不同的角色将对代码、项目拥有不同的权限;SAE提供了一系列分布式计算、存储服务供开发者使用,包括分布式文件存储、分布式数据库集群、分布式缓存、分布式定时服务等,这些服务将大大降低开发者的开发成本。同时又由于SAE整体架构的高可靠性和新浪的品牌保证,大大降低了开发者的运营风险。",3,0,"published",80,0,"2015-07-02 15:24:44","2020-06-01 11:11:14","新浪" -31,29,"网宿","wangsu2506","upload/20150719/14372935512479.png","upload/20150719/14372935567304.png","upload/20150726/14379162423893.png","http://www.chinanetcenter.com/","400-816-8777",0,0,"网宿科技股份有限公司,成立于 2000 年 1 月,主要向客户提供全球范围内的内容分发与加速、服务器托管与租用、以及面向运营商的网络优化解决方案等服务,是中国领先的 CDN 及 IDC 综合服务提供商。2009 年 10 月,网宿科技在深交所上市。","##公司介绍 - -网宿科技股份有限公司,成立于 2000 年 1 月,主要向客户提供全球范围内的内容分发与加速、服务器托管与租用、以及面向运营商的网络优化解决方案等服务,是中国领先的 CDN 及 IDC 综合服务提供商。2009 年 10 月,网宿科技在深交所上市。 - -网宿科技总部位于上海,其在北京、上海、广州、深圳设立了 4 家分公司,在美国、香港、南京等地设立了多家子公司,并在厦门及美国硅谷建有研发中心。公司员工人数超过 1600 人,其中 50% 以上为技术及研发人员。其客户群覆盖各大门户网站、流媒体、游戏、电子商务等众多类型的互联网网站、政府、中大型企业以及运营商等。目前公司服务的客户近 3000 家,包括腾讯、百度、京东商城、中国网络电视台、人民网、华为、海尔、财政部、海关总署、银联、招商银行等。目前,网宿科技已经在中国建设 500 多个CDN加速节点,服务覆盖中国所有省市及地区;同时,网宿科技在北美、欧洲、非洲、中东以及亚太地区部署了 60 多个海外加速节点,帮助中国企业拓展海外市场。此外,网宿科技还在中国运营着 150 多个数据中心,在美国运营着4个数据中心。 - -## 网宿科技拥有多个行业第一: -* 网宿科技是中国 CDN 及 IDC 领域第一家上市公司; -* 网宿科技是中国第一家实现自主研发 CDN 的公司; -* 网宿科技是中国第一家实现了动态应用加速技术的公司; -* 网宿科技拥有中国最大的专业CDN平台,带宽高达 7T; -* 网宿科技是中国专业 CDN 行业盈利能力最佳的公司; - -",5,0,"published",140,0,"2015-07-02 15:26:38","2020-06-01 11:11:14","网宿" -32,0,"蓝汛","lanxun1718","upload/20150722/14375508499600.png","upload/20150722/14375508729584.png","upload/20150722/14375508773392.png","http://cloud.chinacache.com/","400120102",0,0,"蓝汛ChinaCache成立于1998年,是中国领先的互联网内容传输整体解决方案提供商,向客户提供全方位网络内容快速分布解决方案。作为首家获信产部许可的CDN服务提供商,蓝汛ChinaCache在全球120多个大中城市拥有超过28000台服务器, 为国内外超过3万家知名企业提供网络应用基础服务,业务遍及欧洲、亚洲、美洲和大洋洲。 -","蓝汛ChinaCache成立于1998年,是中国领先的互联网内容传输整体解决方案提供商,向客户提供全方位网络内容快速分布解决方案。作为首家获信产部许可的CDN服务提供商,蓝汛ChinaCache在全球120多个大中城市拥有超过28000台服务器,CDN网络覆盖中国电信、中国移动、中国联通、和中国教育科研网等各大运营商。 为国内外超过3万家知名企业提供网络应用基础服务,业务遍及欧洲、亚洲、美洲和大洋洲。 - -作为中国第一家完全拥有自主知识产权的CDN服务提供商,蓝汛ChinaCache目前拥有37项发明专利及23项软件著作权、并荣获国家科技进步发明奖二等奖。蓝汛ChinaCache和多个高校建立了研发中心(包括:北京邮电大学、西安交通大学、香港科技大学、硅谷研发中心等),2013年, 蓝汛ChinaCache与中国工信部电信研究院共建联合实验室,深化CDN领域合作研究。 - -2010年蓝汛ChinaCache面向全球正式发布新一代“内容感知网络服务”(Content Aware Network)通过终端感知、网络感知和应用感知系统,提供递送内容在终端、网络和应用之间的智能存储、分发与适配服务,使不同的行业客户需求在不同的应用场景下,都能为终端用户提供最佳的使用体验,提供云到端的一体化高性价比解决方案。 - -展望未来,蓝汛ChinaCache将一如既往地提供优质、高效的服务,秉承着“互联网改变生活,我们改变互联网”的信念,筑就中国互联网上最具规模的内容及应用分发平台,全力支撑全球互联网的发展。",3,0,"published",9999,0,"2015-07-02 15:29:16","2020-06-01 11:11:14","蓝汛" -33,41,"联想","lianxiang8767","upload/20150718/14372000526644.png","upload/20150718/14372000553442.png","upload/20150727/14379741692634.jpg","http://yun.lenovo.com","400-810-0688",0,0,"联想调频科技有限公司成立于2000年,由联想集团法人独资,注册资本1.1亿人民币,是联想集团提供云计算服务业务的法人主体。主要业务范围是互联网服务(包含云主机,云存储,企业网盘等相关业务),技术开发,技术服务等。 -其中云存储业务2008年开始技术布局,09年正式推向市场进行商用。企业网盘目前涵盖50多个行业,10万多企业用户,提供高效的企业数据分发汇总服务。联想云承担着5PB以上的企业数据,针对行业分为存储云、推送云、分发云、视频云、大数据汇集云,智慧环保云等。 -云主机业务2013年启动,2014年加入联想公司云服务业务集团,目前面向企业提供计算、网络、存储等服务。","联想调频科技有限公司成立于2000年,由联想集团法人独资,注册资本1.1亿人民币,是联想集团提供云计算服务业务的法人主体。主要业务范围是互联网服务(包含云主机,云存储,企业网盘等相关业务),技术开发,技术服务等。 -其中云存储业务2008年开始技术布局,09年正式推向市场进行商用。企业网盘目前涵盖50多个行业,10万多企业用户,提供高效的企业数据分发汇总服务。联想云承担着5PB以上的企业数据,针对行业分为存储云、推送云、分发云、视频云、大数据汇集云,智慧环保云等。 -云主机业务2013年启动,2014年加入联想公司云服务业务集团,目前面向企业提供计算、网络、存储等服务。",3,0,"published",190,0,"2015-07-02 15:30:24","2020-06-01 11:11:14","联想" -34,16,"中国电信集团有限公司","zhongguodianxin8633","202011/1604453205131697579.png","upload/20150726/14379178325164.png","upload/20150726/14379171981174.png","http://www.ctyun.cn","400-810-9889",0,34,"中国电信股份有限公司云计算分公司成立于2012年3月,是中国电信旗下的专业公司,是国内最大的云计算服务提供商,集约化统领中国电信全网包括IDC、CDN等在内的广义云业务。 -云公司依托中国电信覆盖全国、通达世界的通信信息服务网络和最大规模的互联网用户基础,集市场营销、运营、产品研发于一体,集约创新,为政府、企业和公众提供电信级、高可靠的云基础资源、云平台应用及云解决方案等产品和服务,致力于成为亚太用户最为信赖的首选云计算服务提供商。","中国电信股份有限公司云计算分公司成立于2012年3月,是中国电信旗下的专业公司,是国内最大的云计算服务提供商,集约化统领中国电信全网包括IDC、CDN等在内的广义云业务。 -云公司依托中国电信覆盖全国、通达世界的通信信息服务网络和最大规模的互联网用户基础,集市场营销、运营、产品研发于一体,集约创新,为政府、企业和公众提供电信级、高可靠的云基础资源、云平台应用及云解决方案等产品和服务,致力于成为亚太用户最为信赖的首选云计算服务提供商。 -云公司愿携手产业链,打造云计算生态环境,服务政府、企业和公众,全面提升社会信息化水平。在不远的将来,云公司将计算、存储和网络资源融入日常生产与生活,实现“宽带入户,云服务到家;终端在手,云服务随身”。",10,0,"published",3,0,"2015-07-02 15:31:16","2021-08-13 11:17:50","中国电信" -35,18,"中国移动通信集团有限公司","zhongguoyidong8569","upload/20150718/14372198313192.png","upload/20150718/14372198387271.png","upload/20150718/14372198437685.jpg","http://ecloud.10086.cn/?from=kexinyun.org","10086",0,11,"中国移动通信集团公司(简称“中国移动”)于2000年4月20日成立,注册资本512亿元人民币,资产规模超过万亿元人民币,拥有全球第一的网络和客户规模,2013年列《财富》杂志世界500强71位,品牌价值位列全球电信品牌前列。","中国移动通信集团公司(简称“中国移动”)于2000年4月20日成立,注册资本512亿元人民币,资产规模超过万亿元人民币,拥有全球第一的网络和客户规模,2013年列《财富》杂志世界500强71位,品牌价值位列全球电信品牌前列。 - -"移动云 "隶属于中国移动通信集团公司,是中国移动面向政企、事业单位、个人等客户推出的基于云计算技术、采用互联网模式、提供基础资源、平台能力、软件应用等服务的业务。现阶段移动云可提供IaaS(Infrastructure as a Service,基础设施服务)类型的云计算服务,移动云是建立在中国移动“大云”的基础上,自主技术研发而成的公有云平台,通过服务器虚拟化、对象存储、网络安全能力自动化、资源动态调度等技术,将计算、存储、网络等基础IT资源作为服务提供,客户根据其应用的需要可以按需使用、按使用付费。现在已推出云主机、云主机备份、弹性块存储、云存储、弹性公网IP、带宽出租、云防火墙、云监控等八种产品,涵盖了IT系统建设必需的计算资源、存储资源、网络资源、安全资源、运维资源。 - -移动云引入了大量云计算方面运营和运维优秀人才,利用自主研发的技术优势,构建了强大的运营和运维支撑团队,充分发挥中国移动现有的运营支撑网络优势,为客户提供售前支撑服务、售后服务和技术保障支持服务。 - -(1)售前支撑服务 - -移动云为客户提供完善的售前支撑服务,包括专属客户经理、方案评估等。 - -移动云客户可以通过18818813500服务台热线、客户经理、ecloud门户网站咨询等方式进行客户咨询。 -客户通过拨打18818813500服务台热线,咨询移动云业务相关问题。咨询热线的客户可以处理移动云业务种类、业务介绍、业务办理方式、缴费方式、联系方式等介绍性质的咨询。 - -客户也可以通过售前团队(提供5×8小时服务)进行方案评估咨询,移动云售前团队将解答客户提出的问题。 -客户还可登录ecloud门户网站,在线对移动云业务的相关问题进行咨询。移动云运营团队会第一时间查看到相关问题,并在规定时间内解答。 - -同时,中国移动VIP客户可通过客户经理预约标准机房参观,现场感受机房基础设施及管理运维能力。 - -(2)客户售后服务 - -移动云为客户提供完善的售后服务,包括7*24小时热线电话、快速投诉处理通道、安全高效的故障处理机制等。 -客户可以通过热线电话、客户经理、移动云平台网站投诉受理等方式进行客户投诉,移动会在规定时间内为客户解决问题。",8,0,"published",50,0,"2015-07-02 15:32:21","2021-08-13 11:18:18","中国移动" -36,17,"中国联通","zhongguoliantong8517","upload/20150719/14372911302615.png","upload/20150719/14372880111920.png","upload/20150719/14372880141334.jpg","http://www.wocloud.cn/","4000-111-000",0,0,"沃·云是中国联通自主研发的面向个人、企业和政府用户的云计算服务。“沃·云”业务实现了用户自己的信息/文件在多个设备上的协同功能和文件/资料的集中存储、安全保管功能。 -","##简介 -2013年12月12日在京召开的2013云世界大会上,中国联通正式发布旗下云计算业务品牌“沃云”。该业务面向集团行业客户、社会企业客户及公众客户提供云服务,表明国内通信运营企业正积极抢占“云计算”商机。 - -据介绍,中国联通推出的“沃云”服务包括企业云产品和公众云产品。企业云提供网络类产品、计算类产品、存储类产品和应用类产品;公众云产品包括云分享、云同步和云备份等。 - -中国联通总经理陆益民说:“云计算被认为是继个人计算机和互联网变革之后的又一次信息技术浪潮,对人类生活、生产方式和商业模式正带来深刻的改变。”他表示,云计算将成为信息技术领域的革命性创新和最具发展潜力的产业之一。 - - -##功能 -云同步:实现了自己的联系人/图片/视频/文件等,在自己多个电脑和智能手机上协同起来(即:可进行个人电脑与个人电脑之间、个人电脑与智能手机之间、智能手机与智能手机之间的同步)。 - -云备份:实现了自己在电脑、U盘、移动硬盘、存储卡、手机上大量和分散的文件、资料的集中存储、安全保管(可选加密保存)。 - -##特点 - -###免费10G空间 -沃·云用户注册即可获得10G永久免费空间。除此之外,用户可通过邀请好友等方式免费获得额外奖励空间。 - -###多终端交互 -沃·云实现了个人电脑、智能手机和平板电脑之间的文件和信息同步。同步内容包括:文件、通讯录、日历、任务、便签。内容自动同步到智能手机或平板电脑相应的应用中,使用方便。 - - -###跨操作系统 -沃·云实现了在Windows XP、Win7、iOS和Android平台之间的文件交互,是您轻松管理自己的文件和资料。 - -###手机文件备份 -沃·云独有的手机文件备份功能,是您通过手机也能将文件便捷地同步到电脑上,是真正全类型备份。 - - -##核心优势 - -###宽带接入 -沃·云平台通过大带宽云计算中心接入保障和用户固定宽带、WCDMA/HSPA+的高速访问,实现极速的“沃·云”业务体验。 - -###云中计算 -沃·云通过云计算的智能协同处理技术,实现用户的文件、信息的同步、分享和文件、资料的备份与管理。 - -###海量存储 -沃·云通过云计算的海量存储技术和1:3备份机制,为用户提供10G免费存储空间,同时还可按需实现动态、在线扩展存储空间。 - -###安全可信 -沃·云通过国有运营商电信级的服务保障,提供安全接入、文件加密和可信管理。",8,0,"published",60,0,"2015-07-02 15:33:05","2020-07-01 16:50:03","中国联通" -37,20,"北京金山云网络技术有限公司","jinshan5643","upload/20150719/14373067711596.png","upload/20150719/14373069369656.png","upload/20150719/14373067773545.jpg","http://www.ksyun.com/","400-028-9900",0,12,"金山云是金山集团旗下控股公司,依托金山集团26年的深厚技术积累,以业内领先的用户体验和服务端技术,为用户和企业提供国内领军级云服务产品。","##关于我们 - -金山云是金山集团旗下控股公司,依托金山集团26年的深厚技术积累,以业内领先的用户体验和服务端技术,为用户和企业提供国内领军级云服务产品。 - -公司拥有云主机、海量云存储、负载均衡、云关系型数据库等多项核心业务。其中,\""天蝎\""云主机以其高性能为特点,为客户带来极速体验,轻松应对高负荷业务;RDS产品的高可扩展性可帮助客户实现动态配置资源,以应对业务的剧烈变化;负载均衡产品能够帮助客户应对高峰流量和单点故障,大大提升业务的稳定性。 - -同时,金山云拥有服务于CP及渠道发行商的游戏云平台。该平台在游戏开发、测试、部署、运营,直至推广的全过程中,为游戏厂商提供了一体化的云计算解决方案。",5,0,"published",70,0,"2015-07-02 15:34:00","2021-08-09 15:34:19","金山" -38,26,"华为云计算技术有限公司","huaweiyun7385","upload/20150828/14407232293599.png","upload/20150718/14372006447311.png","upload/20150718/14372006508839.jpg","http://www.huaweicloud.com","4000-955-988",0,0,"华为云服务成立于2011年,隶属于华为公司。华为云服务在北京、深圳、南京、美国等多地设立有研发和运营机构,贯彻华为公司"云、管、端"的战略方针,汇集海内外优秀技术人才,专注于云计算中公有云领域的技术研究与生态拓展,致力于为用户提供一站式云计算基础设施服务,目标成为中国最大的公有云服务与解决方案供应商。华为云服务立足于互联网领域,依托于华为公司雄厚的资本和强大的云计算研发实力,面向互联网增值服务运营商、大中小型企业、政府、科研院所等广大企事业用户提供包括云主机、云托管、云存储等基础云服务、超算、内容分发与加速、视频托管与发布、企业IT、桌面云、游戏托管、应用托管等服务和解决方案。","华为云服务成立于2011年,隶属于华为公司。华为云服务在北京、深圳、南京、美国等多地设立有研发和运营机构,贯彻华为公司"云、管、端"的战略方针,汇集海内外优秀技术人才,专注于云计算中公有云领域的技术研究与生态拓展,致力于为用户提供一站式云计算基础设施服务,目标成为中国最大的公有云服务与解决方案供应商。华为云服务立足于互联网领域,依托于华为公司雄厚的资本和强大的云计算研发实力,面向互联网增值服务运营商、大中小型企业、政府、科研院所等广大企事业用户提供包括云主机、云托管、云存储等基础云服务、超算、内容分发与加速、视频托管与发布、企业IT、桌面云、游戏托管、应用托管等服务和解决方案。 - -"成就客户、开放进取"的理念深深扎根于我们的内心,华为云服务将秉承"为客户创 造最大价值"的使命,围绕客户的需求持续创新,致力于为客户提供有竞争力的产品和解决方案,持续提升用户体验。华为云服务坚持开放、分享、合作、共赢的理念,愿与广大合作伙伴、开发者共同创建和谐共生的云计算生态系统。",10,0,"published",110,0,"2015-07-02 15:34:47","2021-08-13 11:59:55","华为云" -39,21,"360","3695","upload/20161118/14794571906019.jpg","upload/20150719/14373016035083.png","upload/20150719/14373016069952.jpg","https://qiyun.360.cn/","4009191360",0,40,"奇虎360科技有限公司是中国领先的互联网和手机安全产品及服务供应商。据第三方统计,按照用户数量计算,360是中国前三大互联网公司之一。 -","#公司简介 -奇虎360科技有限公司(美国纽约证券交易所NYSE:QIHU,以下简称360)是中国领先的互联网和手机安全产品及服务供应商。据第三方统计,按照用户数量计算,360是中国前三大互联网公司之一。 - -##中国领先的互联网安全品牌 -360致力于通过提供高品质的免费安全服务,为中国互联网用户解决上网时遇到的各种安全问题。面对互联网时代木马、病毒、流氓软件、钓鱼欺诈网页等多元化的安全威胁,360以互联网的思路解决网络安全问题。360是免费安全的首倡者,认为互联网安全像搜索、电子邮箱、即时通讯一样,是互联网的基础服务,应该免费。为此,360安全卫士、360杀毒等系列安全产品免费提供给中国数亿互联网用户。同时,360开发了全球规模和技术均领先的云安全体系,能够快速识别并清除新型木马病毒以及钓鱼、挂马恶意网页,全方位保护用户的上网安全。 - -作为中国最大的互联网安全公司之一,360拥有国内规模领先的高水平安全技术团队,旗下360安全卫士、360杀毒、360安全浏览器、360安全桌面、360手机卫士等系列产品深受用户好评,使360成为无可争议的网络安全领先品牌。 - -据第三方数据统计,截止2013年6月底,360的PC端产品和服务的月活跃数达到4.61亿,360产品的用户渗透率达到96%;使用360手机卫士的智能手机用户总数已达约3.38亿,市场渗透率近70%;360浏览器的月度活跃用户达到3.3亿,用户渗透率近69%,在国产浏览器中处于领先地位;360个性化起始页和其子页面的日均独立访问用户为1.14亿人,日均点击量约为5.9亿次;360推出具有自主知识产权的搜索引擎服务,稳定拥有18%以上的市场份额,成为中国搜索市场的重要参与者,致力于共同建立一个安全的、有效竞争的搜索市场。 - -##中国互联网上的颠覆式创新者 -随着互联网的发展,安全问题成为互联网不得不面临的严峻挑战。根据全球反病毒产品测试中心(AV-Test.com)的数据,在1998年,安全厂商共收集到1,738种新特征的威胁样本。而在十年后的2008年,全球新增木马、病毒等恶意程序的数量超过千万,超过此前十年所有恶意程序总和。2009年7月4日,微软Mpeg-2漏洞引发的木马疫情开始集中爆发,国内每天超过2万个网站被黑客挂马,数量接近网站总数的百分之一。 - -在中国,流氓软件在2005年-2006年期间一度非常泛滥,严重影响到网民的利益。同时,随着互联网的发展,以谋取不法利益的木马地下产业链也获得快速发展,钓鱼欺诈网站等新型的不法行为也急剧增长。在互联网时代,网络安全不再仅仅是病毒。基于PC时代的安全理念已经越来越不适应互联网时代的发展。同样,随着互联网安全威胁的多元化,传统的网络安全模式难以为网民提供满意的安全保障,基于云计算的“云安全”模式势在必行。 - -360自2006年7月问世以来,用了五年时间,以颠覆式创新的模式,重新定义了互联网安全,改写了安全市场格局。 - -第一是商业模式的颠覆。360是免费安全的首倡者,主张安全作为互联网的基础服务,应该彻底免费;第二是安全技术的颠覆。360具有深厚的搜索引擎技术积累,将这些技术应用于安全领域,建成了目前为止规模、用户数、使用量均处于领先地位的云安全系统;第三,也是最重要的颠覆,是360用全新的用户体验重新定义了互联网安全。安全不等于杀病毒,在互联网时代它还意味着数据安全、隐私安全、账号安全、下载安全,以及电脑健康。",6,0,"published",150,0,"2015-07-02 15:35:42","2020-06-01 11:11:14","360" -40,130,"浪潮","langchao7721","upload/20150718/14372007165659.png","upload/20150718/14372007194384.png","upload/20150718/14372007268980.png","http://cloud.inspur.com","400-607-6657",0,0,"浪潮集团是国内领先的云计算与大数据服务商,综合实力位居中国IT企业前两位、中国自主品牌软件厂商第一位,是计算机信息系统集成特一级企业,国内第一、世界前五的服务器厂商。浪潮建设运营国内首家、亚洲最大的第四代数据中心;在全国建立北至哈尔滨、南至海南的16个云计算中心,参与了15个部委的云服务规划,并为国家质检总局、税务总局、统计局等部委提供大数据融合与分析服务。基于这些实践经验,浪潮实现中国政务云服务市场占有率第一,云计算版图扩至全国54个城市。","浪潮是中国领先的云计算整体解决方案供应商和云服务商,已经形成涵盖IaaS、PaaS、SaaS三个层面的整体解决方案服务能力,凭借浪潮高端服务器、海量存储、云操作系统、信息安全技术为客户打造领先的云计算基础架构平台,基于浪潮政务、企业、行业信息化软件、终端产品和解决方案,全面支撑智慧政府、企业云、垂直行业云建设。 -自2014年国内首家发布行业级云服务之后,这朵安全可靠的“浪潮云”,在市场风云瞬息万变的今天,正在被不断的考验与完善。在不断的探索与实践中,浪潮云服务实现了技术与业务中难点的逐个突破,使行业级云服务的创新型服务模式初具规模。而全面的软硬件产品、过硬的解决方案与可靠的运营服务,正是浪潮能够开行业之先河,敢于提出“一站式”云服务理念的重要支撑。 -这座坚实可靠的“铁三角”,源自于浪潮深耕中国IT市场多年,业务全面发展的多重累积。作为软硬产品“中国制造”的代表厂商,浪潮是目前国内4家特一级系统集成资质企业之一,拥有跨区域IDC/ISP牌照。这个“铁三角”组合,成为浪潮云服务持续和稳定开展的有力保障。 -截至目前,浪潮云服务已经为全国54个省、市、地区提供云服务,拥有北至哈尔滨,南至海南的十六大云计算中心,实力雄厚,值得托付。",4,0,"published",130,1,"2020-07-27 12:44:17","2020-07-27 10:55:29","浪潮" -41,27,"有孚网络","youwangluo2802","upload/20150718/14372010198266.png","upload/20150727/14379749429064.png","upload/20150718/14372010276839.png","http://www.yovole.com","400-720-0020",0,13,"上海有孚计算机网络有限公司创立于2001年,是国内领先的创新型高科技云计算服务商。公司以数据中心(IDC)整机房租用、服务器租用托管服务、基础服务(Iaas)、软件服务(Saas)等为主营业务。 -上海有孚计算机网络有限公司上海总部位于上海杨浦区国定路323号12楼。","阳光云是上海有孚计算机网络有限公司精心打造的专业云计算品牌,阳光互联的兄弟品牌,在阳光互联十年沉淀积累之下, 基于WEB服务,整合了计算、存储与网络资源的弹性云技术,专业互联网行业解决方案服务商。阳光云获得国家工信部首批认证的可信云认证。我们重点服务互联网领域有:电子商务、移动APP、在线教育、在线金融、在线游戏、在线医疗。围绕客户需求不断创新,打造高标准的7*24小时全天服务体系。",2,0,"published",9999,0,"2015-07-02 15:37:29","2020-06-01 11:11:14","有孚网络" -42,0,"企商在线","qishangzaixian9485","upload/20150702/14358279262012.png","upload/20150702/14358279308561.png","upload/20150727/14379750484891.jpg","http://www.xiangcloud.com.cn","400-018-0139",0,0,"象云(北京)网络技术有限公司(以下简称“象云”),由企商在线、新云东方等在云计算领域有着深厚积淀与独特优势的公司发起成立,2015年获国内知名投资公司九鼎投资(430719)增资。象云在全球率先采用OpenPOWER+X86双基础架构搭建企业级公有云,整合高端分布式数据中心、高性能云计算、可信安全技术和企业级云应用,专注企业级公有云与混合云产品。","#一、象云简介 <br/><br/> - -大象,习性温和,寿命长,喜欢群居,既稳重而又憨态可掬,是陆上最大的动物,也是许多国家奉承的忠实可靠的生物象征。云计算,大容量,集群运算,动态弹性扩展且稳定可靠,是当代IT技术的象征符号。盲人摸象的故事诠释了大象和云计算类似的大体态特征。同时大象的动物属性和品质,也是云计算诚实可信的服务品质的绝佳比拟。<br/><br/> - - -象云(北京)网络技术有限公司(以下简称“象云”),由企商在线、新云东方等在云计算领域有着深厚积淀与独特优势的公司发起成立,2015年获国内知名投资公司九鼎投资(430719)增资。<br/><br/> - -象云核心团队是秉承了大象品质,在IaaS层耕耘了十七年,在国内最早从事云计算、IDC领域,立志打造出忠实可靠云计算商业生态服务体系的一群人。<br/><br/> - -象云在全球率先采用OpenPOWER+X86双基础架构搭建企业级公有云,整合高端分布式数据中心、高性能云计算、可信安全技术和企业级云应用,专注企业级公有云与混合云产品。象云,作为工信部可信云认证企业,致力于打造高性能企业级的IaaS平台,是您身边可以信赖的云计算合作伙伴。<br/><br/> - - -#二、象云文化 <br/><br/> - -大象,习性温和,喜欢群居,既稳重而又憨态可掬,寿命长,是许多国家奉承的忠实可靠的生物象征,是陆上最大的动物。盲人摸象的故事诠释了和云计算类似的大体态特征,其动物属性和品质,比拟云计算诚实可信的服务品质。 -云计算,大容量,集群运算,动态弹性扩展且稳定可靠,是当代IT技术的象征符号。象云来自一群秉承大象性格在IaaS层耕耘了17年,立志在云计算领域打造出忠实可靠云计算商业生态服务体系的一群人。<br/><br/> - - -#三、象云基因 - -![象云基因](http://static.kexinyun.org/upload/manu/feixiang.png) - - -#四、象云愿景 <br/><br/> - -愿景:成为中国云计算企业级云服务应用领域的领军者 <br/><br/> -##1.立足IaaS资源:<br/> - -整合十七年IDC和云计算的IaaS资源,为行业用户提供弹性可扩展的基于公有云的混合云服务<br/><br/> - -##2.整合三大行业应用:<br/> - -针对泛金融、政企、移动行业合云服务,让用用变得更加有效率,让应用更具性价比。<br/><br/> - -##3.打通云计算通道:<br/> - -我们坚信云本来没有界限,只有服务。象云将模糊当下IaaS和SaaS的界限,缩短云成长周期。<br/><br/> - - - -#五、象云产品 - -##1.计算与网络 - -###1.1云主机 -云主机服务是云计算中最基础的服务,象云云主机基于Power和x86双构架,不仅可以为客户提供基于x86构架的Windows、Linux主机,同时也可以提供基于Power构架的高性能的Linux以及AIX主机。为行业客户提供更加高性能、安全可靠的云主机选择 。象云云主机可以实时修改网络带宽、硬盘,CPU/内存等主机配置 , 也可以通过VNC终端在无网络的情况下管理虚拟机。在象云中,创建和销毁一台云主机只要不到30秒钟,让用户真正享受云计算带来的秒级响应,弹性资源调度的便利。 - -###1.2负载均衡 -象云网是国内首家采用F5 BIG-IP LTM系列负载均衡器以服务的方式提供给客户使用的IDC运营商,该产品可极大程度的降低客户的项目启动成本,提高业务可用性。 - -###1.3网络 -象云网网络是实现对网络资源的管理,包括公网IP,带宽,带宽包;以及对网络安全访问的管理,如防火墙策略。 - -###1.4数据中心 -目前象云拥有晓月创新云计算基地、广渠门数据中心、华威桥数据中心、京东数据中心、京西数据中心,5个自建运营数据中心以及15个运营商合作数据中心和6个海外数据中心,资源遍布全球。 - - - -##2.存储与CDN - -###2.1云存储 -象云云存储一款分布式、高性能、高可用的Key-Value内存存储,兼容常用的Memcache/Redis协议,主要用作持久化数据的存储,也可以用作缓存数据的存储。 - -###2.2云备份 -灾难发生时,信息技术人员与时间竞赛,把握每分每秒恢复数据。此时,每秒之差亦可导致数以百万元的损失、有损公司声誉甚至是影响业务存亡。象云特推出一站式多功能在线云备份方案,将您的重要数据远端存储于安全的环境,并可随时随地实时恢复数据。 - - -象云云备份针对企业用户的数据资料备份与恢复问题提出解决方案,就算没有专业的IT管理人员,员工只需要安装一套简单的软件客户端于电脑或服器务内,企业员工或系统管理员便能即时把重要商业数据通过互联网备份到远端的IDC数据中心。同样也可以轻松的将数据通过客户端软件进行还原。也可以藉由web介面,简单易用的方式,将所需要还原的备份数据集,透过搜寻的方式找到并还原。中间的过程完全不需要麻烦IT人员,使用者自己就可以轻松得进行数据还原,不但可以提高使用者的生产力,也减少IT管理人员的繁琐管理工作。云备份将原有的备份形势完全取代,取而代之的是尖端的数据备份理念和全自动化,全智能化,策略化,多层数据安全加密手段的数据备份解决方案。 - - -象云云备份平台除了满足传统的文件级备份以外,还具有主流数据库,邮件服务器,甚至系统级的备份。象云不光专注于为客户提供高端的云主机服务,更为客户的数据安全提供特有解决方案。同时备份文件可以通过网络或者存储介质进行拷贝或者迁移,并且可以在异地云平台上快速恢复,还可以为用户快速构建异地灾备系统。 - -###2.3云CDN -随着互联网用户的激增,网站越来越难以保障用户的访问质量,进而造成用户流失影响公司业务。CDN的全称是Content Delivery Network,即内容分发网络,是位于网络层与应用层之间的网络应用,其目的是通过在现有的Internet中增加一层新的网络架构,通过智能的中心管理系统,使用广泛分布的服务器群来帮助网站满足互联网用户的各类需求。从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均等原因造成的用户访问网站响应速度慢的问题。 -象云经过数年研发合作,成立了专业CDN服务平台,向客户提供全方位网络内容快速分布解决方案。目前象云在全国30多大中城市拥有近百个节点,覆盖中国电信、中国联通、中国移动、中国教育科研网等各大运营商,同时通过和海外CDN服务商的合作,在海外有节点分布,提供高质的全球网站加速服务。 -象云CDN独有的优势是:为每个客户提供独立的资源环境和服务器环境,做到真正的应用独享;对客户的特殊需求具有更灵活的"适配"能力,以满足客户多样化的配置要求;独立的资源环境能带来更高的安全性,如当其他的客户发生攻击时,不会影响到您的客户体验。 -象云CDN平台具有充足的资源,跨6大运营商网络(中国电信、中国新联通、中国移动、中国铁通、中国卫通、中国教育网)的节点部署,并且服务节点中包含6个运营商网络汇聚的BGP资源,无论源站使用哪个运营商的网络,都可以非常快速的进行内容分发。 -同时,按需随取的带宽扩充机制和能力,使得CDN系统能应对大规模的突发流量,确保用户对网站访问的客户体验。象云CDN为每个客户指定专门的客服人员,及时专业地解决客户问题。象云CDN运维中心可全天候对CDN节点网络链路状况、节点设备运行状况、客户网站服务响应、客户源站点网络状况等进行实时的监控和管理,有效保障了CDN平台的运营质量。象云的客户服务系统提供完善准确的增值服务,通过实时的数据分析对客户直播应用品质的控制提供有效支持。 -象云CDN客户提供具有"带宽流量查询""PV数查询""频道点击查询""日志详细分析""加速信息管理""日志下载"等多项功能的CDN客户后台管理系统平台。标准服务包括:带宽流量查询、加速频道情况查询、PV数查询、缓存命中率查询和状态返回码等五部分功能。增值服务包括:用户统计分布(省份)、用户统计分布(运营商)、网页热点统计、网站引用统计、访问者统计等。 - - - - -##3.安全与监控 - -###3.1云监控 -服务器出现故障、应用服务出现故障或者CPU以及内存等资源持续使用较高的现象在互联网应用中难以避免,既然无法绝对避免,那么实时了解所有服务器状态,做到防患于未然,就非常重要,以免在故障来临之时措手不及。象云提供的监控服务无需用户投入大量软硬件,也无需投入专业的IT人员,只需要将要监控的服务器IP地址输入到监控平台,并设置要监控的范围(如CPU、内存、磁盘IO、网络流量等资源以及Apache、Nginx、Mysql、IIS、SQL Server等应用),即可对目标资源以及应用进行监控,当目标监控设备或者应用符合报警条件时系统将通过邮件和短信通知管理员,管理员收到告警后可针对服务器或者应用检查具体原因并排除,有效的将故障扼杀到摇篮中。 - -###3.2云安全 -大多数用户在安全方面有个误区,认为购买一台硬件防火墙就万事大吉了,然而当业务刚刚走上正轨即出现数据丢失、程序篡改、网页被挂病毒代码等现象,访客对网站主办者失去信任,前期大量投入付诸东流。任何程序都做不到完美无瑕,多少都会有些漏洞,即便程序没有漏洞,操作系统版本漏洞、web服务的版本漏洞也会给黑客可乘之机。象云采用的WAF (web防火墙)专门针对http与https协议进行拆包检查,对已知漏洞的攻击进行阻断,对sql注入、跨站脚本以及操作系统攻击、web服务漏洞攻击等常见的99%的攻击类型有效的防护,可最大程度保证业务正常运行。 - -企业用户对于核心业务数据非常的敏感,而云计算往往会放大用户对系统安全问题的焦虑。因此无论从数据中心的物理安全,以及云平台的应用安全,都是企业级用户非常关注的,如何能够做到可信、物理溯源、逻辑隔离与物理隔离是企业级IaaS服务商必须面对的问题。 - -象云的云安全产品与知名的安全厂商Imperva 和信诺瑞得合作,不仅能够提供DDoS防护,主机入侵防护,以及漏洞检测、木马检测等一整套安全服务; 同时还能提供基于数据安全防护和高性能审计能力。 - -###3.3云邮箱 -建立于象云计算平台之上,采用Microsoft Exchange/Outlook协作套件为客户提供优质邮箱的一项服务。作为云计算SaaS服务的一种解决方案,客户无需额外购买硬件和设置。 - - - -#六、象云优势 -##1.简单高效的控制平台 - -轻松部署、创建、调度资源,实现实时有效管理、监控,减少IT运维成本 - -可视化的监控平台,IaaS资源(计算、存储、网络)实时监控 - -完善的操作记录,快速定位查找 数据安全 - -安全便捷的交易与支付平台 - -轻松便捷的远程控制平台(VNC) - - - - -##2.弹性云计算 灵活调度资源 -弹性云计算是基于云数据中心基础服务设施及专业服务能力,向客户提供按需使用IT基础资源能力(计算、存储、网络等)的IT整体服务 - -计算、存储、网络等资源弹性可伸缩,实时创建,即刻使用 - -自定义配置,满足业务发展各种可能,实现业务无限扩容 - -根据业务发展变化,随时在线进行云主机升级、扩容 - -保证业务不间断持续性增长 - -包月、包年灵活付款模式,即买即用 - -##3.强大的负载均衡能力 - -负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性 - -大量并发访问或数据流量实现有效分摊,缩短等待响应时间 - -提高系统处理能力 - -自动检错,智能化剔除不可用主机,打造高效的业务能力 - -任何一个主机或集群发生故障或堵塞,在极短时间内实现数据备份 - -##4.高品质的私有云服务 - -通过 SDN 实现的虚拟路由器和交换机,您可以快速搭建属于自己的私有云环境,私有网络与基础网络2层隔离,保证数据安全性 - -智能化系统,快速搭建私有云 - -私有网络与基础网络2层隔离,保证数据安全性 - -除缺省防火墙外,还可根据客户需要进行定制化部署 - -安全隧道连接,形成功能强大的混合云 - -##5.虚实结合 - -独有的虚实结合技术,将云计算和物理设备完美的结合起来,既有效保护企业已有硬件投资,又可以让企业拥有云计算的便利。 - -###5.1 灵活的虚实结合方案,让客户的已有硬件投资得到充分利用 - -###5.2前端使用云主机,充分享受负载均衡、弹性计算的便利性;后端使用物理服务器,保证服务的稳定性和安全性。 - -##6.可靠的安全保障 - -实现云计算数据中心物理安全,通过系统加固、防病毒等手段实现云环境的安全,以及通过网络隔离、攻击防护、传输安全等大大提高数据及资源的安全性 - -数据系统级备份,对数据实现双重保护 - -缺省及定制化防火墙,防DDos攻击、ARP攻击、MAC欺骗等 - -资源隔离、加密技术等多种防护手段应用 - -立体化监控,智能化分析,实时报警机制 - -##7.多数据中心全球分布及优质的服务 - -遍布全球的数据中心分布,为云计算的部署提供得天独厚的优势,助力象云成为业界翘楚 - -丰富的资源池,实现分布式部署,数据可靠性为99.95% - -BGP接入,实现多个运营商不同地点快速访问 - -专业的运维管理团队实现全天候服务 - - -",1,0,"draft",9999,0,"2015-07-02 15:38:07","2020-06-01 11:11:14","企商在线" -43,15,"北京百度网讯科技有限公司","baiduyun6887","upload/20170428/14933663102153.jpg","upload/20150726/14379178816603.png","upload/20150726/14379159873774.png","http://bce.baidu.com","无",0,0,"百度开放云提供直达号、网站服务、数字媒体等解决方案为您的应用顺利开发、部署、测试、上线提供一系列的强有力支持和易用可扩展的多种功能。","1、技术领先:15年国际领先技术积累承载超过二十款过亿用户产品支撑百度每天60亿次用户点击; -2、生态支持:云计算全系列产品完整体系凤巢,网盟,直达号百度生态支持产品服务自由组合,灵活应用; -3、性能卓越:自主化一键快速批量部署、秒级响应BGP多线网络,万兆内网极速互联万量级IOPS满足极限存储需求CPU内存资源真实供应无超售。",4,0,"published",90,0,"2015-07-02 15:39:02","2020-06-01 09:58:56","百度智能云" -44,118,"甘肃移动","gansuyidong7617","upload/20150728/14380562746259.png","upload/20150728/14380562762402.png","upload/20150728/14380562835423.png","http://wwww.xb-cloud.cn","10086",0,15,"中国移动通信集团甘肃有限公司成立于1999年9月8日,隶属于中国移动通信集团公司(中央直属国有特大型骨干企业),具有独立的法人资格。 - ","中国移动通信集团甘肃有限公司成立于1999年9月8日,隶属于中国移动通信集团公司(中央直属国有特大型骨干企业),具有独立的法人资格;在甘肃省专注于移动通信网络的建设维护、业务开发,通信产品的市场经营、信息化服务等现代通信业务。公司下设14个市州分公司、4个直属单位和70个县(市)公司。主要产品和服务内容有移动话音、数据业务和传真、IP电话、多媒体业务,并具有计算机互联网国际联网单位经营权和国际出入口局业务经营权;拥有“全球通”、“神州行”、“动感地带”等服务品牌,经营GSM(GPRS、EDGE)、TD-SCDMA、TD-LTE网络,服务网号码为134(1347除外),135,136,137,138,139,147,150,151,152,157,158,159,182,183,184,187,188。 - ",4,0,"published",9999,0,"2015-07-02 15:39:49","2020-06-01 11:11:14","甘肃移动" -45,0,"苏州国科","suzhouguoke3072","upload/20150702/14358227995195.png","upload/20150702/14358228018015.png","upload/20150702/14358228033705.png","http://www.sisdc.com.cn/","400101010",0,0,"苏州国科","#苏州国科",0,0,"published",9999,1,"2015-07-02 15:40:25","0000-00-00 00:00:00","" -46,28,"万国数据","wanguoshuju7547","upload/20150718/14372016752683.png","upload/20150727/14379739961401.png","upload/20150727/14379740013317.png","http://www.gds-services.com","4006-911-911",0,16,"万国数据服务有限公司( GDS )成立于2000年, 是国内领先的高可用云计算与数据中心服务提供商,拥有世界级新一代数据中心与高可用IT服务经验及卓越的云端产业链整合能力。万国数据业务涵盖从数据中心设计、建设和运营,到数据中心托管,IT管理运营外包,业务连续性管理及灾难恢复的整合解决方案、咨询、服务和培训,及面向企业级市场的高可用云计算服务。万国数据始终关注客户面临的挑战,提供安全可靠和灵活高效的高可用IT服务,助力客户安全运营。目前已广泛服务于银行、保险、证券、能源、制造、物流、 互联网等多个行业及政府机构。","万国数据服务有限公司( GDS )成立于2000年, 是国内领先的高可用云计算与数据中心服务提供商,拥有世界级新一代数据中心与高可用IT服务经验及卓越的云端产业链整合能力。万国数据业务涵盖从数据中心设计、建设和运营,到数据中心托管,IT管理运营外包,业务连续性管理及灾难恢复的整合解决方案、咨询、服务和培训,及面向企业级市场的高可用云计算服务。万国数据始终关注客户面临的挑战,提供安全可靠和灵活高效的高可用IT服务,助力客户安全运营。目前已广泛服务于银行、保险、证券、能源、制造、物流、 互联网等多个行业及政府机构。",1,0,"published",170,0,"2015-07-02 15:42:43","2020-06-01 11:11:14","万国数据" -47,0,"上海天云","shanghaitianyun7588","upload/20150718/14372019222469.png","upload/20150727/14379753829066.png","upload/20150727/14379753787294.png","http://www.tichazuo.com","021-61230566",0,17," 上海天云科技公司由亚信集团创始人田溯宁博士所创立的中国宽带产业资本投资,在上海、北京、香港等省市设立了分支机构,并在美国硅谷拥有研发中心。 - 上海天云科技专注于中国云计算产业的发展和“中国云”的构建,并致力成为国内领先、具有世界水准的云计算综合解决方案供应商、IT基础设施云服务提供商。"," 上海天云科技公司由亚信集团创始人田溯宁博士所创立的中国宽带产业资本投资,在上海、北京、香港等省市设立了分支机构,并在美国硅谷拥有研发中心。 - 上海天云科技专注于中国云计算产业的发展和“中国云”的构建,并致力成为国内领先、具有世界水准的云计算综合解决方案供应商、IT基础设施云服务提供商。",0,0,"published",9999,1,"2015-07-02 15:44:27","2015-07-27 13:36:23","" -48,40,"迅达云成","xundayuncheng4336","upload/20150719/14373093171203.png","upload/20150719/14373093197349.png","upload/20150719/14373093212709.jpg","http://www.speedycloud.cn/","4006911816",0,18," SpeedyCloud是业界领先的云计算服务提供商北京迅达云成科技有限公司旗下品牌,专注于为企业提供互联网及企业IT相关基础设施云服务。公司成员绝大多数来自于蓝汛、世纪互联等IT运维服务型上市企业, 利用最新的云计算及互联网技术帮助客户迅速扩展在线业务。 -","SpeedyCloud是业界领先的云计算服务提供商北京迅达云成科技有限公司旗下品牌,专注于为企业提供互联网及企业IT相关基础设施云服务。公司成员绝大多数来自于蓝汛、世纪互联等IT运维服务型上市企业, 利用最新的云计算及互联网技术帮助客户迅速扩展在线业务。 - - SpeedyCloud迅达云产品覆盖计算、网络、存储三大IT基础设施领域,包括:云主机、云存储、云分发、SDN、负载均衡、全局智能调度、RDS数据库等。 - - SpeedyCloud节点部署遍布全球,欧美地区13个节点;美国拥有14个IDC机房,总面积达18万平方米,和美国274个网络运营商互联互通,欧洲总面积2万平方米,运营逾4000个机柜。 - - 国内节点部署覆盖北、上、广及国内大部分一二城市,提供BGP优质线路。云分发服务覆盖全国50多个城市,带宽储备150G。 - -迅达云成建立了一套以IaaS基础设施云服务为基础的开放式的运维解决方案,包括了数据中心管理、设备租赁、云主机、存储、应用商店等模块。我们致力于互联网及企业IT运维技术和服务的创新和平台化运营。其中,公司的核心产品—游戏云平台服务,已经得到业界多家有影响力的游戏运营商的广泛认同。 - -作为一套成熟的云计算解决方案,迅达云成平台的每一次改进都引领着中国互联网基础设施服务的智能化和开发平台化的潮流。迅达云成和合作伙伴一起建立的生态系统,正在成为中国互联网基础设施服务的事实上的标准。 - -此外,迅达云成还为开发者和运维管理人员推出了云API,让开发者拥有了使用程序代码就可以驾驭分布式云系统的能力,开发者可以通过我们的云平台,搭建具有高度伸缩性的、自动化运维的、分布式的动态应用系统。 - -作为有志于在IaaS或者PaaS领域发展的创业者、开发者或者开发团队,也可以向我们申请免费的云API账号及资源。如果其开发出的应用或者工具符合我们迅达云成的接口标准,经审核后,我们会放入open.speedycloud.cn应用商店,我们负责在线推广,让更多的客户使用,和大家一起共建我们的云生态链。 - -迅达云成还为SaaS应用开发商、电商、及移动互联网团队提供丰富的运维工具及服务。我们愿意竭尽全力帮助研发和创意高手搞定需要日常运维和资源扩展的常见问题。这样的问题本身和业务的功能性无关,但是往往反复出现,所以需要一定的运维经验。我们愿意用我们团队十多年的运维经验帮助客户解决这些必须解决的成长的烦恼。 - -迅达云成,让云服务加速您的成功(SpeedyCloud,Speed up Your Success)! - -公司理念:让互联网业务化繁为简!",2,0,"published",180,0,"2015-07-02 15:45:21","2020-06-01 11:11:14","迅达云成" -49,0,"海南易建","hainanyijian5699","upload/20150702/14358275081903.png","upload/20150727/14379754519552.png","upload/20150702/14358275323533.jpg","http://www.eking-tech.com/","95071000",0,35,"海南易建科技股份有限公司(简称:易建科技;股票代码:831608)是一家信息技术综合服务企业,公司成立于1998年3月,拥有员工近700人,总部位于海南海口,并在北京、上海、厦门、西安等地设有研发中心。2014年12月31日,易建科技正式在全国中小企业股份转让系统挂牌,成为海南首家挂牌新三板的高科技企业。 -","#公司介绍 <br/><br/> - -易建科技汇集了一批精通国内外主流软硬件产品的技术专家及经验丰富的行业专家,秉承“客户第一、创新为先、诚信负责”的企业价值观,以前端智慧科技与后端云计算基础架构相结合,通过持续的IT服务,全面提升客户IT投入的商业价值。 <br/><br/> - -通过执著追求与不懈努力,易建科技赢得了业界的广泛认同和赞誉,获得多项国家科技部、工信部、住建部、保密局所颁发的行业资质,通过了CMMI 3级国际认证;取得中国十大智能建筑系统集成商、中国优秀智能建筑管理系统品牌、中国建设工程鲁班奖等国家级荣誉,荣获全国百项建筑智能化经典项目及中国安防百强企业等行业高端奖项。 <br/><br/> - -凭借雄厚的企业实力,易建科技与惠普、IBM、微软、ORACLE、用友、华为、AVAYA、H3C、宇视科技、中国移动、中国电信、中国联通、神州数码等众多国内外知名企业建立了良好的战略合作伙伴和合作关系。 <br/><br/> - -海南易建科技股份有限公司相信,信息技术的革命和进步为中国带来了新的历史性机遇,公司将锐意进取、开拓创新,致力为政府与企业客户提供行业领先的信息技术综合服务,以IT技术和智慧不断推动社会变革。 - <br/><br/> -",1,0,"published",9999,0,"2015-07-02 15:46:16","2020-06-01 11:11:14","海南易建" -50,22,"中兴","zhongxing7991","upload/20150722/14375415642722.PNG","upload/20150727/14379758242844.png","upload/20150727/14379758302617.png","http://www.zte.com.cn/cn/","4001011010",0,0,"深圳市中兴云服务有限公司是中兴通讯全资控股子公司,以中兴通讯云服务中心为前身,基于八年服务运营的经验积累,孵化形成新的中兴云服务公司。","深圳市中兴云服务有限公司是中兴通讯全资控股子公司,以中兴通讯云服务中心为前身,基于八年服务运营的经验积累,孵化形成新的中兴云服务公司。公司于2013年初成立于深圳前海,注册资金5000万元。中兴云服务有限公司现有员工近千人,主要分布在南京、深圳、西安等地,逐步形成以呼叫云、财务云、企业云、知识云等为特色的系列化云服务产品,专注于为政府行企客户提供运营支撑服务。业务范围覆盖企业互联、数据中心、大数据平台、数据挖掘、智能分析、电子政务、呼叫平台、视频会议、智慧城市等领域,旨在开拓大数据、云平台及云服务市场,打造中国云服务产业领先企业。 -",1,0,"published",9999,0,"2015-07-02 15:46:57","2020-06-01 11:11:14","中兴" -51,0,"首信","shouxin2414","upload/20150718/14372020577436.png","upload/20150718/14372020607980.png","upload/20150727/14379761056504.jpg","http://www.capinfo.com.cn","88511155",0,19," 首信云平台于2012年建成,并成为工信部、北京市第一批电子政务公共服务平台试点示范项目,为北京市各市级政务部门提供云服务。 - 首信云具有安全、稳定、灵活、高效的特点,其“电子政务云基础设施”与“电子政务云管理平台”均已通过公安部信息系统安全等级保护三级认证。云平台充分发挥云计算服务资源集约化、技术标准化、能力服务化、提供快速化、资源弹性化、管理自动化、管控集中化的优势,为用户提供立体化的云服务。首信云已为30余家市级部门,包括北京市市委组织部、北京市财政局、北京市经信委、北京市计生委等提供云服务,涉及高并发、大流量、大存储、移动互联、综合门户、公共服务等多种典型应用。 -","首都信息发展股份有限公司(简称:首信公司)其前身首都信息发展有限公司成立于1998年1月,围绕数字北京重要基础设施和重大应用工程开展工作。2000年7月,公司完成股份制改造,正式更名为首都信息发展股份有限公司。2001年12月21日,公司成功在香港联交所创业板挂牌上市,股票代码8157。2011年1月21日,公司成功转至香港联交所主板上市,股票代码1075。在2003年抗击非典、2006年中非论坛、2008年北京奥运会、2009年国庆六十周年、2010年上海世博会、2012年十八大及北京市党代会、2014年中国APEC峰会等重大突发事件和活动保障中,首信公司更是树立了“职业精神为本,客户利益至上”与时俱进的新国企形象! - 随着全国化市场战略的不断推进,首信公司服务地域遍布全国,业务范围涵盖电子政务、医疗卫生、住房公积金、市政管理、公共安全、企业信息化应用等多个领域。公司定位于互联网、云计算时代的智慧城市运营与便民服务商,全力打造智慧城市系列解决方案、云服务产品及“互联网+”服务平台,包括医疗、城市管理和服务、民生、城市门户、CAPINFO云服务、物联网和城市感知中心,置身于互联网、大数据、物联网、云计算、医疗信息化、企业信息化等高增长行业及领域。在智慧城市与互联网时代的发展进程中与每一座城市、每一位客户、合作伙伴及生活在城市中的每一个人并肩前行!",1,0,"draft",9999,0,"2015-07-02 15:47:30","2020-06-01 11:11:14","首信" -52,31,"中企通信","zhongqitongxin5249","upload/20150718/14372021694828.jpg","upload/20150727/14379763339422.png","upload/20150727/14379763361034.png","http://www.china-entercom.com","400 884 1883",0,14,"中企网络通信技术有限公司(简称“中企通信”)是中信集团旗下的一家子公司,为企业提供最可信赖及全面的信息通信技术(ICT)供应商,拥有良好的业界口碑、强大的股东背景、覆盖广泛的网络、多元化的客户资源和世界级的客户服务能力。公司拥有信息产业部第一类、第二类多项增值电信业务经营许可执照,是增值服务运营商中首家拥有全国IP VPN经营牌照的企业,同时是中国首家通过TL9000认证的增值服务商。中企通信凭借多年的运营及与世界级伙伴的合作经验,致力于为客户与合作伙伴提供高端通讯服务及资讯科技解决方案,是企业及商业伙伴最可信赖的合作伙伴。","中企网络通信技术有限公司(简称“中企通信”)是中信集团旗下的一家子公司,为企业提供最可信赖及全面的信息通信技术(ICT)供应商,拥有良好的业界口碑、强大的股东背景、覆盖广泛的网络、多元化的客户资源和世界级的客户服务能力。公司拥有信息产业部第一类、第二类多项增值电信业务经营许可执照,是增值服务运营商中首家拥有全国IP VPN经营牌照的企业,同时是中国首家通过TL9000认证的增值服务商。中企通信凭借多年的运营及与世界级伙伴的合作经验,致力于为客户与合作伙伴提供高端通讯服务及资讯科技解决方案,是企业及商业伙伴最可信赖的合作伙伴。",4,0,"published",9999,0,"2015-07-02 15:48:10","2020-06-01 11:11:14","中企通信" -53,0,"搜狐","souhu5035","upload/20150719/14372982941049.png","upload/20150719/14372983001323.png","upload/20150719/14372983098809.jpg","https://cs.sohu.com/","010-56602900",0,0,"2014年,搜狐云台厚积薄发,开始对外提供云服务,用我们的经验解决用户与数据存储相关的困扰,让用户更有精力专注于自己的特长与成长。","![图片描述](http://static.kexinyun.org/upload/2015/07/19/about-us-bgagPFy.jpg) - -**2010年**,搜狐研究院为解决搜狐内部的海量数据存储问题,满足搜狐内部的需求,开始研发搜狐云台。 - -**2012年起**,搜狐内部的相册、视频图片、新闻客户端图片、博客、大视野、微博、白社会等图片存储应用,邮箱及网盘等文件存储已迁移到搜狐云台上,搜狐内部实现了 One Sohu One Storage。搜狐云台提升了搜狐内部服务图片相关业务的研发速度,服务质量,服务性能,且降低了存储处理成本。 - -**2014年**,搜狐云台厚积薄发,开始对外提供云服务,用我们的经验解决用户与数据存储相关的困扰,让用户更有精力专注于自己的特长与成长。目前,搜狐云台已经接入包括赶集网,马蜂窝,当当网,酒仙网,无双科技在内的近多家大型互联网产品。我们专注于企业级云存储,致力于打造互联网健康云生态环境,为企业定制一站式云存储、数据处理与加速解决方案。",1,0,"draft",9999,0,"2015-07-02 15:48:51","2020-06-01 11:11:14","搜狐" -54,42,"七牛","qiniu5811","upload/20150718/14372025145020.png","upload/20150727/14379745319836.png","upload/20150727/14379745357734.png","http://www.qiniu.com","400-808-9176",0,20,"上海七牛信息技术有限公司由国内云存储领域领军人物之一许式伟于2011年创立,专注以数据为中心的公有云服务市场,目前已获得经纬、启明、宽带资本3轮投资。公司总部位于上海,在北京、深圳、杭州、成都、合肥均有本地团队。公司核心团队在海量存储等领域拥有超过十年的技术积累,核心技术完全自主研发。目前客户已经覆盖互联网、移动互联网、传统企业的数十万家,其中包括中兴、步步高、海康威视、美图秀秀、陌陌、大众点评、OPPO等知名企业。","公司主营产品为七牛云存储。我们的世界可理解为由各类数据以及围绕数据的计算所构成。在互联网+时代,七牛云存储依托于本身的技术积累与创新,从数据应用场景出发,从数据的收集、托管、处理、分析,到传输加速,通过一站式的数据管理方案帮助开发者缩短从想法到产品的距离。七牛的产品应用非常广泛,提供所有涉及到非结构化数据的场景的相关服务,如图片、视频类的APP、静态网站或网站的静态数据、UGC类的业务、在线教育、视频监控、直播、SNS、音乐类业务、备份和冷数据存储、网盘等。 - -七牛认为在数据全生命周期上的每一个节点,都可以通过云服务的技术和产品,加以支持。因此,除了基础的数据托管和加速以及基本处理,七牛也逐步支持各类围绕数据的第三方服务的接入,或用户自定义的数据服务开发,以形成围绕数据管理展开的生态体系。同时,七牛将着力全云平台产品的研发,将开发者的产品运维成本降低为0,真正帮助创业者降低创业的技术门槛。",1,0,"published",200,0,"2015-07-02 15:49:23","2020-06-01 11:11:14","七牛" -55,0,"中软国际","zhongruanguoji6051","upload/20150702/14358296116368.png","upload/20150702/14358296151543.png","upload/20150702/14358296206173.jpg","http://www.chinasofti.com/","+86 10 8286 1666",0,21,"中软国际有限公司(以下简称:中软国际)成立于2000年,为香港主板上市公司,股票代码354。中软国际是中国大型综合性软件与信息服务企业,提供从咨询、解决方案、技术服务到IT人才培养的“端到端”软件及信息服务,目前已经覆盖政府、制造流通、金融银行、保险证券、移动应用、电信、高科技、公用事业、能源等多个行业。中软国际服务遍布全球,业务覆盖包括中国大陆和香港地区、美国普林斯顿、西雅图、奥斯丁和华盛顿、英国伦敦、爱尔兰都柏林和日本东京在内的数十个城市,服务于百余家跨国企业客户,员工逾25,000人。 ","#公司介绍 <br/><br/> - -## 中软国际提供的服务主要包括:<br/><br/> - -1.专业服务包括咨询服务、解决方案和集成与服务三大类。咨询服务包括:战略咨询、业务咨询和信息化规划咨询。解决方案包括涉及电子政务、审计、行政许可管理、银行卡、信用卡、支付与清算、信贷管理、保险业务、手机支付、移动电子商务、制造执行系统(MES)、物流管理、轨道交通AFC/ACC、智能交通等在内六十多个行业解决方案,以及包括ERP、企业核心应用、企业微博、数据中心、物联网、云计算等在内的通用解决方案;集成与服务主要包括系统集成、系统维护和系统运行服务。 - -2.技术服务包括人力资源外包、产品研发与测试服务、产品部署与支持服务、IT基础设施管理服务、应用开发与测试服务、遗留系统升级及移植服务、客户联络中心服务、BPO、EPO、KPO、软件产品全球化服务和多媒体内容翻译服务等服务。 - -3.新兴服务着重于新兴互联网服务和研发,包括社交应用服务、移动互联网应用服务、云计算应用服务和大数据应用服务等,并以“平台服务+定制开发”的创新方式向客户提供解决方案。 - -4.卓越培训服务包括大学生培训(实训)和社会培训服务,涵盖面向各行业的各种国际认证培训。 - 公司开发了可信云众包服务平台JointForce(解放平台),是ITS产业的资源平台,可以帮助IT企业提升效率,降低成本。公司还拥有自主知识产权产品ResourceOne(R1),该产品经过十年研发,历经四大版本,目前已具备云计算和物联网平台能力,为公司解决方案业务提供了强大的应用基础平台支撑。公司还建立了遍布全国的信息技术实习训练基地(ETC)体系,提供以人才供应链为导向的培训服务,为公司主营业务提供人力资源保障。 - “奋斗为本,成就客户,创造分享,共同成长”是中软国际的企业精神和核心价值观,是公司多年来奋斗历程和集体智慧的总结,也是未来中软国际发展的精神基石。",1,0,"draft",9999,0,"2015-07-02 15:49:55","2020-06-01 11:11:14","中软国际" -56,105,"国云科技","guoyunkeji8810","upload/20150718/14372027127177.png","upload/20150727/14379773452005.png","upload/20150718/14372027184023.jpg","http://www.g-cloud.com.cn/","400-8833769",0,22,"国云科技是广东省知识产权优势企业,东莞市专利试点企业,东莞市专利培育企业,并通过ISO9001:2008质量管理体系认证。办公面积达8000平方米,员工200多人,其中院士1人,教授8人,博士后4人,博士16人,多数高层具备丰富的企业或产业化经验。云计算机房占地600平方米,400台高性能服务器,50万亿次公共云计算平台,存储能力达PB级。 -2010年,引进以李国杰院士为带头人的“云计算产业国际创新团队”,该团队集合了国际计算机领域的权威科学家、国内外率先开展云计算技术研究的一流专家和具有全球云计算产业应用经验的实业家,是一支专业背景雄厚、能力结构合理、合作关系密切的创新型团队组织。","\""国云科技是广东省知识产权优势企业,东莞市专利试点企业,东莞市专利培育企业,并通过ISO9001:2008质量管理体系认证。办公面积达8000平方米,员工200多人,其中院士1人,教授8人,博士后4人,博士16人,多数高层具备丰富的企业或产业化经验。云计算机房占地600平方米,400台高性能服务器,50万亿次公共云计算平台,存储能力达PB级。 -2010年,引进以李国杰院士为带头人的“云计算产业国际创新团队”,该团队集合了国际计算机领域的权威科学家、国内外率先开展云计算技术研究的一流专家和具有全球云计算产业应用经验的实业家,是一支专业背景雄厚、能力结构合理、合作关系密切的创新型团队组织。 -已研发的G-Cloud云操作系统是国内首个自主知识产权的云计算平台产品,掌握多项云计算基础设施平台的核心技术,针对未来数据中心发展的瓶颈和云化需求,构建可扩展、安全可控的高效云计算解决方案。2011年G-Cloud云操作系统获工信部(CSIP)“基于安全可控软硬件产品云计算解决方案”重点推介项目,2014年成为首个获得中国信息安全测评中心EAL3+认证的云操作系统产品,是国内最高安全级别的云操作系统产品。在云计算IaaS核心产品领域处于国内领先水平,成功应用于电子政务、教育、办公等行业。 -\""",1,0,"draft",9999,0,"2015-07-02 15:50:32","2020-06-01 11:11:14","国云科技" -57,30,"友普信息","youpuxinxi7466","upload/20180925/15378355799518.png","upload/20180925/15378356632137.png","upload/20180925/15378357038108.png","https://www.urplus.cn/","400-186-9900",0,0,"友普信息技术有限公司","友普信息技术有限公司",3,0,"published",9999,0,"2015-07-02 15:51:25","2020-06-01 11:11:14","友普信息" -58,0,"国富瑞","guofurui1118","upload/20150702/14358308824876.png","upload/20150702/14358308945991.png","upload/20150702/14358309021467.png","http://www.cidschina.com","010-60579001",0,23," 国富瑞数据系统有限公司(简称:CIDS)注册资本1.98亿元,是CIECC旗下具体承担数据中心和云计算板块业务的控股子公司。CIDS以高品质的数据中心基础设施、专业化的技术服务团队和优质的服务产品为核心,面向政府、事业单位及国内外企业,提供高可用数据中心服务、IT服务和云计算服务。 ","#公司介绍 <br/><br/> - -国富瑞数据系统有限公司(简称CIDS)是中国国际电子商务中心旗下具体承担数据中心和云计算板块业务的控股子公司。CIDS以高品质的数据中心基础设施、专业化的技术服务团队和优质的服务产品为核心,面向政府、事业单位及国内外企业,提供高可用数据中心服务、灾难恢复与业务连续性管理咨询服务、云计算服务等。<br/><br/> - - -国富瑞采用世界一流的软硬件设备,在北京亦庄、北京东单、北京通州和广州分别建有具有国际领先水平的数据中心,形成了“两地四中心”的先进构架,实现了“同城+异地”最高级的应用级灾备系统,具备防范局部和区域性灾难的能力。是中国唯一、世界一流的IT基础设施环境,具备信息安全最高级别的保障措施。 <br/><br/> - - -作为国内领先的信息技术综合外包服务提供商,国富瑞依托具有国际领先水平的数据中心运营网络,并结合多年在数据中心建设咨询、业务连续性专业咨询、IT架构专业咨询以及IT增值服务等领域的专业知识和技能,针对不同行业客户的需求,运用国际、国内最佳实践经验,为客户提供专业化的咨询及IT增值服务解决方案。 <br/><br/> - - -目前,国富瑞已成为商务部、国家电网、中国航信、中国保险行业协会、三星数据(中国)有限公司等众多重要客户提供了专业、优质、一流的数据中心外包服务。<br/><br/> - -未来,国富瑞将秉承“联合盟友、联合资源、优势互补”的原则,全力打造国家级、第三方、专业化的数据中心运营网络平台。计划在全国20个服务外包示范城市中的区域重点城市建设数据中心,以形成遍布全国的数据中心运营网络,从而成为国内规模最大、实力最强、安全性最高的数据中心专业化服务提供商持续提供国际领先的高可用性IT服务,树立“中国数据中心外包企业”的国际品牌形象。<br/><br/> -",1,0,"published",9999,0,"2015-07-02 15:52:01","2020-06-01 11:11:14","国富瑞" -59,0,"中联润通","zhonglianruntong3887","upload/20150722/14375408783106.png","upload/20150727/14379775038018.png","upload/20150727/14379775034891.png","http://www.china-ops.cn/","40010010",0,0,"中联润通成立于2009年,是一家专注于云计算技术研究、产品开发以及业务运营服务的云计算服务运营商。","中联润通成立于2009年,是一家专注于云计算技术研究、产品开发以及业务运营服务的云计算服务运营商。中联润通以最优的产品、最佳的解决方案及个性化的专业服务得到了众多客户的认可。目前,中联润通已与三大运营商:北京联通、天津移动、广州电信合作,分别推出了易享云、移动云、高新产业云服务,并成长为运营商可信赖的重要合作伙伴。公司骨干从中国互联网发展的起步阶段就开始研究IT基础设施的建设和运营管理。经过不断的技术积累,成功打造了中国首个自主知识产权的弹性主机服务平台。自成立之初、中联润通始终在挑战与机遇的磨砺中前行,始终遵循以“市场为导向、客户为核心”的原则,以“创新、求实”为宗旨,以“诚信、合作”为理念,为客户提供标准化、专业化的解决方案与IT增值服务,致力于成为客户最信任的成长伙伴。 -",1,0,"published",9999,0,"2015-07-02 15:52:37","2020-06-01 11:11:14","中联润通" -60,0,"上海斐讯","shanghaixun3436","upload/20150718/14372028144952.png","upload/20150718/14372028207913.png","upload/20150718/14372028247903.jpg","http://www.phicomm.com","4000 431 031",0,0,"上海斐讯数据通信技术有限公司(以下简称“PHICOMM 斐讯”)成立于2008年,其全球总部位于中国-上海,是一家致力于向用户及客户提供移动终端、数据通信产品、云计算、ICT 等各种软硬件及整体解决方案的研发型高新技术企业,集研发、制造、销售一体,用户范围涵盖个人消费者、企业、政府、各行业等各个层次。PHICOMM 斐讯秉承“致力于更自由的沟通”这一使命,构建起了“快速响应、高性价比”的公司整体核心竞争力,使公司在短时间内实现跃迁式的发展。 -",""上海斐讯数据通信技术有限公司(以下简称""PHICOMM斐讯"")成立于2009年,其全球总部位于中国-上海,是一家致力于向用户及客户提供移动终端、数据通信产品、云计算等各种软硬件及整体解决方案的研发型高新技术企业,集研发、制造、销售于一体,用户范围涵盖个人消费者、企业、政府、各行业等不同层次。 PHICOMM 斐讯秉承“致力于更自由的沟通”这一使命,构建起了""快速响应、高性价比""的公司整体核心竞争力,使公司成立6年间以每年超过200%的增速快速发展,至2014年公司年销售额达100亿人民币,跻身中国电子信息百强。 - -核心竞争力构筑发展基石 -斐讯能够实现快速发展,最核心的竞争力的首先来源于清晰的公司定位:斐讯提供的业务囊括了从智慧个人到智慧家庭、智慧企业、智慧城市、智慧社会,清晰地定位在“智慧”领域;其次来源于斐讯的三大战略——产业垂直化、品牌国际化和资产证券化,这三大战略互相支撑,互为犄角。产业垂直化是斐讯能够在行业内立足的根本,通讯行业已经从单一硬件产品的竞争发展到整合的服务和总体解决方案的竞争。要想在激烈的竞争中立于不败,通讯企业需要具备产业链级别的竞争力。品牌方面斐讯以国际化的视野与运作积极践行“智慧互联,享你所想”的品牌理念,通过斐讯五大BU所有业务单元包括软硬件、解决方案,从个人一直到社会的智慧互联,实现让消费者“享你所想”。产业垂直化为品牌国际化和资产证券化提供坚实的产业基础,品牌国际化为产业垂直化和资产证券化提供更多的溢价能力,资产证券化则为产业垂直化和品牌国际化提供更充足的资金。 -在成立之初斐讯就给自己定了三个三年计划,第一个三年计划从公司刚成立的2009年到2011年,目标是实现行业认可,技术上做到人有我有。通过第一个三年计划,斐讯建立起了较为完善的研发、生产、销售体系;第二个三年计划从2012年到2014年,目标是实现渠道认可,技术上做到部分独有,通过第二个三年计划,斐讯建立起了目前支撑公司发展的个人移动终端、家庭终端、企业网、云计算等等五大模块的战略发展框架,并正式开始国际化道路。前两个阶段的目标斐讯均已实现,2014年,斐讯首次入围中国电子信息百强企业。今年进入斐讯第三个三年计划阶段的第一年,也是斐讯的品牌元年。从2015年到2017年,斐讯要实现消费者认可,技术上做到创新独有。 - 全球研发,自主生产 -作为一家研发型导向的高科技企业,能够将先进的技术与客户的需求进行完美的融合,满足不同客户用户的个性化需求,研发实力至关重要。PHICOMM斐讯现拥有员工近5000人,研发技术人员占比达到55%,所有技术人员均为大学本科以上学历,其中硕士比率超过30%,博士比率超过5%。斐讯不仅坚持每年3-5 %的研发投入同时针对研发人员建立了一套完善的激励机制,鼓励创新和探索,从而激发出研发人员的积极性与主动性。 -斐讯在中国上海、北京、南京、深圳等地设立了研发基地,同时在德国慕尼黑及美国旧金山成立了研发基地,与国际化最前沿的技术接轨。 -斐讯除了自己的研发团队,还将产学研作为企业研发体系的一个重要组成部分。从2013年开始,斐讯就与国内多所著名高校签署协议共同推进产学研体系建设,陆续与复旦大学成立终端安全联合实验室;与上海电机学院成立“上海电机-斐讯信息技术研究院”;与复旦大学联合成立的“院士专家工作站”;与上海交通大学联合成立“上海交大斐讯智慧城市研究中心”;与上海工业技术大学达成战略合作。充分发挥了企业与院校研究机构之间的协同效应,对斐讯的技术和专利的积累具有前瞻指导性和巨大的推动作用。 -以上这些,都推动了斐讯自主知识产权的累积,2014年斐讯专利申请总数达988件,其中发明714件,实用新型99件,外观设计175件。专利数在上海市仅次于上汽和上海电气集团。 -2010年斐讯首个生产基地——7号基地(手机及数通产品)投产,占地3.5万平;新建6号生产基地预计2015年中投产,占地33.5万平。PHICOMM斐讯的生产基地已经通过了ISO9001、ISO14001、ROSH等各种国际标准认证,保证了所生产的产品具备国际级品质。未来云计算的基石——IDC数据中心,斐讯也在全国范围内进行布局,上海、北京的IDC已经陆续投入使用,天津、鹤壁、南宁等地的IDC正在紧锣密鼓的建设中。所有的这些,都为PHICOMM斐讯的持续发展提供了强劲的动力。 - 全球布局,销售渠道 -作为定位于全球的国际品牌,PHICOMM斐讯销售、服务网络在全球的布局已经逐步成型:已经在中国27个大中型城市设立办事处,业务范围覆盖全国;斐讯自2012年建立欧洲分公司、东南亚分公司,正式开启国际化征程以来,经过3年多的发展,海外分公司已经遍布欧洲、中东、北非、俄罗斯独联体、东南亚、亚太、拉美等全球多个区域,同时在德国、美国设立了研发基地。目前海外销售主要以个人移动终端以及智慧家庭终端为主。通过销售网络覆盖的不断提高,PHICOMM斐讯为更多的用户及客户提供优质的产品、完备的服务,赢得了越来越多的信任和口碑。" -",1,0,"published",9999,0,"2015-07-02 15:53:37","2020-06-01 11:11:14","上海斐讯" -61,0,"宝德科技","baodekeji3137","upload/20150702/14358313236579.png","upload/20150702/14358313303701.png","upload/20150702/14358314903124.jpg","http://www.plcloud.com/","4008-502-789",0,24,"宝德科技集团是在香港上市,以服务器、云计算、大数据等为主营业务的综合性IT企业集团,是中国领先的IT解决方案和服务供应商。 ","#公司介绍 <br/><br/> - - -宝德科技集团是在香港上市,以服务器、云计算、大数据等为主营业务的综合性IT企业集团,是中国领先的IT解决方案和服务供应商。 <br/><br/> - -深圳市宝德软件开发有限公司是宝德科技集团旗下的创新型软件研发子公司,以云计算基础设施即服务(IaaS)、内容分发网络(CDN)、网络加速服务为主营业务,致力于提供安全、稳定、高可用的公有云、私有云、混合云产品,是国内领先的云计算产品解决方案和软件服务提供商。 <br/><br/> - -宝德在2010年筹建云计算业务,提出“中国云•宝德云,中国赢•宝德赢”的云战略。从2012年起,基于OpenStack云计算平台研发宝德公有云,达到近万台虚机的规模。宝德云在软件定义网络(SDN)、软件定义存储(SDS)等研究方向上大力投入,于2014年推出“大数据一体机”、“云计算一体机”。宝德云计算平台迅速覆盖网络游戏、电子商务、P2P等行业领域。2015年,宝德云基于计算存储超融合架构,设计全新的融合一体机(Cloud In Box),为广大企业级客户提供基于超融合架构的混合云服务 。 <br/><br/> - -宝德云的愿景是携手各行业上下游,共建良性云生态,与合作伙伴共同做大做强中国的云计算产业,实现“中国云•宝德云,中国赢•宝德赢”的战略目标。 <br/><br/>",1,0,"published",9999,0,"2015-07-02 15:54:15","2020-06-01 11:11:14","宝德科技" -62,0,"飞天诚信","feitianchengxin8288","upload/20150702/14358271367209.png","upload/20150702/14358271442660.png","upload/20150702/14358271484720.png","https://cloudauth.cloudentify.com","400-061-8850",0,25,"飞天诚信科技股份有限公司是全球领先的智能卡操作系统及数字安全系统整体解决方案的提供商和服务商,成立于1998年,总部设在北京,经过17年的创新发展,公司现已成为中国信息安全领域领导品牌,中国智能身份识别领域第一股(股票代码:300386)","#公司简介 -<br/> - -飞天诚信科技股份有限公司是全球领先的智能卡操作系统及数字安全系统整体解决方案的提供商和服务商,成立于1998年,总部设在北京,经过17年的创新发展,公司现已成为中国信息安全领域领导品牌,中国智能身份识别领域第一股(股票代码:300386)。<br/><br/> - - -作为国家规划布局内的重点软件企业、国家重点高新技术企业、中关村十百千工程企业,公司多次参与国家相关行业标准制定起草工作,成为国内多个标准组织核心成员单位,其中由我公司主导的《信息安全技术公钥基础设施电子签名卡应用接口基本要求》和《智能密码钥匙密码应用接口数据格式规范》标准作为国标已成功发布,同时,公司总经理李伟受聘为全国金融标准化技术委员会专家。<br/><br/> - - -飞天诚信自成立以来始终坚持自主研发的发展策略,公司作为保护数字身份安全和交易信息安全,验证并管理数字身份和交易信息的行业领先企业。在网络银行安全交易,支付卡及服务,移动支付安全,云认证,身份认证及软件保护等多个领域提供完整的服务和解决方案。经过17年的技术积累和创新,公司现已拥有了一个基于嵌入式安全操作系统、APP技术、应用平台、云平台及国密算法研究的基础研发中心,同时,与国家信息技术安全研究中心、中国银联等第三方机构建立了战略合作伙伴关系,公司凭借过硬的技术和研发实力,不断创新,不断推出技术先进、性能稳定的信息安全产品和服务,为全球用户提供值得信赖的信息安全产品和解决方案为了迅速响应并满足客户需求,公司以北京总部为中心,在广州、上海、成都、武汉设立了华南、华东、西南、华中营销中心,在深圳、杭州和昆明等多个地区建立了办事处,在亚洲、欧洲、大洋洲、美洲等全球范围内建立起市场推广和营销服务体系,并配有专业的系统解决方案和服务团队,公司产品销售至全球80多个国家和地区,积累了金融、政府、邮政、电信、交通、互联网等领域6000余家客户。<br/><br/> - -公司银行客户覆盖最为广泛,为包括工行、建行、农行、中行、交通银行、招商银行、民生银行、上海浦东发展银行、中信银行、兴业银行、光大银行等近200家银行的网上银行系统安全提供完善的解决方案和专业的技术服务,是国内银行客户数最多的智能网络身份认证产品提供商;同时公司产品还销售给马来西亚土著商业银行、马来西亚银行、日本运通集团、美国飞塔公司等国外客户。通过积极参与国际市场竞争,飞天诚信一方面成功地向世界展示了一个中国高新技术企业的技术实力与创新精神,一方面也在管理水平、产品品质、服务规范、环保贡献等诸多方面进一步与国际先进水平接轨,使公司能够站在一个服务全球的水平和高度,均衡发展、不断进步,并使自身在国内的广大客户与合作伙伴能够切实分享到这种国际性成长在产品与服务方面的切实回报。<br/><br/> - -17年以来,公司业绩以平均每年超30%的速度稳健增长,2014年营业收入10.8亿元,利润2.47亿元,公司现注册资本金9501万元,拥有员工800余人,其中40%人员为中高级软件开发工程师,公司目前拥有一支由国内外信息安全领域专家,中青年核心骨干,海归、硕士,博士等组成的研发队伍。同时,2014年7月,公司成为行业内首家院士专家工作站建站企业,这将进一步促进飞天诚信发挥高端人才的引进优势,引领科技创新、将产学研工作有机结合,有助于培养创新人才,增强企业自主创新能力和市场竞争力。<br/><br/> - -知识产权是企业全球化的基石,是打破国外企业对中国企业的知识产权壁垒的利器,截至目前,飞天诚信已拥有计算机软件著作权登记证书140多件,申请专利1100余项(含150余项国外专利申请),获得已授权专利700余项,授权专利中,绝大多数为发明专利,是同行业内拥有国内及国外专利申请和授权量最多的企业。基于在国家知识产权领域的贡献,公司先后被评为北京市专利试点先进单位、北京市专利示范企业、全国企事业知识产权试点单位、中国专利优秀奖单位等。<br/><br/> - -同时,公司取得:北京市唯一一家智能网络身份认证技术工程实验室挂牌企业、科技部和工信部“国家重点创新基金及物联网发展专项资金”支持企业、国家发改委“信息安全专项”承接企业、中关村管委会、市发改委、市财政局、市科委、市商务委、市经信委、市统计局联合主持的《中关村国家自主创新示范区现代服务业试点》扶持资助企业、入围“智慧北京”移动支付安全优秀解决方案奖 、中国互联网金融支付安全技术应用创新奖 、中关村信用培育双百工程百家最具影响力信用企业 、中关村高成长TOP100、福布斯2015最具潜力上市企业100强名单等重要资质和荣誉。 -",1,0,"published",9999,0,"2015-07-02 15:55:08","2020-06-01 11:11:14","飞天诚信" -63,0,"天润融通","tianrunrongtong1576","upload/20150718/14372028755940.png","upload/20150727/14379776881526.png","upload/20150727/14379776674033.png","http://www.ti-net.com.cn/","4006869009",0,0,""北京天润融通科技有限公司创建于2006年,是中国领先的“智能呼叫中心云服务商”(工信部呼叫中心运营许可:B2-20070013)。 -天润融通致力于推动呼叫中心建设从“项目式”向“平台化 、智能化、服务化” 的转变,开创了呼叫中心云服务模式,让呼叫中心从此变得“简单、专业、可信赖”! -天润融通现有员工157人,总部设在北京云基地,在上海和深圳分别设有华东和华南总部。北京云基地是北京市政府授予的""祥云计划""云计算基地之一。作为国内云计算应用的早期实践者,天润融通公司于2010年10月,受邀整体迁入北京云基地。" -","北京天润融通科技有限公司管理团队的全体人员均来自国内主流的电信运营商和大型跨国企业集团,公司主要的管理人员都拥有10年以上在大型IT企业以及电信运营商工作的经历。因此,公司在通信产品开发、通信网络运营维护、通信服务质量保障等方面具有非常扎实的基础。 - -公司聚集了一批互联网和电信领域的优秀技术人才,在互联网通信产品的研发和运营上拥有丰富的经验。在 Soft Switch(软交换)、CTI(通信与计算机集成)、CRM(客户关系管理)、呼叫中心等领域均拥有具备独立知识产权的核心技术。",1,0,"published",9999,0,"2015-07-02 15:55:54","2020-06-01 11:11:14","天润融通" -64,0,"美团云","meituanyun8347","upload/20150718/14372030426158.png","upload/20150727/14379777286077.png","upload/20150718/14372030489013.jpg","http://mos.meituan.com","无",0,26,"美团云(MOS)是美团推出的公有云平台,在物理机房维护、底层基础架构搭建、自动化运维、安全防护、网络连接、客服响应等多层面,持续为互联网和企业级用户提供“最稳定”的用户体验,帮助用户连接云计算未来。 -","美团云(MOS)是美团推出的公有云平台,在物理机房维护、底层基础架构搭建、自动化运维、安全防护、网络连接、客服响应等多层面,持续为互联网和企业级用户提供“最稳定”的用户体验,帮助用户连接云计算未来。 -",4,0,"published",9999,0,"2015-07-02 15:56:36","2020-06-01 11:11:14","美团云" -65,0,"临沂拓普","linyituopu9299","upload/20150702/14358276515611.png","upload/20150702/14358276649921.png","upload/20150702/14358276686735.jpg","http://www.0539.com","400-006-7996",0,27,"临沂市云计算中心是临沂市唯一一家城市云计算中心,经市经信委批复(临经信字[2013]444号)、由临沂市拓普网络股份有限公司投资建设,项目总投资6000万元,一期完成投资3500万元,机房面积1200平米。 -临沂市云计算中心具有完备、稳定的软、硬件基础设施。以领先的云计算核心技术及综合资源汇聚能力,为政府和企事业单位提供一站式IT服务,在数字化城市和“智慧临沂”的建设中起到至关重要的技术支撑作用。 -","#公司介绍 -<br/> - -临沂市云计算中心是临沂市唯一一家城市云计算中心,经市经信委批复(临经信字[2013]444号)、由临沂市拓普网络股份有限公司投资建设,项目总投资6000万元,一期完成投资3500万元,机房面积1200平米。 -临沂市云计算中心具有完备、稳定的软、硬件基础设施。以领先的云计算核心技术及综合资源汇聚能力,为政府和企事业单位提供一站式IT服务,在数字化城市和“智慧临沂”的建设中起到至关重要的技术支撑作用。 -目前,临沂市云计算中心已获得工信部IDC认证、国家ISCCC信息安全服务许可、国家增值电信业务许可等资质,是中国数据中心联盟会员单位、中云计算基地(中心)联盟成员单位、中国云计算山东战略联盟成员单位等。"",1,0,"published",9999,0,"2015-07-02 15:57:39","2020-06-01 11:11:14","临沂拓普" -66,0,"华宇","huayu9120","upload/20150702/14358238676841.png","upload/20150702/14358238696129.png","upload/20150702/14358238724119.png","http://www.thunisoft.com/","400101010",0,0,"华宇","#华宇",0,0,"published",9999,1,"2015-07-02 15:58:50","0000-00-00 00:00:00","" -67,0,"广州云宏","guangzhouyunhong9674","upload/20150702/14358239437709.png","upload/20150702/14358239454171.png","upload/20150702/14358239485738.png","http://www.winhong.com","020-28260050",0,28,""广州云宏信息科技股份有限公司是国内最早专注于云计算关键技术------云操作系统(虚拟化引擎、虚拟化管理、云服务管理)研发的企业之一,也是最早具有相关自主知识产权的企业之一。我们围绕客户需求深入研究、持续创新,在云计算关键领域建立了先发优势和技术优势,能够实现跨平台、跨厂商、高融合、可定制的云计算和传统IDC统一管理及运营服务。 -",""云宏信息的业务范围涉及服务器虚拟化、云管理、云存储、大数据及应用平台,涵盖IaaS、PaaS、SaaS三个层次。我们致力于电信运营商、政府、金融及大中型企业等提供领先的云计算产品,从前期咨询、设计到实施、集成、运营维护全流程服务和端到端解决方案,帮助客户从传统IT模式向云计算演进,提升其核心竞争力,创造最大价值。 - -公司现在已拥有高价值客户,包括:广东电信、广东联通、福建移动、广西移动、贵州移动、国家审计署、河北公安厅、贵州交通厅、广发银行、福建某商业银行、广东省中医院、中国航天科工集团、南方航空、重庆烟草等。 - -云宏以创造便捷的信息生活为使命,以成为国际领先的云计算核心技术提供商为愿景,运用先进的云计算技术,使得人人都能享受快捷、丰富的信息服务,为云计算民族产业作出持续贡献。 -",0,0,"published",9999,1,"2015-07-02 15:59:29","2015-07-18 13:50:43","" -68,0,"黑龙江亿林","heilongjiangyilin8577","upload/20150702/14358239965128.png","upload/20150702/14358239985282.png","upload/20150702/14358240013922.png","http://www.hljdata.net/index.html","400101010",0,29,"黑龙江亿林","#黑龙江亿林",0,0,"published",9999,1,"2015-07-02 16:00:12","0000-00-00 00:00:00","" -69,56,"畅捷通","changjietong2361","upload/20150718/14372031085726.jpg","upload/20150727/14379778749136.png","upload/20150727/14379778782590.png","http://www.chanjet.com","4006-600-566",0,30,"畅捷通信息技术股份有限公司(畅捷通)是中国领先的小微企业软件及服务供应商。畅捷通的产品和服务包括软件产品、产品支持服务及其他服务、销售采购商品以及云服务。截至2013年12月31日,根据畅捷通的登记记录,畅捷通的软件产品用户已发展超过600,000家,遍布中国,覆盖多个行业。根据计世资讯(北京时代计世资讯有限公司),2013年,按收入计算,畅捷通在中国小微企业财务软件市场佔有17.6%的份额,在整个中国小微企业软件市场佔有3.8%的份额,均排名第一。 -","畅捷通信息技术股份有限公司(畅捷通)是中国领先的小微企业软件及服务供应商。畅捷通的产品和服务包括软件产品、产品支持服务及其他服务、销售采购商品以及云服务。截至2013年12月31日,根据畅捷通的登记记录,畅捷通的软件产品用户已发展超过600,000家,遍布中国,覆盖多个行业。根据计世资讯(北京时代计世资讯有限公司),2013年,按收入计算,畅捷通在中国小微企业财务软件市场佔有17.6%的份额,在整个中国小微企业软件市场佔有3.8%的份额,均排名第一。 -",6,0,"published",9999,0,"2015-07-02 16:00:55","2020-06-01 11:11:14","畅捷通" -70,0,"杭州又拍云","hangzhouyoupaiyun3436","upload/20150719/14372973525527.png","upload/20150719/14372973614143.png","upload/20150719/14372973708991.jpg","https://www.upyun.com","0571-81020203",0,0,"UPYUN(又拍云)成立于 2010 年,致力于为客户提供一站式的云加速服务,帮助客户解决数据存储、处理和加速问题,实现快速上云,让创业变得更简单。 -","##公司简介 - -###关于UPYUN - -UPYUN(又拍云)成立于 2010 年,致力于为客户提供一站式的云加速服务,帮助客户解决数据存储、处理和加速问题,实现快速上云,让创业变得更简单。 - -UPYUN 是中国专业的云计算服务商,凭借多年的专业技术优势,在全球范围率先实现了“CDN + Cloud”的服务模式,现已拥有 6 大数据中心,120 多个自建 CDN 节点,3000 多台服务器,日均请求超过 250亿次,并吸引了超过 10 万付费用户的使用, 为客户持续提供稳定、可靠、安全的云服务体验。 - - -###企业文化 - -* 我们的使命:让创业更简单 -* 我们信奉的价值观:服务第一、持续精进、勇于承担 - -### 企业荣誉 - -* 2015 年,年度十大最具成长力云服务奖(工信部) -* 2015 年,云加速技术创新奖(IDC 大会) -* 2014 年,年度最佳电商云计算服务平台(电商博览会) -* 2014 年,未来之星(中国企业家) -* 2014 年,年度创新企业(创业邦) -* 2014 年,首批通过乌云众测认证 -* 2014 年,最佳开发者服务平台奖(艾媒咨询-拳头奖) -* 2013 年,年度最佳技术服务提供商(中国互联网创业者大会) -* 2012 年,互联网年度最佳云服务商(Donews 互联网盛典) -* 2012 年,创新企业 Top10(CSDN) - - - - - - -",1,0,"published",210,0,"2015-07-02 16:01:37","2020-06-01 10:05:35","杭州又拍云" -71,0,"海云捷迅","haiyunjiexun3840","upload/20150722/14375497496107.png","upload/20150722/14375497536923.png","upload/20150722/14375497594392.png","http://www.awcloud.com/","400101010",0,31,"北京海云捷迅科技有限公司(AWcloud),国内领先的企业级OpenStack云服务提供商。总部位于北京中关村高新技术园区,拥有行业内最专业的研发、运营和服务团队,对云计算、虚拟化、IaaS有着深入理解和多年研发运营经验。 -","北京海云捷迅科技有限公司(AWcloud),国内领先的企业级OpenStack云服务提供商。总部位于北京中关村高新技术园区,拥有行业内最专业的研发、运营和服务团队,对云计算、虚拟化、IaaS有着深入理解和多年研发运营经验。 -海云捷迅以OpenStack为契机,与包括Intel、HP、Oracle、Canonical、东软等公司在内的国内外各大软硬件厂商及IT服务提供商建立了紧密的战略合作关系,构建了一套完整的OpenStack服务生态体系,为企业提供基于OpenStack的公有云、私有云、混合云、融合计算等解决方案,为IDC企业提供联合运营服务。此外,还为客户提供OpenStack的架构咨询、设计、实施、培训、运维等一条龙服务。",1,0,"published",9999,0,"2015-07-02 16:02:18","2020-06-01 11:11:14","海云捷迅" -72,182,"华为技术有限公司","huaweijishu4808","upload/20150726/14378983506400.png","upload/20150726/14379179335104.png","upload/20150726/14379158181255.png","http://anyoffice.huawei.com/","400-822-9999",999,0,"华为技术有限公司是一家生产销售通信设备的民营通信科技公司,总部位于中国广东省深圳市龙岗区坂田华为基地。华为的产品主要涉及通信网络中的交换网络、传输网络、无线及有线固定接入网络和数据通信网络及无线终端产品,为世界各地通信运营商及专业网络拥有者提供硬件设备、软件、服务和解决方案。华为于1987年在中国深圳正式注册成立。","\""过去20多年,华为抓住中国改革开放和ICT行业高速发展带来的历史机遇,坚持以客户为中心,以奋斗者为本,基于客户需求持续创新,赢得了客户的尊重和信赖,从一家立足于中国深圳特区,初始资本只有21000人民币的民营企业,稳健成长为世界500强公司。2014年,公司年销售规模达到近2882亿人民币。如今,我们的电信网络设备、IT设备和解决方案以及智能终端已应用于全球170多个国家和地区。 - -作为全球领先的信息与通信解决方案供应商,我们为电信运营商、企业和消费者等提供有竞争力的端到端ICT解决方案和服务,帮助客户在数字社会获得成功。我们坚持聚焦战略,对电信基础网络、云数据中心和智能终端等领域持续进行研发投入,以客户需求和前沿技术驱动的创新,使公司始终处于行业前沿,引领行业的发展。我们每年将销售收入的10%以上投入研发,在近17万华为人中,超过45%的员工从事创新、研究与开发。华为在170多个标准组织和开源组织中担任核心职位,已累计获得专利授权38825件。 - -华为积极致力于社会经济的可持续发展,运用信息与通信领域专业经验,弥合数字鸿沟,让人人享有高品质的宽带联接;我们努力保障网络的安全稳定运作,助力客户和各行各业提升效率、降低能耗,推动低碳经济增长;我们开展本地化运作,构建全球价值链,帮助本地发挥出全球价值,实现整个产业链的共赢。 - -我们深信:未来将是一个全联接的世界。华为与合作伙伴一起,开放合作,努力构建一个更加高效整合的数字物流系统,促进人与人、人与物、物与物的全面互联和交融,激发每个人在任何时间、任何地点的无限机遇与潜能,推动世界进步。 -\""",1,0,"published",120,0,"2015-07-21 18:21:45","2021-08-12 11:04:43","华为技术" -73,0,"国信灵通","guoxinlingtong4535","upload/20150722/14375364279516.png","upload/20150722/14375364324247.png","upload/20150727/14379780806948.png","http://www.nationsky.com ","400-102-2919",999,0,"国信灵通是国际领先的企业安全移动解决方案提供商,一直以引领企业移动化为使命,为政府和大型央企提供国际领先的安全移动化整体架构规划咨询服务,积极推进中国的移动化进程。 -凭借多年对各行业及安全移动信息化的深刻理解和技术积累,并借助网秦全球领先的移动安全技术,国信灵通自主研发了多个兼具高安全性、高管理性并可同时支持iOS、Android、Windows 8 多平台的移动管理平台,包括移动设备管理平台(MDM)、移动应用管理平台(MAM)、移动内容管理平台(MCM)、企业移动应用开发平台(MEAP)、企业移动IM、企业移动邮件网关,以及移动应用安全加固系统等。","北京国信灵通网络科技有限公司(简称:国信灵通)成立于2005年,注册资金4000万,是一家专注于企业安全移动解决方案的高新技术公司,也是中国第一家成功登陆美国纽交所上市的移动互联网企业—网秦移动有限公司(NYSE listed:NQ)的全资子公司。国信灵通是全球企业移动联盟(简称“GEMA”)成员,苹果中国一级行业授权经销商、苹果中国区咨询顾问团成员。 -作为国内最早的专业企业移动服务提供商,国信灵通早在2006年就开始与电信运营商及黑莓(BlackBerry)中国公司合作推动黑莓业务在中国企业的应用落地服务,并陆续与苹果、三星、联想、中兴、微软、诺基亚等进行业务合作。凭借多年对各行业及移动信息化的深刻理解和技术积累,并借助网秦公司全球领先的移动安全技术,国信灵通自主研发了多个兼具高安全性、高管理性并可同时支持iOS、Android、Windows 8 多平台的移动管理平台,包括移动设备管理平台(MDM)、移动应用管理平台(MAM)、移动内容管理平台(MCM)、移动应用开发平台(MEAP)、移动即时通讯平台、移动邮件平台,以及移动应用安全加固平台等。 -作为国内第一家推出MDM产品(NQSky MDM)的公司,该产品目前在全球部署用户数已经超过200万,涉及政府、电信、金融、能源、制造、快消、教育等多个行业。2013年,国信灵通为国家统计局第三次全国经济普查一期超过50万台设备成功部署NQSky MDM产品,该项目总量达到70万台,是中国乃至全球迄今为止最大的MDM部署项目。同时国信灵通与三星KNOX企业安全解决方案开展全球产品及销售合作,成为其全球合作的中国唯一MDM产品。 -国信灵通长期专注于企业安全移动服务领域,并与国内外领先移动技术公司保持畅通的业务合作和交流。作为国产软件商的代表,国信灵通一直以引领企业移动化为使命,并鼓励创新,多年来培养并汇聚了一批国内最优秀,有多年IT及移动化经验的资深移动化咨询人才,为政府和大型央企提供了多项国际领先的移动化整体架构规划服务,以积极推进中国的移动化进程。 -在行业移动应用上,国信灵通尤其聚焦于银行、保险和证券行业,形成了包括移动保险展业系统、银行移动财富管理、信用卡开卡开户管理系统、大堂展业系统、移动微贷系统等行业应用解决方案,并成功服务于众多大中型银行、保险、证券公司,区别于传统的软件开发商,国信灵通将金融的业务与其自身独有的移动化平台技术和移动交互技术完美结合,为客户带来了优质的移动体验,极大满足了客户的需求。 -2014年,国信灵通再上台阶。成功中标中央政府采购项目,成为中央政府指定采购产品;与中国电信集团实现强强联手,搭建了“手机管家”云平台业务;成功中标中国移动集团MDM云平台项目;与全球第七大运营商日本软银达成合作,MDM产品全面挺进日本市场;与国家信息中心签订战略合作协议,双方确定在移动信息化和移动安全方面进行深入的战略合作,以加速提升中国各级政府的政务移动化水平。 -国信灵通公司总部位于北京,下设华东、华南、华中、华西、山东、北方六个大区,产品与解决方案和专业服务区域覆盖整个中国大陆。同时,NQSky MDM已经同步发布英文版和日文版,分别通过网秦海外分公司及GEMA全球组织成员进行全球范围营销。 -",1,0,"draft",9999,0,"2015-07-22 11:41:39","2020-06-01 11:11:14","国信灵通" -74,0,"乐视","leshi8499","upload/20150722/14375539874228.png","upload/20150727/14379781052376.png","upload/20150723/14376218518354.jpg","http://www.letvcloud.com/","13260299185(QQ:263528755)",99,0,"乐视云计算有限公司在2014年成立,是国内首家由视频网站独立出来,凭借超过十年的视频、云计算、大数据等技术积累,提供中立视频云计算服务的公司。","乐视云计算有限公司在2014年成立,是国内首家由视频网站独立出来,凭借超过十年的视频、云计算、大数据等技术积累,提供中立视频云计算服务的公司。作为中国视频云第一品牌,我们致力打造围绕内容汇聚、内容发行、衍生服务的超级视频云平台,为全球企业客户及个人用户提供优质丰富的视频云服务。乐视云计算拥有全球第一个全面支持 4K 和 H.265、新一代的中立企业级视频云开放平台,在全球范围拥有 450 个 CDN 节点、7Tbps 出口带宽,提供强大的视频分发能力,为用户打造清晰、流畅、极致的视频播放体验,为各行各业提供视频服务平台和技术资源,引领变革传统商业模式。我们相信,“如果人们能随时随地丰富视界,创享生活,人生将会更美好!",1,0,"published",9999,1,"2020-07-22 20:32:36","2020-06-01 11:11:14","乐视" -75,123,"云端网络","yunduanwangluo8114","upload/20160314/14579174016860.png","upload/20160314/14579174056784.png","upload/20160314/14579174101549.png","http://www.verycloud.cn","4000051023",0,0,"VeryCloud云端网络是一家致力于云计算技术创新的“一站式云服务”高科技公司,专注于云分发、云主机、云DNS、云桌面、ISP等与云计算相关产品的研发和推广,为用户提供"一站式服务"解决方案。公司坚持走自主研发道路,为用户提供从网络链路资源到服务器资源的全方位优化和提升的解决方案,充分提高 IT资源使用效率,降低成本,提升系统可靠性,全面减少和消除各类故障所带来的损失,大大降低用户的数据和运营等风险。 -",""云端网络科技有限公司是一家致力于云计算技术创新的“一站式云服务”高科技公司,其前身为普尔网络,创建于2006年12月的全国性数据中心,持有中华人民共和国颁布的增值电信业务经营许可证(ISP、ICP)。现旗下已拥有超过10家独立自建机房(其中江浙沪三省数据中心网络已打通,实现内网互联互通)以及200多家合作机房,网络资源遍布全国,专业从事IDC、CDN、ISP、云桌面、云分发、云主机等业务,是国内知名的网 络接入服务商,更是长三角地区知名的IDC服务商。 -公司坚持走自主研发道路,已获得VeryCDN GSLB全球负载均衡系统、VeryCDN加速系统、超级云端等7项软件著作权,并成功推出VeryCloud与 VeryCDN面向不同规模客户的两个云服务平台。产品及其解决方案可为用户提供从网络链路资源到服务器资源的全方位优化和提升,充分提高 IT资源使用效率,降低成本,提升系统可靠性,全面减少和消除各类故障所带来的损失,大大降低用户的数据和运营等风险。" -",5,0,"published",9999,0,"2016-03-14 09:04:34","2020-06-01 11:11:14","云端网络" -76,43,"华云数据","huayunshuju5850","upload/20170912/15052057181653.png","upload/20170912/15052057219288.png","upload/20170912/15052057334874.jpg","http://www.huayun.com","400-808-4000",0,0,"华云数据集团有限公司成立于2010年,秉承着“自主、安全、可控”的理念,面向客户提供稳定可靠的云计算与大数据产品与服务。集团拥有员工600多人,吸引多家基金与上市公司的投资,打通了上下游产业链,成功打造了华云的云生态圈。目前,集团运营着全球逾20个云数据中心,网络覆盖电信、联通以及华云自有BGP网络。 - -华云产品覆盖高品质公有云、企业级私有云、企业级大数据、企业级混合云以及数据中心云化产品,垂直整合医疗、教育、交通、金融、政企等十余个行业,深度满足企业云化对安全、可靠、稳定、连续、高效的要求,推动中国企业全面上云。","华云数据集团有限公司成立于2010年,秉承着“自主、安全、可控”的理念,面向客户提供稳定可靠的云计算与大数据产品与服务,着力推进中国云计算与大数据的发展,公司遍布于北京、上海、无锡、深圳、香港、厦门、杭州、南京、临沂等地。集团吸引了海通证券、京道、万向集团、东方证券、国联证券、五道口基金、红豆、远东、杉杉等多家基金与上市公司的投资,打通了上下游产业链,成功打造了华云的云生态圈。 -作为中国全云能力领导者,华云以“市场及服务驱动”为研发理念,为企业级客户提供一站式云与大数据的产品与服务,并与数百家区域及行业服务伙伴达成合作。集团运营着全球逾20个云数据中心,网络覆盖电信、联通以及华云自有BGP网络。 - -华云以驱动用户业务为目标,提供企业运营全生命周期所需的云与大数据产品,打造贴合行业需求的解决方案及应用。产品覆盖高品质公有云、企业级私有云、企业级大数据、企业级混合云以及数据中心云化产品,垂直整合医疗、教育、交通、金融、政企等十余个行业,深度满足企业云化对安全、可靠、稳定、连续、高效的要求,推动中国企业全面上云。 - -高品质公有云服务 - -华云高品质公有云服务涵盖“数据密集型”和“计算密集型”两大范畴,包括高性能的综合弹性计算解决方案与产品、面向业务的SDN解决方案和产品、敏捷VPC产品、CLB智能负载均衡产品、高IOPS分布式存储产品、海量数据对象存储COS产品、关系型数据库RDS、大数据引擎CDPS及高性能计算等解决方案,帮助企业快速构建弹性、稳定、易用的云平台,降低开发运维难度和IT成本。 - -企业级私有云产品 - -华云企业级私有云是新一代企业级IT基础平台,集虚拟化、资源调度、可视化管理平台于一体,包含私有云平台CloudUltra™、私有对象存储StorUltra™、私有云盘StorUltra™ Box、自动化运维平台OpsUltra™以及云应用管理平台Amall™,帮助企业以更低的成本,获得稳定、高效、敏捷、安全的IT应用环境及一站式云应用管理能力。 - - -企业级大数据产品 - -华云企业级大数据产品包含面向企业级业务应用和数据分析的基础架构平台DataUltra™及基于大数据生态系统的数据挖掘平台InsightUltra™,满足企业大数据处理、挖掘需求,助力复杂业务的分析和决策。 - -企业级混合云产品 - -华云混合云管理平台MixUltra™帮助企业高效运营混合云基础架构,可集约化管理CloudUltra™、vSphere、华云公有云等常见的公私有云,通过简单直观的图形界面为用户提供一站式部署。同时,华云还为用户提供Bridge™混合云迁移服务,帮助企业在已经使用的云服务之间便捷移动管理和应用。 - -数据中心云化产品 - -华云数据中心云化产品CloudIDC™是数据中心资源统一管理平台,帮助传统IDC企业一步上云,实现传统数据中心转为云数据中心,使传统IDC企业提供创新的公有云服务。 - -此外,华云基于行业场景提供定制化解决方案,包括交通云、金融云、教育云、政务云、能源云、医疗云等行。华云服务网络与技术团队遍布全球超过15个城市,为企业提供全方位的售前咨询、专业的解决方案推荐、强大的交付保障、及7*24+360º的技术支持,保障企业体验和业务不间断运营。 -",7,0,"published",9999,0,"2016-03-14 09:42:40","2020-06-01 11:11:14","华云数据" -77,120,"软通动力","ruantongdongli1367","upload/20160314/14579214316550.png","upload/20160314/14579214343685.png","upload/20160314/14579217948063.jpg","http://www.isoftstone.com","4001019977",0,0,"公司立足中国,服务全球。公司主营业务涵盖智慧业务与信息服务两大领域,具备端到端“软件+服务”综合业务能力和强大的纵深服务优势。 -","软通动力信息技术(集团)有限公司 - -作为中国智慧城市与产业互联网(互联网+)建设的领导者,领先的创新型技术服务提供商,软通动力立足中国,服务全球。公司主营业务涵盖智慧业务与信息技术服务两大领域,具备端到端“软件 +服务”综合业务能力和强大的纵深服务优势。 - -在智慧业务领域,软通动力秉承“智慧城市建设产业先行”的理念,先后在全国近100个城市展开“智慧城市”战略布局,业务范围包括顶层设计、城市管理、产业发展、环保节能、民生服务及基础设施等多个方向;在信息技术服务领域,软通动力提供信息技术外包、众包、业务流程外包(BPO)、系统集成等多项服务,覆盖银行、保险、电力、交通、零售等10余个重要行业,并积累了深厚、丰富的经验。 - -我们的愿景 - -成为世界级技术服务提供商及全球客户首选合作伙伴 - -我们的使命 - -以创新思维和领先技术,为客户创造价值 - -我们的定位 - -中国智慧城市与产业互联网(互联网+)建设的领导者,创新型技术服务提供商 - -我们的核心价值观 - -品质、创新、学习、开放、价值创造 -",1,0,"published",9999,0,"2016-03-14 10:17:51","2020-06-01 11:11:14","软通动力" -78,46,"宝信软件","baoxinruanjian8926","upload/20160314/14579226953139.png","upload/20160314/14579227203065.png","upload/20160314/14579227503915.png","http://www.baocloud.cn","400-821-0860",0,0,"上海宝信软件股份有限公司(简称“宝信软件”)系宝钢股份控股、宝钢集团实际控制的软件企业,2001年4月于上海证券交易所上市,公司总部位于中国(上海)自由贸易试验区。 -宝信软件秉承“IT服务,提升信息价值”的经营理念,凭借30多年的经验和技术积累,全面提供具有自主知识产权的企业信息化解决方案、自动化系统集成及运行维护服务。产品与服务业绩遍及钢铁、有色金属、装备制造、医药、化工、采掘、智能交通、金融、水利水务等多个行业。宝信软件累计已申请专利、软件著作权、技术秘密认定数百项,承担着国家发改委高新技术产业化示范项目、国家科技部863项目、国家工信部电子基金项目等诸多重大技术和产品项目。 -","宝信软件经过30余年信息化建设与运维的积累,奠定了进行云计算技术研究与实践的基础。 于2011成立了“云计算专项研究”小组,确定了9个自主集成研发重点和82个子研究课题,相继在云计算虚拟化技术及应用、数据库及中间件水平扩展、多租户开发技术等关键技术领域取得突破,形成了虚拟化环境下的高可用技术、服务标准化建模技术、大数据作业调度技术等6项专利技术,以及SaaS多租户数据安全隔离方法、跨应用的Session共享技术、CMDB动态建模技术等15项技术秘密,具备云计算咨询、建设与运营能力。宝之云是宝信软件向社会提供的公有云服务,于2012年12月正式发布并提供服务。 -",2,0,"published",9999,0,"2016-03-14 10:33:07","2020-06-01 11:11:14","宝信软件" -79,47,"森华易腾","senhuayiteng4218","upload/20160314/14579244471046.png","upload/20160314/14579244507649.png","upload/20160314/14579244598014.png","http://www.lenet.com.cn","400-818-1123",0,0,"北京森华易腾通信技术有限公司(简称森华易腾)创建于2005年,致力于提供互联网专业服务,与2015年在A股上市(股票代码:600589)。","北京森华易腾通信技术有限公司(简称森华易腾)创建于2005年,致力于提供互联网专业服务,与2015年在A股上市(股票代码:600589)。森华易腾总部位于北京,目前在上海、广州开设了分公司,企业主营业务包括互联网数据中心、互联网接入服务、网络安全服务、云计算业务、应用加速以及全方位的增值服务和完整的行业解决方案。 - " -",1,0,"published",9999,0,"2016-03-14 11:01:51","2020-06-01 11:11:14","森华易腾" -80,0,"安全狗(厦门服云)","anquangouxiamenfuyun7518","upload/20160314/14579252356098.png","upload/20160314/14579252398549.png","upload/20160314/14579252445326.jpg","http://www.safedog.cn","400-1000-221",0,0,"安全狗,领先的云安全服务与解决方案提供商。首创的云+端云安全管理平台(SAAS模式)为用户解决公有云、私有云和混合云环境中可能遇到的安全及管理问题。提供了包含自动化系统风险识别和加固、系统级的安全防护(防黑/防入侵/抗攻击)、云监控(安全监控/性能监控/日志监控)、云管理(多公有管理/混合云管理)以及基于大数据架构的安全事件分析等功能。安全狗通过独创的安全技术,云安全体系以及优秀的用户体验,获得了众多用户的认可,目前已经为超过百万的用户提供云安全解决方案。 -","厦门服云信息科技有限公司(品牌名称“安全狗”)是国内领先的云安全服务与解决方案提供商,公司目前已经获得包括IDG、华软在内知名投资机构的两轮投资。秉承“安全狗即服务”(Safedog as a Service)的理念,依托全球领先的安全技术与完善的大数据处理平台,致力于为电商、金融、医疗、教育、游戏、云计算服务商、政府机关等行业客户提供最具核心竞争力的云安全产品及解决方案,帮助客户实现业务平台的安全、稳定、可靠运行。 - -安全狗自创立以来,始终坚持信息安全技术自主创新,并专注于云安全相关技术和产品的研发。公司核心成员均来自于国内大型安全公司,在安全领域具有深厚的技术积累。安全狗系列产品先后获得公安部第三研究所、西海岸实验室“东方之星”等权威机构认证,同时也获得了多项技术发明专利及产品著作权。安全狗云安全服务平台目前已经为客户保护超过100万台(云)服务器及200万个网站,日均拦截超过近亿次的攻击,已成为国内该领域用户量最大的云安全服务平台。 - -同时安全狗还积极参与到国内云计算安全生态的建设,目前已经跟阿里云、腾讯云、天翼云、华为云、亚马逊AWS等国内外云计算平台建立合作伙伴关系;安全狗云安全服务平台已经成功对接各大云计算平台。安全狗独创的安全云主机,将安全狗的安全服务跟各大云计算平台相结合,为客户提供双重安全服务及保障。 -",1,0,"published",9999,1,"2020-05-11 16:48:33","2016-05-05 11:06:33","" -81,0,"武汉闪达","wuhanshanda7287","upload/20160314/14579257417753.png","upload/20160314/14579257441743.png","upload/20160314/14579257475277.jpg","http://sendcloud.sohu.com/","400-006-9990",0,0,"武汉闪达科技有限公司(以下简称闪达)是一家注册在武汉东湖高新区,从事现代服务业的高科技公司,专注在为企业提供邮件、通讯等SaaS服务的提供商。公司的愿景是“服务互联网未来”,旨在更好的为企业客户提供有价值的服务,拥抱互联网未来。","武汉闪达科技有限公司(以下简称闪达)是一家注册在武汉东湖高新区,从事现代服务业的高科技公司,专注在为企业提供邮件、通讯等SaaS服务的提供商。公司的愿景是“服务互联网未来”,旨在更好的为企业客户提供有价值的服务,拥抱互联网未来。公司主营业务是SendCloud和爱发信的邮件发送服务,已经形成了相当的市场口碑。目前服务的客户有:Uber,51talk,超级课程表、360doc、知乎、丁香园、知乎、庞果网、猎聘网、大街网、拉勾网、妖精的口袋、返利网、米折网、时光旅游网、FESCO、聚信创盈、随手记、东航金融、36氪、华西都市报、优酷、17173超级玩家、快玩等 -",1,0,"published",9999,0,"2016-03-14 11:23:17","2020-06-01 11:11:14","武汉闪达" -82,45,"上海科技网络","shanghaikejiwangluo1395","upload/20160314/14579290884919.png","upload/20160314/14579290927762.png","upload/20160314/14579290954944.png","http://cloud.stnc.cn","8008198818",0,0,"上海科技网络通信有限公司成立于2000年11月,注册资金2亿元人民币;上海科技网的前身是上海市人民政府科技网,“九五”期间上海市政府三项标志工程之一;上海市高新技术企业; 一流、中立的专业数据中心业务(IDC)运营商、大型宽带城域网运营商(NSP),领先的企业云技术服务提供商。 - -",""上海科技网络通信有限公司由上海国资委所属企业集团--上海仪电集团控股。上海仪电是一家历史悠久、与时俱进的企业。她既有辉煌的产业发展史,又始终与时代同步、与创新同行,不断开拓创新转型发展的道路,是物联网整体解决方案的提供商与运营商,形成从“云”到“端”的业务布局。上海科技网络通信有限公司具有业务资质:拥有国家工信部和通信管理局颁发的业务经营牌照,包括IDC,ISP,IP-VPN,CPN,ICP.通过ISO20000信息服务流程管理体系,ISO27001信息安全管理体系,ISO9000质量管理体系的认证,以及系统集成三级和信息系统等级保护三级资质。 -主营业务包括互联网数据中心、云计算服务、互联网接入、虚拟专用网和驻地网等。长期服务于各政府机关、企事业单位、科研院所、电子商务、物流制造、银行证券、互联网金融等行业,累计客户超过3000家,其中不乏世界500强和中国百强企业。 -上海科技网络通信有限公司从2011年起提供云计算服务,积累了丰富的运营管理经验,积累了一批有价值的客户。 -" -",1,0,"published",9999,0,"2016-03-14 12:19:00","2020-06-01 12:05:44","上海科技网络" -83,38,"曙光云","shuguangyun2281","upload/20160314/14579313987568.png","upload/20160314/14579314054342.png","upload/20160314/14579314095931.png","https://www.sugon.com/","028-85336991",0,0,"曙光云计算技术有限公司作为中科曙光的全资子公司,是面向云计算和智慧城市领域的高新技术企业。","依托中科曙光集团,凭借与中国科学院计算技术研究所、国家智能计算机研发中心、国家高性能计算机工程中心等国家级科研机构的紧密合作,曙光云计算技术有限公司为各地政府、企业和个人提供云计算及智慧城市领域的各类优质服务。凭借成熟的云计算解决方案和自主可控的高新技术,曙光云计算将其解决方案和云计算服务广泛应用于智慧城市建设,涉及教育、气象、医疗、能源、互联网及公共事业等多个领域。自2009年以来,曙光在各地广泛开展城市云业务,在成都、无锡、南京、包头、哈尔滨、宜昌、乌鲁木齐等地建设、运营多个城市云计算中心,是国内独立运营商业化城市云计算中心最多的公司。",1,0,"published",9999,0,"2016-03-14 12:57:06","2021-10-18 17:01:13","曙光云" -84,0,"上海宽惠","shanghaikuanhui2193","upload/20160314/14579341809941.png","upload/20160314/14579341847346.png","upload/20160314/14579342339421.jpg","http://www.91yun.cn","021-51087028",0,0,"上海宽惠网络科技有限公司是致力于提供互联网网络以及云计算解决方案的专业公司,拥有一个多年行业工作经验的专家团队,与众多基础运营商和优秀硬件厂商保持着密切的深层次合作,覆盖中国电信、中国新联通、中国移动三大运营商以及中国教育科研网、上海科技网等资源。 -","上海宽惠网络科技科技有限公司是一家致力于提供互联网以及云计算解决方案的专业公司,拥有一个多年行业工作经验的专业团队,与众多基础运营商和优秀硬件厂商保持着密切的深层次合作,覆盖中国电信、中国新联通、中国移动、等各大运营商以及中国教育科研网、中国科技网等资源。宽惠网络科技科技本着“恭、宽、信、敏、惠”精神服务,为大中小企业提供云服务、上网专线及数据中心解决方案。 -",1,0,"published",9999,0,"2016-03-14 13:44:29","2020-06-01 11:11:14","上海宽惠" -85,0,"沃丰时代","wofengshidai3966","upload/20160314/14579404177517.png","upload/20160314/14579402301592.png","upload/20160314/14579402351405.png","http://www.udesk.cn","010-82167490",0,0,"Udesk是国内领先的新一代企业级智能客服系统,拥有上万家企业用户,为企业提供了一套集智能机器人、在线客服、呼叫中心、工单系统为一体的完整的客服系统解决方案。包括:支持微信、网站、移动浏览器、移动APP的在线客服系统,支持IVR语音、智能路由、坐席管理、统计报表的全新一代云呼叫中心系统(Call Center),支持工单流转、SLA、自定义工单等强大的工单系统。通过Udesk,您可以倾听用户来自电话、邮件、微博、微信、网站、移动APP、即时通讯的声音,可以及时处理用户的反馈,可以迅速提高客户服务的满意度。 -","Udesk是国内领先的新一代企业级智能客服系统,拥有上万家企业用户,为企业提供了一套集智能机器人、在线客服、呼叫中心、工单系统为一体的完整的客服系统解决方案。包括:支持微信、网站、移动浏览器、移动APP的在线客服系统,支持IVR语音、智能路由、坐席管理、统计报表的全新一代云呼叫中心系统(Call Center),支持工单流转、SLA、自定义工单等强大的工单系统。通过Udesk,您可以倾听用户来自电话、邮件、微博、微信、网站、移动APP、即时通讯的声音,可以及时处理用户的反馈,可以迅速提高客户服务的满意度。 -",1,0,"published",9999,0,"2016-03-14 15:29:31","2020-06-01 11:11:14","沃丰时代" -86,0,"犀思云","xisiyun9903","upload/20160314/14579406872298.png","upload/20160314/14579406902670.png","upload/20160314/14579407063796.png","https://www.syscloud.cn","4008166620",0,0,"犀思云(苏州)云计算有限公司是国内领先的企业级云计算应用服务商,致力于为用户提供高效、安全、可靠的云应用基础设施服务------云计算IaaS领域内网络、计算、存储、安全等,以及面向互联网及传统行业的混合云技术解决方案。 -",""犀思云(苏州)云计算有限公司是国内领先的企业级云计算应用服务商,致力于为用户提供高效、安全、可靠的云应用基础设施服务------云计算IaaS领域内网络、计算、存储、安全等,以及面向互联网及传统行业的混合云技术解决方案。 -目前在北京、上海、苏州、太原、香港等地区设置有办公室及分支机构,并通过24小时热线4008-166-620正在为国内外数百家优秀企业提供着优质服务。 -犀思云提供的产品和服务涵盖计算和网络资源,主要包括主机云服务器(内存,CPU)、IP资源、 带宽、块存储、路由器等。 -通过犀思云平台用户可以自主创建、关闭、删除云主机服务,获取主机运行状态,调整云主机配置(内存,CPU),购买存储空间以及IP,带宽等资源。 -同时还为用户提供负载均衡、子网络、VPN等网络服务,使用户可以方便的构建自己的虚拟数据中心。" -",1,0,"published",9999,0,"2016-03-14 15:32:18","2020-06-01 11:11:14","犀思云" -87,0,"中金云网","zhongjinyunwang3455","upload/20160314/14579409623921.jpg","upload/20160314/14579410218083.jpg","upload/20160314/14579409125733.jpg","http://www.zhonghuajinrongyun.com/","010-67828680",0,0,"业界领先的数据中心与信息系统外包、云计算与大数据应用、灾难备份与业务连续性管理的信息技术基础设施和IT综合服务提供商。 -"," 中华金融云是依托中金数据高等级数据中心,深刻理解金融行业的IT服务要求,为客户提供高性能、高可用、高安全、高品质的一站式云服务。",1,0,"draft",9999,0,"2016-03-14 15:37:57","2020-06-01 11:11:14","中金云网" -88,0,"亿方云","yifangyun5209","upload/20160411/14603406472528.png","upload/20160411/14603407897566.png","upload/20160411/14603407156185.png","http://www.fangcloud.com/","400-993-9050",0,0,"亿方云科技起源于美国硅谷,核心创始团队曾帮助世界最大的企业云存储服务商Box成功登陆纽交所。带着最尖端的产品理念与丰富的研发运营经验,2013年底我们回国创业,受到资本市场的高度关注与青睐,至今已获得数千万人民币的天使投资和数百万美元的A轮融资。公司成立以来一直致力于为企业提供最专业的文件管理服务,通过安全便捷的文件访问、共享和协作,将企业的人与数据无缝连接,帮助企业实现“核心数字资产”的价值最大化。 -","亿方云科技起源于美国硅谷,核心创始团队曾帮助世界最大的企业云存储服务商Box成功登陆纽交所。带着最尖端的产品理念与丰富的研发运营经验,2013年底我们回国创业,受到资本市场的高度关注与青睐,至今已获得数千万人民币的天使投资和数百万美元的A轮融资。公司成立以来一直致力于为企业提供最专业的文件管理服务,通过安全便捷的文件访问、共享和协作,将企业的人与数据无缝连接,帮助企业实现“核心数字资产”的价值最大化。 -",1,0,"published",9999,0,"2016-04-11 10:14:02","2020-06-01 11:11:14","亿方云" -89,0,"环信","huanxin8100","upload/20161123/14798668046370.jpg","upload/20161123/14798668098915.jpg","upload/20161123/14798668165533.jpg","http://www.easemob.com","400-612-1986",0,0,"环信成立于2013年4月,是一家国内领先的企业级软件服务提供商,并于2016年荣膺“Gartner 2016 Cool Vendor”。产品包括国内上线最早规模最大的即时通讯云平台——环信即时通讯云,以及移动端最佳实践的全媒体智能云客服平台——环信移动客服。截至2016年上半年,环信即时通讯云共服务了82149家App客户,SDK覆盖手机终端5.64亿,平台日发送消息5.57亿条。","环信移动客服——全媒体智能云客服倡领者,于2016年荣膺“Gartner 2016 Cool Vendor”。环信支持全媒体接入,包括网页在线客服、社交媒体客服(微博、微信)、APP内置客服和呼叫中心等多种渠道均可一键接入。基于环信业界领先的IM长连接技术保证消息必达,并通过智能客服机器人技术降低人工客服工作量。同时,基于人工智能和大数据挖掘的客户旅程透析产品"环信客户声音"能够帮助企业优化运营,提高跨渠道客服体验。截至2016年上半年,环信移动客服共服务了29437家企业用户,现已覆盖包括电商、O2O、互联网金融、在线教育、在线旅游、移动医疗、智能硬件、游戏等领域的Top10客户,典型用户包括国美在线、58到家、楚楚街、海尔、神州专车、新东方、链家、泰康在线、中信证券等众多标杆企业。根据易观发布的《中国SaaS客服市场专题研究报告》显示,环信移动客服在SaaS移动端客服用户覆盖占比高达77.4%,稳居行业第一。",1,0,"published",9999,0,"2016-11-23 10:07:47","2020-06-01 11:11:14","环信" -90,32,"首都在线","shouduzaixian2321","upload/20161123/14798679498499.jpg","upload/20161123/14798679592488.jpg","upload/20161123/14798679582962.jpg","http://www.capitalonline.net.cn/","4008105300",0,0,"北京首都在线科技股份有限公司(以下简称“首都在线”)成立于2005年,是一家总部设在北京,并在美国、上海、武汉、深圳建有分公司的国际化云计算服务商。公司2010年在新三板挂牌,2015年获新三板最具影响力公司奖,2016年进入第一批新三板创新层。","北京首都在线科技股份有限公司(以下简称“首都在线”)成立于2005年,是一家总部设在北京,并在美国、上海、武汉、深圳建有分公司的国际化云计算服务商。公司2010年在新三板挂牌,2015年获新三板最具影响力公司奖,2016年进入第一批新三板创新层。 -首都在线是中国最早的IDC服务商之一,拥有十多年网络和数据中心服务经验,是中国最早进行云计算商业化运营的专业网络以及数据中心服务商。 -目前,公司已发展成为国际云计算服务提供商,在北京、无锡、香港、台湾、美国(达拉斯、洛杉矶、纽约)、东京、新加坡、法兰克福、阿姆斯特丹等地建有云平台。通过首都在线独有的环球光纤网络,为每个用户搭建属于自己的全球私有网络,实现与世界连接的伟大梦想。",1,0,"published",9999,0,"2016-11-23 10:26:51","2020-06-01 11:11:14","首都在线" -91,0,"保利威视","baoliweishi3090","upload/20161123/14798683563727.png","upload/20161123/14798683809683.png","upload/20161123/14798684136044.png","http://www.polyv.net/","http://www.polyv.net/",0,0,"广州易方信息科技有限公司是一家拥有自主知识产权的高新技术企业,其高层管理人员和技术骨干均来自于网易、阿里、百度等一流的互联网企业。保利威视是广州易方信息科技有限公司旗下拥有自主知识产权的视频云计算服务平台,2012年在广州建立。在国内部署有500余个节点,覆盖全国各个省市以及各网络运营商,是目前国内唯一一家拥有三套CDN分发网络的云视频服务商,为用户带来极速播放体验。保利威视的视频版权保护技术拥有国家专利,致力于保障视频文件安全;平台提供API、SDK技术支持以及200项视频功能(其中157项为在线教育行业定制)。","保利威视于2012年正式上线,隶属广州易方信息科技有限公司,创始团队均来自网易、阿里等互联网公司,拥有多年视频项目研发经验,一直以“让视频提升价值”为理念,为机构用户提供SaaS级的云视频服务。 -成立至今,保利威视已为全国超过2万家企业和教育机构提供视频技术支持,同时帮助数千家传统行业用户顺利实现用户体验极佳的在线视频应用。历时四年,保利威视成为了集云视频、云直播于一体的在线云视频平台。 -",1,0,"published",9999,0,"2016-11-23 10:35:18","2020-06-01 11:11:14","保利威视" -93,0,"互联港湾","huliangangwan3950","upload/20161130/14804746029159.png","upload/20161130/14804746044671.png","upload/20161130/14804746107081.png","http://www.hunheyun.com","400-678-8875",0,0,"北京互联港湾科技有限公司成立于2009年,是一家行业领先的混合云服务提供商,致力于为全球用户提供优质的混合云服务、云托管服务、技术服务及其他综合性IT服务。通过与中国电信、中国移动、中国联通、科技网、教育网等运营商长期紧密合作,互联港湾在北京、上海、广州等地部署有互联互通的多线BGP网络,并打通了北上广之间的二层波分传输网络,在全国各主要城市及香港部署有SDN骨干网。截至目前,互联港湾已成功以北京、上海、广州、深圳为核心,辐射华北、华东、华南三大中国重点核心经济区域,并在香港、北美积极布局。","互联港湾公司混合云平台通过公有云+私有云+云托管+跨云管理的全方位服务体系,结合SDN(软件定义网络)、CDN(内容分发网络)等增值服务,同时涵盖自动化运维、监控告警、自服务IT和持续交付,支持异地多中心和异构网络架构,实现分布式部署在多个数据中心的公有云、私有云、物理架构之间的内网互通,降低用户基础资源投入,并对多云平台的资源进行更直观的调配与管理,满足用户灵活部署业务的需求,为用户的底层资源搭建提供一站式解决方案,同时为用户应用层业务提供各类增值服务。",2,0,"published",9999,0,"2016-11-30 10:58:04","2020-06-01 11:11:14","互联港湾" -94,113,"万达信息","wandaxinxi5336","upload/20170113/14843132086246.png","upload/20170113/14843133359832.png","upload/20170113/14843133461284.png","http://www.wondersgroup.com","4009210956",0,10,"万达信息股份有限公司成立于1995年12月,是国内领先的智慧城市领军企业。历经多年发展,万达信息在城市各行业积累了丰富经验,形成了突出的行业软件与服务优势。公司以行业核心业务为基石,重点发展公共服务在线运营,积极开拓线下闭环服务。 -凭借在丰富行业实践中形成的自主创新核心技术,万达信息目前已拥有250余项具备自主知识产权的软件产品和软件著作权、14项国内外专利技术;承担着20余项国家各类标准及指南、近10项上海市及其他地方各类标准的制定工作;并先后获得1项教育部科技进步一等奖、2项国家科技进步二等奖、5项上海市科技进步一等奖,1项上海市科技进步二等奖,为企业的可持续创新能力提供了坚强动力。","公司以行业应用软件( Software )、专业IT服务( Service )和系统集成( System Integration )作为三大主营业务,为智慧城市提供整体解决方案",1,0,"published",9999,0,"2017-01-13 21:32:37","2020-06-01 11:11:14","万达信息" -95,111,"蚂蚁金融云","mayijinrongyun6748","upload/20170115/14844806539458.png","upload/20170115/14844806903059.png","upload/20170115/14844806969048.png","http://www.cloud.alipay.com","暂无",0,10,"2015 年 9 月蚂蚁金服集团(以下简称"蚂蚁金服")宣布启动"互联网推进器"计划,计划将在 5 年内助力超过 1000 家金融机构向新金融转型升级。作为"互联网推进器"蚂蚁金服将推动平台、数据和技术方面的能力全面对外开放。其中"互联网推进器"中的技术能力指的就是蚂蚁金融云平台。 - -历经十多年在金融领域的技术沉淀,依托于阿里集团和蚂蚁金服集团在云计算方面的积累,蚂蚁金服正式推出面向金融机构深度定制的行业云计算服务--"蚂蚁金融云"","金融级技术 - -历经十年金融实践的磨砺,每年一次的双 11 海量交易大考,以及日趋复杂多样的产品体系,蚂蚁金服逐渐具备了符合金融级 IT 系统安全标准的技术能力,而蚂蚁金融云正是这些技术深度淬炼的成果。通过蚂蚁金融云,金融机构就能天然拥有金融级安全、可靠、一致的服务能力,同时还能获得快速交付、零停机维护的高效云计算运维能力,以及符合新金融要求的高并发、移动化服务能力,并借助先进的大数据分析能力把握金融市场脉搏,开创新一代金融服务模式。 - -融合性 - -蚂蚁金融云平台作为一个互联网金融创新业务的技术推进器,已经成功助力了余额宝、芝麻信用、招财宝、聚宝等众多创新业务,通过平台内置接口即可快速与蚂蚁金服各项开放业务进行融合,同时降低金融机构尤其是广大中小金融机构的创新成本,助力金融机构更快速地向新金融转型升级,共同探索全新业态。 - -开放性 - -蚂蚁金融云平台开放了技术接入标准,为软件提供商、服务提供商、数据提供商等合作伙伴提供一个自由创新的空间。只要符合蚂蚁金融云安全准入标准,即可通过 API 将自己的软件/服务快速的对接到蚂蚁金融云上,共同为金融机构提供更丰富的产品、服务和价值。蚂蚁金融云不仅仅给予合作伙伴强大的技术支持,同时借助蚂蚁的客户群优势及良好的品牌及口碑效应,促进金融服务以最快的速度、最高的效率传播开来,真正实现以技术引领金融快速发展、安全创新,促进行业繁荣,助力普惠金融。",1,0,"published",9999,0,"2017-01-15 19:46:45","2020-06-01 12:06:06","蚂蚁金服" -96,0,"睿江云","jiangyun3793","upload/20170115/14844825223782.png","upload/20170115/14844825294050.png","upload/20170115/14844827676094.jpg","http://www.eflycloud.com/","4000662212",0,10,"广东睿江云计算股份有限公司成立于2007年,是国内技术领先的互联网服务商,优秀的云计算服务商以及云计算数据中心运营商, 致力于为全球各行业提供最专业的云计算和云平台、互联网数据中心(IDC)、内容加速服务(CDN)、国际云加速及相关的整体解决方案。","广东睿江云计算股份有限公司成立于2007年,是国内技术领先的互联网服务商,优秀的云计算服务商以及云计算数据中心运营商, 致力于为全球各行业提供最专业的云计算和云平台、互联网数据中心(IDC)、内容加速服务(CDN)、国际云加速及相关的整体解决方案。 - -迄今,睿江云已拥有优质企业客户超过3000家, 在网运营机柜总数超过3000个, 云主机超过1万个, 在建机柜或空余机柜超5000个, 为各类互联网业务和云计算业务提供扎实的网络服务和技术保障;已与中国电信、中国联通、中国移动等基础网络运营商建立了长期牢固的战略合作伙伴关系,并在全国范围内拥有超过50个主营运计算数据中心和覆盖全国的CDN节点网络,务求为客户提供快速稳定的一站式服务。",1,0,"published",9999,0,"2017-01-15 20:18:21","2020-06-01 11:11:14","睿江云" -97,0,"美通云动","meitongyundong6807","upload/20170115/14844845144960.png","upload/20170115/14844845299196.png","upload/20170115/14844845538648.png","http://www.yunshipei.com","400 069 0309",0,10,"云适配(AllMobilize Inc.) 是全球领先的HTML5企业移动化解决方案供应商,由前微软美国总部IE浏览器核心研发团队成员及移动互联网行业专家在美国西雅图创立,并获得了微软创投的扶持以及晨兴资本、IDG资本、天创资本等国际顶级风投机构的投资。通过“统一办公入口+安全管理平台+移动开发工具”一站式移动化解决方案,云适配可以迅速为政府和企业构建一套安全、便捷、跨平台的HTML5移动应用系统(内网或外网),帮助各类企业实现跨屏营销和移动办公战略。","从2012年至今,云适配的开放技术平台已经吸引了超过40万开发者加入;云适配跨屏云也成功应用于超过30万家企业网站,包括微软、联想等世界500强企业,光明网、中国青年报等知名媒体,清华、北大、中科大等知名大学,以及中国政府网、中国共产党网等政府机构。2014年6月,比尔·盖茨先生访华时特地观看了云适配的技术演示,并给予高度的赞赏。对于云适配在全球范围内的领先技术及创造的社会价值,CCTV 《新闻联播》、《经济半小时》等节目都进行了重点报道。",0,0,"draft",9999,0,"2017-01-15 20:50:24","2020-06-01 11:11:14","美通云动" -98,121,"EasyStack ","easystack7472","upload/20170115/14844853798929.jpg","upload/20170115/14844854431196.png","upload/20170115/14844855647577.jpg","http://www.easystack.cn","400-648-5123",0,10,"EasyStack(北京易捷思达科技发展有限公司)由中国最早从事OpenStack研发的IBM中国研发中心的核心团队创建,基于OpenStack为企业级客户提供开放、稳定可靠、高性能的云计算服务。EasyStack自成立以来已为超过100家企业级客户提供OpenStack云产品和服务,服务客户涵盖金融、电信、政府、电力、教育以及互联网金融、电商、大数据等新兴行业,走在中国OpenStack企业实践的最前沿。2016年荣膺Gartner Cool Vendors in China,成为上榜的唯一一家开源云计算企业。EasyStack成为OpenStack基金会黄金会员,同年加入Linux基金会。","EasyStack(北京易捷思达科技发展有限公司)是领先的OpenStack云平台和服务提供商,由中国最早从事OpenStack研发的IBM中国研发中心的核心团队创建, 基于OpenStack为企业级客户提供开放、稳定可靠、高性能的云计算服务。 -企业市场:EasyStack自成立以来已为中国移动、中国电信、顺丰速运、邮储银行、兴业、银联、农信银、国泰君安、联想集团、国家电网、浙能集团、清华大学、长城宽带、TCL、天河云等超过100家企业级客户提供OpenStack云产品和服务, 服务客户涵盖金融、电信、政府、电力、教育以及互联网金融、电商、大数据等新兴行业,走在中国OpenStack企业实践的最前沿。2016年荣膺Gartner Cool Vendors in China,成为上榜的唯一一家开源云计算企业。 -社区贡献:EasyStack坚持开放云计算理念,持续参与社区核心功能开发,在最新发布的OpenStack版本Newton版中,EasyStack核心贡献中国第一、全球第八。2016年,EasyStack成为基金会黄金会员,同年加入Linux基金会。 -生态合作:EasyStack不懈的推动生态发展,2013年7月由EasyStack创始人陈喜伦创办OpenStack中国技术社区(www.openstack.cn),凝聚了一流OpenStack、 云计算技术和行业专家;2015年7月推动OpenStack中国产业联盟成立。 EasyStack于2016年发布E+合作伙伴计划,目前拥有IBM、联想、北明软件、宇信科技、科蓝软件、SUSE、红旗、Mellanox、360、山石网科等百余家合作伙伴。",1,0,"published",9999,0,"2017-01-15 21:05:12","2020-06-01 12:06:28","EasyStack" -99,48,"山东众志","shandongzhongzhi3083","upload/20170116/14845741173764.png","upload/20170116/14845741223510.png","upload/20170116/14845741277934.jpg","http://www.tacloud.net","0538-6263779",0,10,"泰安市云计算中心经泰安市政府批准,在“政府引导、企业运作”的精神指导下,由山东众志电子有限公司投资建设,数据中心占地面积800余平方米,总投资7000万元,业务机柜达到200个,容纳2500台物理服务器(5万台虚拟服务器),达到国家A级机房建设标准,制冷系统采用模块化设计,实现冷热空气隔离以降低能耗。 -泰安市云计算中心定位于区域领先的云计算运营服务商、智慧城市建设运营商,自2013年上线以来,为广大政府、企事业单位提供个性化云服务,得到了社会各界的广泛关注和支持。","山东众志电子有限公司是国家高新技术企业,位于泰安市国家高新技术产业开发区。公司以云计算创新行业应用、物联网、大数据、城市一卡通、软件开发与服务为主要业务,是信息化综合解决方案提供商和区域领先的云计算运营服务商、智慧城市建设运营商。 - -公司通过ISO9001国际质量管理体系认证,获得国家信息系统集成及服务三级资质、涉密信息系统集成乙级资质、信息系统安全三级保护认证、山东省安全技术防范工程设计施工一级资质,国家信息技术服务标准认证(ITSS)二级资质、国家声频工程企业综合技术等级一级资质,获得ISP和IDC运营牌照。 - -公司被评为“山东省双软认定企业”,“ 山东省物联网产业重点企业”、“信用等级AAA级企业”;“山东省正版产品经营示范单位”、 “山东省诚信企业”,首批“泰安市创新型企业”,“泰安市管理创新企业”,泰安市企业与企业家联合会副会长单位、泰安市信息化协会会长单位,山东省信息化协会会员单位,中国云计算市场与应用联盟秘书长单位;中国电子工业标准化技术协会信息技术服务分会会员单位;全国信息技术标准化技术委员会大数据标准工作组成员单位;农业大数据产业技术创新战略联盟单位。2014年公司居家“益券养老”项目团队被评为山东省服务业创新团队。2015、2016年众志电子公司连续入选《中国大数据企业排行榜》,2016年位列“农业行业”排名第四位,“智慧农业”领域排名第二位。公司董事长李松和入选国家科技创新创业人才,荣获“2015年度中国火炬创业导师”称号;2015年荣获“山东省信息化行业优秀企业家”和“山东省电子信息行业优秀企业家”;2016年入选第二批国家“万人计划”科技创业领军人才。 - 2013年6月,经泰安市政府批准(泰经信电子字【2011】208号),公司负责建设的泰安市云计算中心上线运营。云中心已经落地的项目应用包括现代农业、旅游云、企业云、城市一卡通、医疗云、智慧物价、食品药品监管、智慧社区、智能守押云平台、金融监管平台等。目前,入驻和租赁云计算中心基础设施服务和外包运维服务的企事业单位达到120多家。 -公司将自主研发和拥有自主知识产权产品作为重要的发展战略。2012年,公司被批准设立泰安市奶牛数字化精准养殖工程技术研究中心。目前,公司有软件著作权20余项,实用新型专利9项。公司承担的各类科技创新成果转化项目16项,其中国家级项目3项,省级项目10项,市级项目3项。 - -公司注重人才培养建设,不断加大高端技术人才的引进,目前研发队伍中有博士、硕士研究生等资历的人员占80%以上,同时与复旦大学、北京航空航天大学、山东大学、山东农业大学、山东科技大学、泰山医学院等加强“产、学、研、用”的合作,加强了研发队伍建设,加快了平台项目研发,提高了科技成果的转化率。其中山东农业大学、泰山医学院、泰山职业技术学院都在公司设立“教学科研与实践就业基地”。 - -泰安市信息安全与网络应急支援中心在公司设立,肩负着为全市企事业和科研等单位提供网络信息安全应急支援等工作。公司重视专业运维服务队伍建设,目前成立了一支80余人组成的专业运营服务团队,为泰安市120余家政府和企事业单位提供专业外包服务,已经形成了 “向服务要品牌、向服务要市场、向服务要质量、向服务要发展、向服务要效益”的品牌服务战略。 -2015年,公司联合泰安高新技术产业开发区、上海市数据科学重点实验室、山东农业大学联合建设山东神农智谷大数据产业园,共同打造国家级农业大数据产业园区和高端信息中心。 - -公司以集成项目和平台应用为基础,坚持做优做强,以推动“云、物、大、智”产业发展为根本,以云计算创新应用和推动大数据产业发展为方向,努力打造专业的区域云计算运营商和大数据行业应用商、大数据资产管理运营商!",1,0,"published",9999,0,"2017-01-16 21:43:51","2020-06-01 11:11:14","山东众志" -100,50,"白山云","baishanyun6882","upload/20170116/14845758933515.jpg","upload/20170116/14845759262921.jpg","upload/20170116/14845763875830.jpg","http://www.baishancloud.com","400-081-3388",0,10,"白山云科技有限公司是国内首家云链服务提供商,为企业客户提供高效数据内容应用与交换的定制化服务。 -白山率先在中国市场引入云链服务(CCX),其核心是建立数据与数据的连接,为数据的产生、传输、消费和归档提供完整的生命周期服务。云链包括云分发、云存储、云聚合,针对企业数据内容的高速分发、在线存储、流动与整合需求,建立网络服务系统。","白山云科技有限公司是国内首家云链服务提供商,为企业客户提供高效数据内容应用与交换的定制化服务。 -白山率先在中国市场引入云链服务(CCX),其核心是建立数据与数据的连接,为数据的产生、传输、消费和归档提供完整的生命周期服务。云链包括云分发、云存储、云聚合,针对企业数据内容的高速分发、在线存储、流动与整合需求,建立网络服务系统。 -自2015年4月成立以来,白山以云分发为切入点,在泛云服务市场上迅速崛起,首款云分发产品CDN-X迅速进入行业第一梯队。2016年6月,白山发布云存储产品CWN-X。作为白山“云链”系列的第二款产品,其属于分布式对象存储,专注于热数据的存储及存储间的数据转移。 - -目前,白山已签约包括今日头条、美团、秒拍、搜狐、汽车之家、暴风集团等两百多家大中型互联网及企业客户。业务遍布全球,在北京设立总部,厦门、贵安设立研发中心,上海、深圳、广州和美国西雅图设立办公室。白山目前员工中66%为研发人员。",1,0,"published",9999,0,"2017-01-16 22:20:39","2020-06-01 11:11:14","白山云" -101,112,"未来国际","weilaiguoji9107","upload/20170116/14845769338707.png","upload/20170116/14845769372876.png","upload/20170116/14845773404834.jpg","http://www.ourfuture.cn","029-882655555",0,10,"陕西省教育厅依托西安未来国际运营的陕西省政务云平台构建了一个服务于全省教育行政管理部门和学校的教育专属数据中心,使用云主机400余台,该中心支撑和满足教育部系统在陕西省省级教育行政部门的部署和运行维护;集成和支撑全省现有和即将开发部署的各类教育基础数据库和各类教育管理信息系统;","陕西省卫计委公共服务平台包括全员人口、流动人口、技术服务、奖励扶助、出生实名登记等多个业务系统,支撑陕西省全省的各类卫计服务,该平台依托未来国际运营的陕西省政务云构建,使用云主机120余台,服务于上千个基层卫计工作站.",1,0,"published",9999,1,"2020-07-22 20:30:01","2020-06-01 11:11:14","未来国际" -102,0,"达闼科技","dakeji3461","upload/20170121/14850042951775.png","upload/20170121/14850043213696.png","upload/20170121/14850047355700.jpg","http://iaas.cloud.cloudminds.com/","4006633888",0,10,"深圳达闼科技控股有限公司于2015年04月15日在深圳市市场监督管理局前海注册工作组登记成立。法定代表人江海涛,公司经营范围包括计算机数据库的技术开发;计算机系统分析;从事计算机软件及移动终端的技术开发等。","目前,达闼科技正在创建一个全球的高速安全应用网,“天网”(采用完全独立于现有的互联网之外, 利用SDN和SDP技术实现的高安全高性能骨干网);并研发了全球首款基于双芯片和虚拟化技术的云端智能连接终端(AI Mobile),将云端的人工智能应用通过该设备来激活适配的机器人,或将给全球移动终端和IOT产业带来了革命性突破;同时,公司正在研发专门针对健康护理的人工智能应用,未来将用于全球家庭护理机器人的服务;达闼科技独创的移动内联网云服务(MCS)技术,是实现下一代企业移动信息化的“云网端”关键架构,该项技术已经在金融、医疗、党政军、公检法司、大型制造业等多个行业内获得用户的广泛认同。",1,0,"published",9999,0,"2017-01-21 21:19:00","2020-06-01 11:11:14","达闼科技" -103,106,"青云","qingyun1268","upload/20170121/14850053392575.jpg","upload/20170121/14850053626168.png","upload/20170121/14850056172774.jpg","https://www.qingcloud.com/","400-8576-886",0,10,"青云QingCloud(北京优帆科技有限公司)是全球首家实现资源秒级响应并按秒计量的基础云服务商,致力于为企业用户提供安全可靠、性能卓越、按需、实时的IT资源与服务交付平台。","公司于2012年4月正式成立,并于2013年7月推出青云QingCloud云计算平台。发展至今产品线已覆盖完整的IaaS层组件、PaaS/Orchestration层服务、对象存储服务、超融合硬件设备和桌面云等产品与服务,为企业用户提供包括公有云、私有云、托管云、混合云等全面专业的云计算服务与整体解决方案。",2,0,"published",9999,0,"2017-01-21 21:34:38","2020-06-01 11:11:14","青云" -104,64,"大唐网络","datangwangluo3548","upload/20170121/14850060628031.png","upload/20170912/15052025826350.png","upload/20170121/14850061588343.jpg","http://www.datangnet.com.cn","83421999",0,10,"大唐网络是大唐电信旗下专门从事移动互联网创新平台研发和创业项目孵化的公司,致力于面向民生服务和传统产业领域,打造民生互联网和产业互联网创新创业生态。","大唐网络提出了资源型移动互联网发展战略,形成了创新平台+创客空间+创业资源的双创模式。在“双创”实践活动中积累了大量经验和成功案例,被授予国家专业化众创空间和专业化开放平台,成为中央企业践行创新驱动发展战略、落实“互联网+”行动计划的践行者。",3,0,"published",9999,0,"2017-01-21 21:43:50","2020-06-01 11:11:14","大唐网络" -105,57,"广东盈世","guangdongyingshi4286","upload/20170121/14850070704706.jpg","upload/20170121/14850070835012.jpg","upload/20170121/14850070912502.jpg","http://www.coremail.cn","400-000-1631",0,10,"广东盈世计算机科技有限公司专注于提供电子邮件整体技术方案运营服务,拥有双软认证、高科技企业等多种资质。公司自主研发的 Coremail 大规模电子邮件系统 是中国第一套中文电子邮件系统;目前在中国大陆地区拥有超过8亿终端用户,是中国用户实际使用最广泛,最频繁的邮件系统。公司产品涵盖邮件系统、企业邮箱、邮件归档系统、邮件投递系统、海外云镜像加速、企业协同工具等。 - -","广东盈世计算机科技有限公司专注于提供电子邮件整体技术方案运营服务,拥有双软认证、高科技企业等多种资质。公司自主研发的 Coremail 大规模电子邮件系统 是中国第一套中文电子邮件系统;目前在中国大陆地区拥有超过8亿终端用户,是中国用户实际使用最广泛,最频繁的邮件系统。公司产品涵盖邮件系统、企业邮箱、邮件归档系统、邮件投递系统、海外云镜像加速、企业协同工具等。盈世总部设立于广州,在北京、上海、武汉、重庆、西安、济南、沈阳、杭州、厦门等地设有分支机构。",1,0,"published",9999,0,"2017-01-21 21:59:02","2020-06-01 11:11:14","广东盈世" -106,0,"云科数据","yunkeshuju8541","upload/20170121/14850078901924.png","upload/20170121/14850079204953.png","upload/20170121/14850079559922.jpg","http://www.cloudtds.com.cn/ ","400-0471-155",0,10,"内蒙古云科数据服务有限公司(简称:云科数据)坐落于中国云谷·和林格尔。我公司自成立伊始便专注于云计算服务,致力于打造基于互联网的公有云及行业私有云服务,为用户提供全面、高效、专业的解决方案。","内蒙古云科数据服务有限公司(简称:云科数据)坐落于中国云谷·和林格尔。我公司自成立伊始便专注于云计算服务,致力于打造基于互联网的公有云及行业私有云服务,为用户提供全面、高效、专业的解决方案。云科数据在DT时代下秉承担当、协作、创新、服务、共享的理念,为全球企业提供云计算服务,让每一个企业用户享用集低价、便捷于一体的IT基础设施及各种应用软件服务。 -云科数据以保证企业业务连续性为首要目标,为企业提供安全稳定、高可用、低成本的云容灾解决方案。公司以大运维·云服务为理念,为全面保障客户业务的稳定运行,提供标准的、规范的、专业的运维服务,助力我国云计算、大数据产业的发展,实现人类“绿色发展的绿色消费”。 -",1,0,"published",9999,0,"2017-01-21 22:13:49","2020-06-01 11:11:14","云科数据" -107,34,"中移物联网","zhongyiwulianwang3655","upload/20170121/14850084065078.png","upload/20170121/14850084095331.png","upload/20170121/14850084157856.jpg","http://open.iot.10086.cn/","13637892912",0,10,"中移物联网有限公司是中国移动通信有限公司出资成立的全资子公司,公司位于“国家新型工业化物联网产业示范基地”——重庆市南岸区。","中移物联网有限公司是中国移动通信有限公司出资成立的全资子公司,公司位于“国家新型工业化物联网产业示范基地”——重庆市南岸区。公司以市场化机制运营中国移动公众物联网,研发、设计、生产物联网专用芯片模组和终端,开发智能家居、车联网等特色产品,打造OneNET物联网开放平台,推广行业解决方案,致力于推动物联网在各行业的规模化应用。物联网公司以“网络、平台为基础,模组为抓手,开发合作应用产品为触点”,在智能管道、开放平台、数字化产品、智能模组、行业解决方案五个方面进行能力储备与产品研发,形成“云-管-端”全方位布局的业务体系架构。",1,0,"published",9999,0,"2017-01-21 22:21:40","2020-06-01 12:06:58","中移物联" -108,0,"互联港湾","huliangangwan3859","upload/20170121/14850096765478.png","upload/20170121/14850097496470.png","upload/20170121/14850098751685.png","http://www.hunheyun.com","400-678-8875",0,10,"北京互联港湾科技有限公司成立于2009年,是一家行业领先的混合云服务提供商,致力于为全球用户提供优质的混合云服务、云托管服务、技术服务及其他综合性IT服务。通过与中国电信、中国移动、中国联通、科技网、教育网等运营商长期紧密合作,互联港湾在北京、上海、广州等地部署有互联互通的多线BGP网络,并打通了北上广之间的二层波分传输网络,在全国各主要城市及香港部署有SDN骨干网。截至目前,互联港湾已成功以北京、上海、广州、深圳为核心,辐射华北、华东、华南三大中国重点核心经济区域,并在香港、北美积极布局。","互联港湾公司混合云平台通过公有云+私有云+云托管+跨云管理的全方位服务体系,结合SDN(软件定义网络)、CDN(内容分发网络)等增值服务,同时涵盖自动化运维、监控告警、自服务IT和持续交付,支持异地多中心和异构网络架构,实现分布式部署在多个数据中心的公有云、私有云、物理架构之间的内网互通,降低用户基础资源投入,并对多云平台的资源进行更直观的调配与管理,满足用户灵活部署业务的需求,为用户的底层资源搭建提供一站式解决方案,同时为用户应用层业务提供各类增值服务。 - 互联港湾混合云平台创建出符合企业需求的定制化系统,处理更加复杂多变的企业业务,为用户提供稳定可靠的计算、存储、网络服务和交付高效敏捷、业务快速响应的服务功能,融合企业级客户it管理思路,通过对动态的基础架构、快速的开发、自动化的运维、敏捷的数据获取和展示以及应用交付的便捷支撑,最终实现对复杂多变的企业业务的快速支撑。 -",0,0,"draft",9999,1,"2017-01-21 22:45:46","0000-00-00 00:00:00","" -109,0,"EasyStack","easystack7606","upload/20170216/14872526161175.png","upload/20170216/14872528272376.png","upload/20170216/14872531427333.jpg","http://www.easystack.cn/","待录入",12,0,"待录入","待录入",0,0,"draft",9999,1,"2017-02-16 21:52:24","0000-00-00 00:00:00","" -110,114,"鹏博士","pengboshi4647","upload/20170216/14872534711475.png","upload/20170216/14872534769262.png","upload/20170216/14872534815549.jpg","http://www.drcloud.cn/","待录入",14,0,"http://www.drcloud.cn/","http://www.drcloud.cn/",1,0,"published",9999,0,"2017-02-16 21:58:44","2020-06-01 11:11:14","鹏博士" -111,0,"山东众志","shandongzhongzhi2143","upload/20170216/14872536805268.png","upload/20170216/14872536844365.png","upload/20170216/14872536876929.jpg","http://www.tacloud.net/","待录入",15,0,"待录入","待录入",0,0,"published",9999,1,"2017-02-16 22:02:44","0000-00-00 00:00:00","" -112,0,"未来国际","weilaiguoji8069","upload/20170216/14872539412600.png","upload/20170216/14872539466410.png","upload/20170216/14872539508089.jpg","http://www.ourfuture.cn/","待录入",17,0,"待录入","待录入",0,0,"draft",9999,1,"2017-02-16 22:06:21","0000-00-00 00:00:00","" -113,0,"卓易","zhuoyi4249","upload/20170522/14954282849673.png","upload/20170216/14872587419901.png","upload/20170216/14872587448757.jpg","http://www.droibaas.com","021-33563840",16,0,"DroiBaaS由上海卓易科技股份有限公司(833711)设计、开发, 并为中小企业及开发者提供高效、稳定、安全的一站式后端云服务。","作为数据中心联盟的理事会成员,DroiBaaS拥有从应用开发、测试到应用分发、移动营销、流量变现的全产业链服务能力。自2012年以来,DroiBaaS团队已经累计服务于数百万优秀移动应用,并拥有同时服务数个过亿用户产品的成功经验。",1,0,"published",9999,0,"2017-02-16 23:26:08","2020-06-01 11:11:14","卓易" -114,0,"怡合春天","hechuntian4309","upload/20170216/14872589363891.png","upload/20170216/14872589403328.png","upload/20170216/14872589445243.jpg","https://www.benmu-health.com/","待录入",5,0,"待录入","待录入",1,0,"published",9999,0,"2017-02-16 23:29:32","2020-06-01 11:11:14","怡合春天" -115,49,"一一五","yiyiwu5594","upload/20170216/14872593131929.png","upload/20170216/14872593171666.png","upload/20170216/14872593205302.jpg","http://qiye.115.com/","待录入",7,0,"待录入","待录入",1,0,"published",9999,0,"2017-02-16 23:35:48","2020-06-01 11:11:14","一一五" -116,115,"立思辰银山","lisichenyinshan6551","upload/20170216/14872597771680.png","upload/20170216/14872597807992.png","upload/20170216/14872597885992.jpg","http://www.nxeduyun.com/","待录入",1,0,"待录入","待录入",1,0,"published",9999,0,"2017-02-16 23:43:32","0000-00-00 00:00:00","" -117,36,"北京光环新网科技股份有限公司","beijingguanghuanxinwangkejiguf","202004/1585712168632667202.jpg","upload/20170216/14872600704299.png","upload/20170216/14872600743122.jpg","http://www.sinnetcloud.com.cn","4000-988-000",9,0,"\"北京光环新网科技股份有限公司成立于1999年1月(股票代码300383),是国内专业的数据中心及云计算服务提供商。公司主营业务为互联网数据中心服务(IDC及其增值服务)、云计算服务、互联网宽带接入服务(ISP)等互联网综合服务。 -光环新网致力于以先进技术、优质资源和高品质服务推动互联网创新发展,为用户提供更加高速、稳定、安全的互联网环境。经过近二十年积累与深耕,公司累计服务企业客户逾万家,树立了优秀的行业口碑,在市场上享有领先的市场占有率和较高的品牌知名度。","待录入",4,0,"published",9999,0,"2017-02-16 23:48:36","2020-06-01 12:06:44","光环新网" -118,58,"国裕数据","guoyushuju8452","202004/1585895093678586533.gif","upload/20170216/14872604024527.png","upload/20170216/14872604054018.jpg","http://www.gophagroup.com/index.html","13936399539",2,0,"零雲云资源服务平台是一个提供按需分配、弹性可伸缩的计算能力的云服务。通过使用零雲,用户不必再为了软件开发、测试、以及应用/服务上线运行而购买物理设备了,也不必投入人力物力进行设备维护。用户可以直接在零雲中迅速得到任意需要的计算、存储、网络、安全等方方面面的能力,并可以随时调整它们的规模。","待录入",1,0,"published",9999,0,"2017-02-16 23:53:44","2020-06-01 11:11:14","国裕数据" -119,0,"亿林网络","yilinwangluo5308","upload/20170216/14872606753248.png","upload/20170216/14872606795529.png","upload/20170216/14872606824153.jpg","http://www.cnhlj.cn","待录入",8,0,"http://www.cnhlj.cn","http://www.cnhlj.cn",1,0,"published",9999,0,"2017-02-16 23:58:34","2020-06-01 11:11:14","亿林网络" -120,59,"江河瑞通","jiangheruitong4616","202004/1585902835049134433.png","upload/20170217/14872609446127.png","upload/20170217/14872609488531.jpg","http://www.rtongcloud.com","010-88892470 010-88892471 010-88892472 010-88892473",11,0,"江河瑞通(北京)技术有限公司致力于为水利(防洪减灾、水资源管理、高效农业节水灌溉)、水务(供水、排水、城市雨洪)、水环境(流域体系水环境、城市区域水环境、海洋生态环境)提供从IT基础服务到物联网、大数据与云服务的全过程服务,并依托物联网云平台(www.RTongCloud.com)打造起水行业产业生态链, 实现从“智能感知、智能仿真、智能诊断、智能预警、智能调度、智能处置、智能控制”到“智能服务”的全生态“智慧水管理服务体系与平台”!","待录入",1,0,"published",9999,0,"2017-02-17 00:02:47","2020-06-01 11:11:14","江河瑞通" -121,60,"宇信数据","yuxinshuju1897","202004/1585898441791026714.png","upload/20170217/14872611594200.png","upload/20170217/14872611625884.jpg","http://www.yuxindata.com","010-61844366",16,0,"宇信数据科技有限公司,成立于2010年。公司成立伊始, 即专注于金融业IT托管运营服务,经过多年努力,目前,已经具备了完善的金融云服务能力,服务范围覆盖IaaS、PaaS、SaaS三大领域,并能够提供全渠道互联网金融平台、核心银行系统、新一代客服系统、营销分析平台等金融业最重要的IT基础产品。","待录入",1,0,"published",9999,0,"2017-02-17 00:06:46","2020-06-01 11:11:14","宇信数据" -122,0,"浦东软件园","pudongruanjianyuan9756","upload/20170217/14872617789250.png","upload/20170217/14872617842182.png","upload/20170217/14872617884747.jpg","https://wizcloud.com.cn/","待录入",1,0,"待录入","待录入",1,0,"published",9999,0,"2017-02-17 00:16:51","2018-08-17 14:31:36","" -123,0,"快云","kuaiyun1718","upload/20170912/15052078653371.png","upload/20170912/15052078693169.png","upload/20170912/15052078727490.jpg","http://www.kuaiyun.cn","4007179999",5,0,"快云信息科技有限公司成立于2015年,注册资金5000万元,隶属景安网络(股票代码:832757),为专业的云计算公司。目前在北京、广州、郑州、长沙设立有四家分公司,拥有员工近200人。","快云信息科技有限公司成立于2015年,注册资金5000万元,隶属景安网络(股票代码:832757),为专业的云计算公司。目前在北京、广州、郑州、长沙设立有四家分公司,拥有员工近200人。 -公司自主研发云计算平台,提供计算、存储、网络等企业必需的基础IT资源,同时深入了解互联网、移动互联网、传统企业等不同的应用场景,为其提供相应的行业解决方案。 -现公司主营业务有:快云服务器、快云VPS、快云数据库、快云存储等计算存储产品,快云抗D、快云CDN、快云SSL、快云Waf等云安全产品,以及快云行业解决方案等一系列云计算产品。目前快云依托郑州、北京、香港等云计算节点已为超过5万家的企业用户提供了云计算服务,致力成为国内可信赖、优口碑的专业云计算服务提供商。",1,0,"published",9999,0,"2017-07-24 11:20:33","2020-06-01 11:11:14","快云" -124,108,"甘肃万维信息技术有限责任公司","gansuwanweixinxijishuyouxianze","upload/20170912/15052039208028.png","upload/20170912/15052039376640.png","upload/20170912/15052039428796.png","http://www.wanwei.cn","4008876330",5,0,"中国电信甘肃万维信息技术有限责任公司成立于1998年,是中国电信股份有限公司的全资子公司,公司依托于中国电信全国垂直一体化的营销服务体系和运行维护体系,凭借中国电信丰富的网络资源、专业的电信级IT技术、优秀的服务团队、广泛的客户资源和行业知识,主要从事软件开发、互联网运营、技术服务和系统集成四大业务,致力于为广大客户提供卓越的ICT整体解决方案。"," 甘肃万维信息技术有限责任公司的股东为中国电信股份有限公司,持股比例100%,属于国有企业,同中国电信股份有限公司于2005年正式挂牌上市。公司注册资金5000万元,位于兰州高新技术产业开发园区,办公面积13000多平方米。截止2016年底,公司拥有资产总额3.11亿元,其中流动资产2.87亿元,固定资产3554万元。 -公司近几年营业收入保持高速增长, 2014年收入总额31218万元,2015年收入总额48132万元; 2016年实现公司收入65520万元。2016年投入研发经费6750万元,近三年,累计投入技术研发经费1.4亿元,研发投入占公司总收入的10%。 -公司依托中国电信强大的基础资源、技术资源以及人才资源,长期专注于行业客户ICT解决方案提供,业务涵盖政务云、教育云、医疗云、旅游云、在线服务、移动行业应用、IDC业务等。具有大量政企行业成功案例,参与设计实施了众多省市级电子政务项目,以及医疗、旅游信息化建设项目,有效拉动了当地的信息消费和劳动就业。当前公司已跻身国内一线ICT服务商之列,为各地落实“智慧城市”战略和“互联网+”行动计划发挥着主力军的作用。 -随着业务规模和研发能力的提升,公司相继成立北京分公司、上海研发中心和杭州研发中心。依托在产品和运营能力方面的竞争优势,先后成为中国电信集团全国教育行业信息化基地和医疗行业信息化基地。为深入拓展教育行业应用领域特设立兰州中电乐智教育科技有限公司,以促进通信主业规模发展。在全省打造纵向一体的支撑体系,成立14个ICT市州分公司。 -公司依托中国电信集团共享服务中心,建成了总部、大区、地市分公司多级的支撑服务体系,拥有350余名具备国内外IT认证和丰富运营维护经验的技术服务人员,为客户提供7*24小时的技术支持,服务网络覆盖全国、服务体系遍及地市。 -甘肃万维公司尊重人才,以人为本,近年来吸引和培养了一批拥有博士、硕士、学士学位并通过系统分析师、系统架构师、微软及思科等认证的优秀人才,实现了人才结构和公司发展的良好匹配。公司现有员工1100名,本科及以上学历员工占总人数的85%以上,30%以上的员工拥有国际或国内的中高级认证证书,研发人员704人,其中从事软件开发的人员有390人,从事技术服务和云计算相关人员314人。技术专家方面,现有甘肃省领军人才1人,省级优秀专家1人,“555人才”1人,“151人才”7人,15人次获得省部级及中国电信集团的科技进步奖。",1,0,"published",9999,0,"2017-07-24 11:22:56","2020-06-01 12:07:22","甘肃万维" -125,0,"小鸟云","xiaoniaoyun1888","upload/20170912/15052104724569.png","upload/20170912/15052104772332.png","upload/20170724/15008666348629.jpg","http://www.niaoyun.com","400-688-3065",5,0,"深圳前海小鸟云计算有限公司创立于2015年7月,是一家专注于云计算技术研发创新,致力于打造一站式云计算服务,为用户提供可信赖的企业级公有云服务的公司。 - -作为国内创新型云计算服务商,小鸟云以高性能云服务器作为服务价值的驱动链条,旨在打造“产品+平台+客户+服务”的开放闭环生态圈,并希望通过不断完善自身实力,大力推动云计算产业向健康、良性的方向发展,从而营造优质、稳定的云服务生态圈。","“小鸟云”是深圳前海小鸟云计算有限公司旗下的云计算服务品牌,专注为个人开发者用户、中小型、大型企业用户提供一站式核心网络云端部署服务,促使用户云端部署化简为零,轻松快捷运用云计算。",2,0,"published",9999,1,"2020-09-23 10:51:38","2020-06-01 11:11:14","小鸟云" -126,0,"国研网络","guoyanwangluo9705","upload/20170916/15055300146307.png","upload/20170916/15055300186169.png","upload/20170916/15055300233821.png","http://www.sritidc.com","4000101121",5,0,"北京国研网络数据科技有限公司为国研信息科技有限公司全资子公司,成立于2001年,注册资金人民币2000万元。2002年正式通过ISO9001:2000质量管理体系认证,同年被认定为北京市高新技术企业。 -公司专注于互联网运营和电信相关业务,向客户提供多方位、高质量的专业基础网络服务,业务涵盖了电信级机房托管、互联网接入、城域网建设等方面。 -核心业务已经从基础网络服务业务发展到现阶段横跨基础网络服务、IT运维外包、IT应用解决方案等综合业务,实现政府和企事业单位的"一站"IT服务。网络数据人有理由相信:通过未来五年的发展,在舆情监管、社会服务综合管理,成为中国最好的"一站式"IT综合云服务提供商。"," 北京国研网络数据科技有限公司为国研信息科技有限公司全资子公司,成立于2001年,注册资金人民币2000万元。2002年正式通过ISO9001:2000质量管理体系认证,同年被认定为北京市高新技术企业。 - - 在秉承国研信息科技有限公司股东资源优势的基础上,公司一直专注从事互联网运营和电信相关业务,向客户提供多方位、高质量的专业基础网络服务,业务内容涵盖了电信级机房托管、互联网接入、城域网建设等方面。 - - 2000年,国内首批IDC运营商资质,国内首批全网ISP运营商资质,国内最优秀的IDC/ISP运营商之一。2006年开始,连续6年通过公开招标成为北京市电子政务互联网接入服务定点供货商。 - - 近年来,公司在做强做精基础网络服务业务的同时,密切关注政府在信息化推进方面的政策及方向,积极跟踪先进的技术、依托强大的研发和顾问团队,在政府及企事业单位的云计算和物联网解决方案方面献计献策,提出了基于先进技术的舆情监管平台、社会服务综合管理平台、医院辅助业务综合管理平台等综合解决方案。方案已经在实践中得到了有效应用,并获得各级政府部门及企事业单位的高度评价。 - 网络数据公司在十几年的发展历程中,不断开拓进取,密切关注IT技术发展的最新方向和动态,结合自身的优势,不断发展壮大。核心业务已经从最初的基础网络服务业务发展到现阶段横跨基础网络服务、IT运维外包、IT应用解决方案等综合业务,真正实现了为政府和企事业单位的"一站"IT服务。网络数据人有理由相信:通过未来五年的发展,在舆情监管、社会服务综合管理、医院辅助业务综合管理方面,成为中国最好的"一站式"IT综合云服务提供商。 -  企业理念:创新、求实、协同、服务 - -  发展准则:以客户为中心,以技术为基础,以质量求生存,以诚信求发展",2,0,"published",9999,0,"2017-07-24 11:28:30","2020-06-01 11:11:14","国研网络" -127,0,"太极计算机股份有限公司","taijijisuanjigufenyouxiangongs","upload/20170912/15052148906296.png","upload/20170912/15052149118519.png","upload/20170912/15052149201084.png","http://www.taiji.com.cn","010-57702888",5,0,"太极计算机股份有限公司(以下简称太极公司)隶属于中国电子科技集团公司(CETC:国家十大军工集团之一,国务院授权的投资机构之一,目前由国务院国有资产监督管理委员会直接监管)第15研究所。其前身太极计算机公司始创于1987年。2002年,经信息产业部、国家经贸委批准,吸收其它投资资本,整体改制为太极计算机股份有限公司。2010年3月12日,太极公司在深交所正式挂牌交易(太极股份,002368)。到2017年,公司已成立30周年,现有员工3500多人。公司总部设在北京,在上海、天津、山东、西安等11个省市设有分公司,在广州、南京、浙江、成都等9个省市设有办事处。参股或控股11家子公司。","太极政务云提供电子政务云建设、咨询、运营、云安全等服务。 -1、建设服务包括基础设施层建设(如网络、服务器、存储等),支撑平台建设(如云管理平台、开发测试平台、大数据平台等),云应用建设(云OA、经济运行监测、数据分析、信用、政务审批等),云安全建设(如物理安全、虚拟化安全、数据安全、应用安全等),运维管理系统建设(运维体系建设、管理体系建设等); -2、咨询服务包括私有云咨询规划,政务云咨询规划,云安全咨询规划等服务,容灾咨询规划等服务; -3、运营服务包括云主机租赁、云存储租赁、数据备份、系统迁移、业务系统监控、7*24小时运维管理等服务; -4、云安全服务包括信息安全等级保护3级相关安全保障,技术支持等服务。",2,0,"published",9999,0,"2017-07-24 11:30:30","2020-06-01 12:07:44","太极公司" -128,63,"兴业数金","xingyeshujin1539","202003/1585017453439121651.png","upload/20170724/15008670696810.png","upload/20170724/15008670727985.jpg","http://www.cibfintech.com","021-20567666",5,0,"兴业数字金融服务(上海)股份有限公司(简称“兴业数金”),是兴业银行集团旗下金融科技子公司。面向集团,作为集团高科技内核和创新孵化器,兴业数金通过上海、成都、杭州、深圳及其他区域研发创新团队,全面负责兴业银行集团科技研发和数字化创新工作。对外,兴业数金致力于运用云计算、人工智能、开放API、流程机器人等前沿科技,为商业银行数字化转型提供解决方案,输出科技产品与服务。","待录入",3,0,"published",9999,0,"2017-07-24 11:32:04","2020-06-01 11:11:14","兴业数金" -129,0,"新网数码","xinwangshuma3581","upload/20170918/15057158232511.png","upload/20170918/15057159674501.png","upload/20170918/15057159702408.png","http://www.xinnet.com","4008182233",5,0,"新网跟随互联网的原点起来,新网作为互联网基础应用服务提供商,推动着互联网潮流在中国的蓬勃发展… -24年峥嵘岁月,新网历经一代人的努力,从域名出发,发展成为具有:域名注册管理、云计算、企业通讯管理等综合业务的科技服务公司。期间我们为京东、新浪等数千万家企业客户提供了互联网基础应用服务,并和我们生态合作伙伴一起,见证了中国互联网从模仿到引领,从概念到真正改变人们生活的进程。 -展望明天,新网仍将秉承“敢想,更敢为”的经营理念,持续强化我们在云计算领域战略优势,并积极拓展企业信息自动化与Saas市场前沿地位。与数万家生态合作伙伴携手,为中国企业的信息化发展提供更优质、高效的基础设施服务。 -","1993年,国内第一根internet专线正式接入,开启了中国互联网的元点。在那个没人了解internet意味着什么年代,新网创始团队想象到了一个充满希望的互联网未来,并义无反顾的加入到这个潮流。在之后的23年中,新网作为互联网基础应用服务提供商,推动着互联网潮流在中国的蓬勃发展… -24年峥嵘岁月,新网历经一代人的努力,从域名出发,发展成为具有:域名注册管理、云计算、企业通讯管理等综合业务的科技服务公司。期间我们为京东、新浪等数千万家企业客户提供了互联网基础应用服务,并和我们生态合作伙伴一起,见证了中国互联网从模仿到引领,从概念到真正改变人们生活的进程。 -长关苦渡凌峰笑,不尽星河万里同。 -展望明天,新网仍将秉承“敢想,更敢为”的经营理念,持续强化我们在云计算领域战略优势,并积极拓展企业信息自动化与Saas市场前沿地位。与数万家生态合作伙伴携手,为中国企业的信息化发展提供更优质、高效的基础设施服务。 -",2,0,"published",9999,0,"2017-07-24 11:33:44","2020-06-01 11:11:14","新网数码" -130,132,"中移苏研","zhongyisuyan7256","upload/20170918/15057177026351.png","upload/20170918/15057177068382.png","upload/20170918/15057177097511.jpg","https://www.bigcloudsys.cn:8443","4001-10086-5",5,0,"中移(苏州)软件技术有限公司(又称中国移动苏州研发中心)是中国移动通信集团公司的全资子公司。公司依托中国移动雄厚的技术积累、海量的数据资源以及海内外高级专业人才,在云计算、大数据和IT支撑领域建立了完善的产品体系,业务范围覆盖产品研发、软件销售、系统集成及运营支撑等领域,目标成为国内一流、世界领先的IT服务提供商。","中移(苏州)软件技术有限公司(又称中国移动苏州研发中心)是中国移动通信集团公司的全资子公司。公司依托中国移动雄厚的技术积累、海量的数据资源以及海内外高级专业人才,在云计算、大数据和IT支撑领域建立了完善的产品体系,业务范围覆盖产品研发、软件销售、系统集成及运营支撑等领域,目标成为国内一流、世界领先的IT服务提供商。",3,0,"published",9999,0,"2017-07-24 11:36:04","2020-06-01 11:11:14","中移苏研" -131,0,"大河云联","daheyunlian5500","upload/20180530/15276534964531.png","upload/20180530/15276535002258.png","upload/20180530/15276535037105.jpg","http://www.dahonetworks.com/","400-076-8888",5,0,"大河云联是面向全球的云连接服务提供商,致力于通过SD-WAN产品和技术,为IDC提供商、云服务商、网络服务商和企业用户带来更灵活、更智能、更高性价比的场景化网络连接服务。 -大河云联目前提供的产品包括:DAHO CONNECT全方位灵活组网产品,以及CanalON(Open Network)SD-WAN软硬一体化解决方案。 -","作为国内SD-WAN的引领力量,大河云联的产品已经成功应用于多个运营商,赋能并协同合作伙伴一道成长,在显著提升专线产品的交付速度和灵活性的同时,提高了线路利用率和投资回报率。通过连接数据中心和云平台,大河云联的服务广泛应用于互联网、政府、金融、游戏、直播、教育等行业,极大的降低了高质量专线产品的门槛,为超百家企业带来按需、灵活的连接体验。",1,0,"published",9999,0,"2017-07-24 11:39:17","2020-06-01 11:11:14","大河云联" -132,140,"天津卓朗科技发展有限公司","tianjinzhuolang6210","202007/1594277262926028701.png","upload/20170918/15057180867560.png","upload/20170918/15057180905941.jpg","www.troila.com","4000052220",5,0,"卓朗科技创立于2009年,是国内领先的企业级信息技术服务提供商,我们致力于改善人和机器的工作,驱动制造业高质量发展,构建数字智能城市,满足人民幸福生活,推动数字经济发展。 - 我们面向智能城市和智能制造等领域为企业、政府和各类组织提供安全可信、稳定可靠、智能开放和有竞争力的产品、服务与解决方案,同业务伙伴开放合作,持续利用先进的信息技术不断为客户创造价值,释放机器潜能,丰富个人工作,激发组织创新。我们是中国云计算公司500强企业、天津市服务业百强企业。","天津卓朗科技发展有限公司成立于2009年,主要从事云计算、软件开发和信息系统集成等业务。业务覆盖东北、华北、环渤海区域及周边城市,并保持高速稳定的增长。 - 作为一家创新驱动发展的企业,卓朗科技在核心业务领域拥有多项自主知识产权。目前,公司拥有专利技术6项,软件著作权49项,科技成果登记43项,软件产品登记10项。 - 作为天津市第一批小巨人企业,卓朗科技历经八年的时间,实现跨越式增长,主营收入业务从930万元增长到6亿元。公司连续两年在工信部公布的“天津市软件业务收入百强企业”中进入前三甲;连续两年在天津市商务委公布的“天津市服务外包百强企业”中进入前十强;",1,0,"published",9999,0,"2017-07-24 11:42:39","2020-07-09 15:51:53","天津卓朗" -133,0,"武汉极意","wuhanjiyi3024","upload/20170724/15008677989980.png","upload/20170724/15008677994743.png","upload/20170724/15008678011800.jpg","http://www.geetest.com","待录入",5,0,"待录入","待录入",1,0,"published",9999,0,"2017-07-24 11:44:13","2020-06-01 11:11:14","武汉极意" -134,61,"用友","yongyou1861","upload/20170913/15052742305551.jpg","upload/20170913/15052742375284.jpg","upload/20170913/15052742427895.jpg","https://www.yonyoucloud.com/","400-6815-456",5,0,"用友云致力于为企业提供一站式的社会化云服务。支持企业业务创新,让企业实现更加敏捷的经营;支持企业管理变革,让企业管理变的更加轻松和智能;帮助企业实现IT升级,使企业能够拥有一个更加简便的IT,实现增长收入、降低成本、提高效率、控制风险。 -目前,用友云已聚合超过260万家客户,形成社会化商业下的企业服务新生态。作为用友新时期的核心战略产品服务,用友云有两个定位:面向企业客户,是社会化商业应用的技术设施服务,针对产业,是企业服务产业的共享平台。用友云将于生态伙伴一起服务千万级企业和公共组织,推进中国企业的互联网化和金融化发展,服务我国企业IT和产业的转型升级。","用友云是面向社会级商业应用的基础设施,中国领先的商业(企业)云服务平台,致力于帮助企业、组织、团体等快速构建云应用服务,提供全面的云技术解决方案与云产品。用友云为商业应用提供高价值的资讯(咨询)与服务,推动中国社会级商业云服务市场的创新创业。拥有云市场、云平台、云服务(云产品)、云开发等业务。 - -2017年用友基于云计算、大数据、移动互联网、人工智能、物联网等新一代技术,推出全新的社会级商业应用服务——用友云,覆盖PaaS、SaaS、BaaS和DaaS层。 - -定位于社会级商业应用基础设施的用友云包括用友云平台、营销云、财务云、人力云、采购云、社交与协同办公云、畅捷通云、金融服务云、制造云、建筑云、餐饮云、汽车云、烟草云、政务云等产品。 - -从技术架构来看,用友云的IaaS层是与战略伙伴合作构建,提供计算能力基础服务;IaaS层之上是技术平台,包含分布式中间件、应用开发平台、云运维平台等服务;技术平台层之上是BaaS应用层,用友将应用平台层的框架服务、组件等,形成独立的BaaS应用,如表单、审批、通信等能力。 - -从应用架构看用友云,最重要的特点是聚焦在业务的纵向打通。在ERP时代,企业信息化注重从管理维度上人力、财务、营销、采购等等业务方向横向打通;而在云时代,企业互联网化更倾向于特定领域的纵向、垂直打通,将每项业务从建模、管理、运营、交易等全线打通。从横向到纵向,这也是用友3.0一个非常显著的特征。",8,0,"published",9999,0,"2017-07-24 11:46:32","2020-06-01 11:11:14","用友" -135,0,"随锐科技股份有限公司","suiruikejigufenyouxiangongsi28","upload/20170912/15052117833735.png","upload/20170912/15052117887637.png","upload/20170912/15052118025697.png","http://www.suirui.com","4000106066",5,0,"随锐科技于(证券代码:835990)2006年创立于北京海淀中关村园区,是一家专注企业互联网及视频通信、人工智能领域的高科技公司。公司集创新、设计、研发、销售与服务于一体,坚持自主品牌、自主研发,坚持走“以创新科技产品驱动公司发展”的主营道路。多年来一直致力于为政企客户群体提供全方位、全兼容的视频通信云软件、硬件及云服务。通过交付高可靠的视频通信云产品与服务,高效解决客户的 “开会”(远程沟通)以及实时视频会商等需求。 -","随锐科技于(证券代码:835990)2006年创立于北京海淀中关村园区,是一家专注企业互联网及视频通信、人工智能领域的高科技公司。公司集创新、设计、研发、销售与服务于一体,坚持自主品牌、自主研发,坚持走“以创新科技产品驱动公司发展”的主营道路。多年来一直致力于为政企客户群体提供全方位、全兼容的视频通信云软件、硬件及云服务。通过交付高可靠的视频通信云产品与服务,高效解决客户的 “开会”(远程沟通)以及实时视频会商等需求。 -",1,0,"published",9999,0,"2017-07-24 11:48:09","2020-06-01 12:08:34","随锐科技" -136,107,"云际视界","yunjishijie8887","upload/20170918/15057166308382.jpg","upload/20170918/15057166573047.jpg","upload/20170918/15057166626851.jpg","http://www.onecc.net","4009902133",5,0,"云际视界是一家专注于企业互联网及视频通信领域的高科技企业,基于自主研发的基础设施混合云平台,采搭建了协作应用支撑云平台,提供安全、高清、稳定、 跨平台、跨终端、弹性可扩展的运营级视频协作云服务,解决客户远程沟通以及实时视频会议等商务需求。公司自主研发了协作云管理平台。云际视界还组建了客服中心,随时响应客户的请求处理和质量管理;组建了NOC中心,监控平台设备状态和日常运维,保障云服务的稳定与安全。 - 云际视界以成为中国专业的企业协作云服务提供商为愿景,面向企业提供端到端的解决方案,帮助政府、医疗等行业实现互联互通的产业协作生态。","杭州云际视界科技有限公司(以下简称“云际视界”)总部位于杭州,在北京、广州等地设有分支机构,公司拥有多张电信增值业务牌照。 - 云际视界是一家专注于企业互联网及视频通信领域的高科技企业,基于自主研发的基础设施混合云平台,采用全球领先技术,搭建了协作应用支撑云平台,并通过互联网以及软硬件视频终端,为广大企业提供安全、高清、稳定、 跨平台、跨终端、弹性可扩展的运营级视频协作云服务,致力于可靠、高效解决客户远程沟通以及实时视频会议等商务需求。为了更好地支撑平台运营,为客户提供更加优质的服务体验,公司自主研发了协作云管理平台,包括面向用户的一站式会议管理平台、网上营业厅,和面向平台运营和商务运营的Boss系统。同时,云际视界还组建了专业的客服中心,随时响应客户的请求处理和质量管理;组建了NOC(network operations center)中心,监控平台设备状态和日常运维,保障云服务的稳定与安全。 - 云际视界以成为中国专业的企业协作云服务提供商为愿景,面向企业提供端到端的解决方案,帮助政府、医疗、教育、金融等行业实现互联互通的产业协作生态。",1,0,"published",9999,0,"2017-07-24 11:51:31","2020-06-01 11:11:14","云际视界" -137,62,"厦门帝恩思科技股份有限公司","xiamendiensikejigufenyouxiango","upload/20181023/15402750689747.png","upload/20181023/15402750711842.png","upload/20181023/15402751675970.png","https://www.dns.com","400 008 0908",50,0,"厦门帝恩思科技股份有限公司(DNS.COM)多年专业从事DNS技术研发领域,专注于域名解析与DNS安全事业,为域名注册商、CDN服务商、云厂商、运营商等互联网企业,以及党政军、金融、教育等行业用户提供DNS整体技术解决方案。倾力打造新一代稳定、安全、智能的域名安全解析服务。","厦门帝恩思科技股份有限公司(DNS.COM)是国内知名的DNS安全服务提供商,成立于2012年,新三板挂牌企业,证券代码837018。先后获得高新技术企业证书、信息系统安全等级保护3级认证、可信云企业级SaaS认证,DNS硬件产品CCC证书与公安部销售许可证等资质。 -多年专业从事DNS技术研发领域,专注于域名解析与DNS安全事业,为域名注册商、CDN服务商、云厂商、运营商等互联网企业,以及党政军、金融、教育等行业用户提供DNS整体技术解决方案。 -倾力打造新一代稳定、安全、智能的域名安全解析服务,安全可靠,指引未来!",2,0,"published",9999,0,"2017-07-24 11:52:58","2020-06-01 12:08:57","厦门帝恩思" -138,33,"网易云","wangyiyun5668","upload/20170913/15052690355901.png","upload/20170913/15052690508758.png","upload/20170913/15052690557382.png","https://www.163yun.com/","待录入 ",5,0," 网易云,网易集团旗下云计算和大数据品牌,业界领先的高品质场景化云服务和大数据服务提供商。 - 网易云致力于为客户提供云计算基础服务(网易蜂巢)、通信与视频(网易云信和视频云)、云安全(网易易盾)、全智能云客服(网易七鱼)等一系列场景化云服务以及大数据开发与管理平台(网易猛犸)、企业级大数据可视化分析平台(网易有数)、大数据精准营销服务平台等一系列大数据产品,以求更好,更快,更人性化地满足客户业务需求,帮助解决业务痛点,实现业务目标,助力企业客户成功,共创云上精彩世界。"," 网易云,网易集团旗下云计算和大数据品牌,业界领先的高品质场景化云服务和大数据服务提供商。 - 脱胎于网易杭州研究院,网易云为网易全线互联网产品提供公共基础技术平台,拥有多年支撑亿级用户互联网产品的丰富经验,并经历了网易互联网产品全面云化的实践和验证。网易云最大优势来自于其对云的理解更贴近客户的业务需求,对客户业务领域有着深入的理解与洞察。 - 网易云从“互联网/互联网+”客户的实际需求出发,创造性地提出场景化云服务, 基于网易二十年对互联网产品研发体系及运营模式的丰富经验和深入探索,网易云已面向市场推出云计算基础服务(网易蜂巢)、通信与视频(网易云信和视频云)、云安全(网易易盾)、全智能云客服(网易七鱼)等一系列场景化云服务,致力于更好更快地满足客户业务需求,帮助解决业务痛点、实现业务目标。 - 同时,网易云基于自有大数据核心产品,整合全网易数据,集多年经验打造的大数据平台(网易大数据),广泛服务于电商、金融、文娱、教育和制造等领域客户。网易大数据先后推出大数据管理与应用开发平台(网易猛犸)和企业级大数据可视化分析平台(网易有数)两大产品,为企业经营决策、业务洞察、运营优化和精准营销创造价值。 - 网易云的愿景: 以高品质的场景化云服务和大数据服务平台助力客户成功,共创云上精彩世界。",4,0,"published",9999,0,"2017-07-24 16:05:36","2020-06-01 11:11:14","网易云" -139,116,"杭州尚尚签网络科技","hangzhoushangshangqianwangluok","upload/20180303/15200575299108.png","upload/20180303/15200575339799.png","upload/20180303/15200575376044.png","http://www.bestsign.cn","4009936665",0,1,"上上签电子签约为客户提供「实名认证」、「在线签署」、「合同存管」、「诉讼支援」、「保险赔付」等覆盖电子合同全生命周期的SaaS服务。帮助客户获取有法律效力的电子签名,随时随地在线上签署各类合同文件,同时实现后台轻量化运营。 -2014年成立至今,上上签先后获经纬中国、DCM、顺为、WPS等基金投资,是业内第一家通过ISO27001认证、可信云认证的电子签约公司。2017年,上上签收购快签。目前,上上签平台日签约量已突破1000万次,服务超过30万家企业客户。","上上签(www.bestsign.cn),中国电子签约云平台领导者,为客户提供「实名认证」、「在线签署」、「合同存管」、「诉讼支援」、「保险赔付」等覆盖电子合同全生命周期的SaaS服务。帮助客户获取有法律效力的电子签名,随时随地在线上签署各类合同文件,同时实现后台轻量化运营。 -2014年成立至今,上上签先后获经纬中国、DCM、顺为、WPS等基金投资,吸引了众多来自于苹果、微软、阿里、亚马逊、甲骨文等国内外知名公司的优秀人才加盟,拥有强大的技术实力、丰富的企业服务经验和资源。上上签是业内第一家通过ISO27001认证、可信云认证、信息系统安全等级保护三级的电子签约服务供应商。 -2017年,上上签收购快签,开启中国电子签约行业收购先河。 -目前,上上签平台日签约量已突破1000万次,服务超过30万家企业客户。银联、交通银行、建设银行、联想、链家、沃尔沃、吉利、美团、金蝶、找钢网、TCL、紫金矿业、厦门集装箱码头集团、永辉超市、瓜子二手车、人人车等均为上上签的付费客户。 - -## 功能介绍: -###实名认证: -帮助用户完成严格的实名认证过程并获取权威CA机构颁发的数字证书,为电子签名赋予法律效力。 -###在线签署: -用户可通过手机、电脑、平板等设备,随时随地签署合同文件并发送给对方,实时查看合同创建、审批、签署状态。 -###合同存管: -海量合同无需打印、存放和专人管理,加密存储在上上签云端服务器,随时查看、下载,实现后台轻量化运营。 -###诉讼支援: -上上签联合公证处、仲裁委等司法机构,在需要时为用户提供“全证据链”公证或“一键仲裁”服务,保障司法权益。 -###保险赔付: -上上签对接多家保险机构,为用户投保高额的“电子合同法律有效性保险”和“数据安全险”,扫除后顾之忧。 - -##产品优势: -###签署合法合规 -银联+工商局+公安部多系统实名认证 -工信部权威CA机构颁发数字证书 -数字签名+时间戳技术,满足法律要求 -联合公证处、仲裁委,提供司法保障 - -###产品便捷易用 -支持电脑、手机、平板全终端签署 -支持网页、App、微信全渠道签署 -高度灵活的API接口,无痕嵌入系统 -多种身份认证方式,可自主选择 - -###服务安全可靠 -业内首个ISO27001认证、可信云认证 -公安部信息安全三级等保,与银行同级 -AES256位最强加密,“一文一密” -与顶级区块链公司Bitfury合作,区块链存管 - -###性能强健稳定 -金融云服务器,可用性高达99.99% -高签约并发能力,最高支持单日上亿次签约量 -同城双活+异地容灾,业内最长无宕机记录 -7x24小时运维,全方位故障监控/响应机制",1,0,"published",9999,0,"2018-03-03 14:20:44","2020-06-01 12:09:13","尚尚签" -140,0,"优特云","youteyun2542","upload/20180315/15211069726269.png","upload/20180315/15211070125836.png","upload/20180315/15211070479719.png","http://www.yottacloud.cn/","0851-84847925",0,1,"贵州优特云科技有限公司成立于2016年5月25日。在贵州省贵阳市大力发展云计算、大数据产业的背景下,优特云得到了贵州省、贵阳市的大力扶持,成为首批落地的重点项目之一。优特云提供基础架构及服务(IAAS),软件及服务(SAAS)等公有云产品,并以贵州省、贵阳市为中心布局交通、医疗、安防、教育等行业云板块。","发展历程 -贵州优特云科技有限公司成立于2016年5月25日。在贵州省贵阳市大力发展云计算、大数据产业的背景下,优特云得到了贵州省、贵阳市的大力扶持,成为首批落地的重点项目之一。优特云提供基础架构及服务(IAAS),软件及服务(SAAS)等公有云产品,并以贵州省、贵阳市为中心布局交通、医疗、安防、教育等行业云板块。 -Yotta, 是十进制系统的最大单位,相当与10的24次方。国际上有研究统计,目前人类互联网全部数据的总和约为一个Yotta. 我们立足贵州,放眼世界,立志成为一家伟大的科技公司。 -使命宣言 -通过不断的创新,为客户提供高效、可靠的计算和数据服务,与客户共同成长。 -公司地址 -贵州省贵阳市观山湖区阳关大道28号赤天化大厦7层 -品牌理念 -创新、开放、包容 -网址 -www.yottacloud.cn",0,0,"draft",9999,0,"2018-03-15 17:45:12","2020-06-01 11:11:14","优特云" -141,0,"北京供销科技有限公司","beijinggongxiaokejiyouxiangong","upload/20180329/15223039998138.png","upload/20180329/15223040016410.png","upload/20180329/15223040059918.jpg","http://www.sinobbd.com","4006123658",99,0,"北京供销科技有限公司(以下简称“供销科技“)成立于2013年,2016年由北京市供销合作总社所属企业北京供销大数据集团增资控股,成为国有控股企业。","北京供销科技有限公司(以下简称“供销科技“)成立于2013年,2016年由北京市供销合作总社所属企业北京供销大数据集团增资控股,成为国有控股企业。北京供销科技有限公司承载北京供销大数据集团IDC、EDC、CDN、Cloud、大数据五大业务线的运营,提供包括数据中心、CDN、传输、云、安全、大数据管理和分析等一体化一站式平台产品及服务体系。 -公司在全球多个城市建立服务节点,目前网络覆盖中国电信、中国联通、中国移动和中国教育科研网等各大运营商,已获批(ISP、IDC、ICP)跨区域独立运营资质,是中国互联网络信息中心CNNIC会员单位、IP地址联盟会员单位,并拥有独立的AS自治域号码和丰富的广域网IP地址资源池,为客户提供高品质的IDC与CDN加速服务。",2,0,"published",9999,0,"2018-03-29 14:14:14","2020-06-01 12:09:34","供销科技" -142,0,"中移苏研","zhongyisuyan1758","upload/20180330/15223747566392.png","upload/20180330/15223748607248.png","upload/20180330/15223748647387.jpg","https://183.232.205.30/ecloud","400-189-0890",90,0,"广东移动云平台是广东公司第一个投入商用的省内公众服务云平台,于2013年8月投入使用。可提供的业务包括:云主机、块存储、对象存储、负责均衡等基础设施云服务,用户可以通过自服务门户实现业务的自助申请、自动化开通、产品的变更及退租。","广东移动云平台是广东公司第一个投入商用的省内公众服务云平台,于2013年8月投入使用。可提供的业务包括:云主机、块存储、对象存储、负责均衡等基础设施云服务,用户可以通过自服务门户实现业务的自助申请、自动化开通、产品的变更及退租。相对于传统 IDC 业务开通以天计, 广东移动云的业务实现都是分钟级的自动化方式实现。目前平台建成有南方基地、广州中能以及佛山三水共3个主要资源节点,其中佛山三水节点资源规模最大。",0,0,"published",9999,1,"2018-03-30 09:55:30","2018-03-30 09:55:32","" -143,119,"广东移动","guangdongyidong5810","upload/20180330/15223761433085.png","upload/20180330/15223762048686.png","upload/20180330/15223762079463.jpg","https://183.232.205.30/ecloud","400-189-0890",10,0,"广东移动云平台是广东公司第一个投入商用的省内公众服务云平台,于2013年8月投入使用。可提供的业务包括:云主机、块存储、对象存储、负责均衡等基础设施云服务,用户可以通过自服务门户实现业务的自助申请、自动化开通、产品的变更及退租。","广东移动云平台是广东公司第一个投入商用的省内公众服务云平台,于2013年8月投入使用。可提供的业务包括:云主机、块存储、对象存储、负责均衡等基础设施云服务,用户可以通过自服务门户实现业务的自助申请、自动化开通、产品的变更及退租。相对于传统 IDC 业务开通以天计, 广东移动云的业务实现都是分钟级的自动化方式实现。目前平台建成有南方基地、广州中能以及佛山三水共3个主要资源节点,其中佛山三水节点资源规模最大。",4,0,"published",9999,0,"2018-03-30 10:17:38","2020-06-01 11:11:14","广东移动" -144,0,"美团云","meituanyun1838","upload/20180330/15223784741413.png","upload/20180330/15223785173055.png","upload/20180330/15223785209282.jpg","https://www.mtyun.com/","4000800170",10,0,"美团云(北京三快云计算有限公司)是国内最大的O2O电商平台美团于2013年推出的公有云计算服务平台,致力于为用户提供高效、稳定、可靠的云计算服务。","美团云(北京三快云计算有限公司)是国内最大的O2O电商平台美团于2013年推出的公有云计算服务平台,致力于为用户提供高效、稳定、可靠的云计算服务。 -成立至今,美团云在华南、华北等多地建设了Tier3+级别高标准数据中心,并在深入把握用户核心需求的基础上相继上线了云主机、存储、网络、负载均衡、数据库、缓存等性能领先产品。同时依托美团在O2O、餐饮、酒店、旅游等行业深耕多年的经验积累,推出了餐饮云、酒店云、交通云等标准化行业解决方案,并根据客户本身的个性化需求提供定制化解决方案以及7*24小时 90秒响应的服务支持,助力企业轻松上云。 -美团云秉承“技术突破边界、服务传递价值”的理念,持续提升产品性能、不断完善服务体系,为企业级用户提供“最稳定”的用户体验、“最用心“的服务,帮助用户连接云计算未来。",0,0,"published",9999,1,"2018-03-30 10:56:10","2018-03-30 10:56:18","" -145,0,"融云","rongyun1976","upload/20180402/15226324129464.png","upload/20180402/15226324175509.png","upload/20180402/15226324214173.png","http://www.rongcloud.cn","400-919-9066",10,1,"融云基于海量业务的技术锤炼,从基础架构到精细化运营,充分体现其平台实力,能够为开发者、企业提供应用内社交、直播互动、商业沟通、企业 IM 等场景化的沟通诉求。融云核心团队源自飞信研发团队,在即时通讯领域拥有十几年的技术研发沉淀和运营维护经验,掌握即时通讯的核心技术。","融云,即时通讯云行业领导者,致力于为开发者、企业提供安全、稳定、可靠、覆盖全球的即时通讯云服务。 -融云在全球多地部署数据中心和接入节点,可提供稳定的海外链路、最优的通讯质量、安全的数据备份、可靠的业务容灾。 -目前,业务覆盖中国(含港澳台地区)、美国、加拿大、巴西、英国、德国、荷兰、日本、韩国、新加坡、印度、阿联酋、澳大利亚,为各大洲用户提供服务。 -融云凭借卓越的产品和优质的服务,在开发者规模、行业覆盖率、平台日活跃用户数、日均消息量等方面领先于全行业。目前,已有十余万应用通过融云实现了场景化沟通,并从中获益,包括聚力视频、土豆视频、汽车之家、携程爱玩、百姓网等知名应用,涵盖社交、医疗、教育、电商、O2O、媒体、智能硬件、企业服务等众多行业应用。",1,0,"published",9999,0,"2018-04-02 09:28:29","2020-06-01 11:11:14","融云" -146,122,"神州数码","shenzhoushuma5995","upload/20180402/15226336575079.png","upload/20180402/15226336607878.png","upload/20180402/15226338698139.jpg","http://www.dcclouds.com","400-856-3666",10,0,"贵州神州数码云计算有限公司(简称神州云计算)致力于整合全球云产业资源,打造企业云服务平台,协同生态体系,为广大客户提供广泛的云计算资源,云应用及云专业化服务,助力客户商业价值提升。秉承开放共赢、价值互补之核心理念,神州云计算期待携手广大生态合作伙伴,围绕用户需求持续创新,共同推动中国云服务产业发展。","贵州神州数码云计算有限公司(简称:神州云计算)是神州数码集团旗下品牌,于2015-04-22成立,注册资金5000万,持有增值电信业务、电信与信息服务业务许可,是一家云服务供应商。致力于整合全球云产业资源,打造企业云服务平台,协同生态体系,拥有云服务专业营销及技术团队,为广大客户提供广泛的云计算资源,云应用及云专业化服务,助力客户商业价值提升。秉承开放共赢、价值互补之核心理念,神州云计算期待携手广大生态合作伙伴,围绕用户需求持续创新,共同推动中国云服务产业发展。 -通过业务支撑平台(数据中心、资质牌照、技术服务)的运营,已形成云基础资源(私有云、企业云),云应用(SaaS)以及云专业化服务(咨询、培训、技术支持)的云服务体系,并通过云服务平台服务中国企业级客户,并已在教育、金融、医疗、信息等行业积累了丰富的解决方案及成功案例。",2,0,"published",9999,0,"2018-04-02 09:51:47","2020-06-01 11:11:14","神州数码" -147,0,"云端智度","yunduanzhidu7372","upload/20180402/15226371568599.jpg","upload/20180402/15226371584153.jpg","upload/20180402/15226371991007.png","http://www.isurecloud.net","010-65765899",10,1,"北京云端智度科技有限公司是一家专注CDN流量聚合及智能调度的高科技创业公司,创始团队有超过10年的资深行业背景及深刻的业界影响力。基于对行业的深刻理解,在对传统CDN模式进行综合考量的基础上,公司对CDN进行了技术创新,致力于打造CDN共享生态。公司通过对多个CDN服务商进行资源聚合,并通过实时监控和大数据分析,实现全网资源的优势组合,为客户业务进行智能优化调度的CDN服务,帮助门户网站、视频点播/直播网站、下载服务网站、电商网站客户提升服务质量,同时尽最大可能提升终端用户的访问体验。","北京云端智度科技有限公司是一家专注CDN流量聚合及智能调度的高科技创业公司,创始团队有超过10年的资深行业背景及深刻的业界影响力。基于对行业的深刻理解,在对传统CDN模式进行综合考量的基础上,公司对CDN进行了技术创新,致力于打造CDN共享生态。公司通过对多个CDN服务商进行资源聚合,并通过实时监控和大数据分析,实现全网资源的优势组合,为客户业务进行智能优化调度的CDN服务,帮助门户网站、视频点播/直播网站、下载服务网站、电商网站客户提升服务质量,同时尽最大可能提升终端用户的访问体验。",1,0,"published",9999,0,"2018-04-02 10:47:09","2020-06-01 11:11:14","云端智度" -148,0,"云智慧","yunzhihui8536","upload/20180402/15226383607182.png","upload/20180402/15226383639543.png","upload/20180402/15226383665385.jpg","http://www.toushibao.com/","010-59426322",10,0,"云智慧(北京)科技有限公司成立于2009年,是全球领先的应用性能管理与智能业务运维服务商。为企业提供以用户体验为核心,业务增长为目标的全平台一体化智能运维服务。涵盖主动用户体验感知、业务健康分析、深度应用诊断和业务容量规划,持续提升业务运营和IT管理效率。","云智慧(北京)科技有限公司成立于2009年,是全球领先的应用性能管理与智能业务运维服务商。为企业提供以用户体验为核心,业务增长为目标的全平台一体化智能运维服务。涵盖主动用户体验感知、业务健康分析、深度应用诊断和业务容量规划,持续提升业务运营和IT管理效率。",1,0,"published",9999,0,"2018-04-02 11:06:56","2020-06-01 11:11:14","云智慧" -149,117,"证通云","zhengtongyun8756","upload/20180402/15226386742422.png","upload/20180402/15226386766390.png","upload/20180402/15226386803066.jpg","https://www.szzt.com.cn/about/#honor","4008810888",10,0,"证通云(ZTCloud)是云计算基础设施提供商,致力于打造智慧城市平台服务、为IDC数据中心, 金融、政务等行业用户提供安全可靠、性能卓越、按需、实时的IAAS&PAAS平台。立足于证通电子IDC数据中心,提供云主机、云存储、云安全、云灾备等基础设施服务平台和行业解决方案。","证通云创立于2015年,是证通电子旗下的IDC及云计算品牌。在深圳、广州、东莞和长沙布局建设5个大型互联网数据中心,在全国各地设立了6个研发中心。可为客户提供高效、安全、稳定的云计算资源池构建服务,实现资源的高效管理、运维和按需交付。 -在强大的云计算基础设施支撑下,证通云专注于为金融、政府、电力、教育等企事业用户提供定制化的云服务解决方案,已有长沙望城经开区智慧城市服务平台、平安银行金融云等成功案例。同时,也一直致力于人工智能及大数据的研究实践,已在物联网、营销等方面取得了广泛应用。 -作为中国专业的云基础服务提供商,证通云运用安全、高效、智能的云计算能力,为社会各行业构建全方位的信息产业生态。",1,0,"draft",9999,0,"2018-04-02 11:11:53","2020-06-01 11:11:14","证通云" -150,109,"上海科箭软件科技有限公司","shanghaikejianruanjiankejiyoux","upload/20180925/15378359735985.png","upload/20180925/15378359792050.png","upload/20180925/15378359828019.jpg","http://www.qt-asia.com","4008125512",100,1,"上海科箭软件科技有限公司,成立于2003年。科箭是国内领先的物流供应链解决方案及云服务提供商,致力于帮助企业构建更敏捷、更高效、更智慧的数字化供应链网络,实现供应链全流程可视化。科箭供应链管理云平台-Power SCM Cloud,是一个整合订单管理(OMS)、运输管理(TMS)、仓储管理(WMS)等供应链流程的云解决方案。该方案基于多年制造及物流行业经验,融合云计算、移动、社交、大数据及消费级产品设计技术,让用户使用更便捷,让管理决策更智慧,让企业连接更顺畅。通过最佳实践的实施方法论,可以帮助客户快速实现投资回报。","上海科箭软件科技有限公司,成立于2003年。科箭是国内领先的物流供应链解决方案及云服务提供商,致力于帮助企业构建更敏捷、更高效、更智慧的数字化供应链网络,实现供应链全流程可视化。科箭供应链管理云平台-Power SCM Cloud,是一个整合订单管理(OMS)、运输管理(TMS)、仓储管理(WMS)等供应链流程的云解决方案。该方案基于多年制造及物流行业经验,融合云计算、移动、社交、大数据及消费级产品设计技术,让用户使用更便捷,让管理决策更智慧,让企业连接更顺畅。通过最佳实践的实施方法论,可以帮助客户快速实现投资回报。 - -OMS云帮助客户处理从订单接收到费用结算的订单全生命周期管理。通过订单中心汇集内、外部多渠道订单,OMS云与WMS云、TMS云无缝集成,实现订单全流程可视化,集中管理与执行监控,确保订单准确、准时交付。 - -TMS云是创新设计的专业SaaS运输管理产品。帮助货主、物流公司及其合作伙伴(承运商及司机)通过互联网或手机访问同一个共享的云平台,实现运输工作协同化及流程360度可视化。TMS云使用成本低,a按需收费,用户无需额外硬件投入及维护成本。 - -WMS云是基于行业沉淀设计的专业SaaS仓储管理产品。针对制造、零售、物流及电商等行业,WMS云流程可配置,场景化设计,可快速上线,投资回报快。 - -科箭供应链管理云平台-Power SCM Cloud推出以来,已经在家乐福、麦德龙、哈药集团、伊利集团、延锋安道拓、富士施乐、普天太力、玖龙纸业及C.H.Robinson、德莎物流、中储物流等行业领导企业成功应用。",1,0,"published",9999,0,"2018-09-25 08:42:08","2020-06-01 12:09:58","上海科箭" -151,0,"杭州天谷信息科技有限公司","hangzhoutianguxinxikejiyouxian","upload/20181008/15389621226909.png","upload/20181008/15389621255705.png","upload/20181008/15389623149855.png","https://www.tsign.cn/","400-087-8198",100,1,"杭州天谷信息科技有限公司(www.tsign.cn)是专业的电子签名服务商。为公共事业、互联网企业、大型企业、金融业等领域客户提供实名认证、电子签名、电子合同、数据存证及法律服务。 -e签宝是天谷科技旗下的第三方电子签名服务平台,是专业的在线电子签名平台。作为可信互联网的重要技术设施之一,平台基于《中华人民共和国电子签名法》,e签宝致力于为客户提供便捷并具有法律效力的电子签名服务,将需要耗费数日之久的传统文件签署,变革为随时随地一键签署。 -e签宝是国家密标委标准化委员会首批成员单位,参与制定了多项国家和地方行业标准;并获得了公安部、国密局、法院、仲裁委、公证处等部门专业齐全的专业资质认可。 -","杭州天谷信息科技有限公司成立于2002年,是专业的电子签名服务商,拥有52项著作权、16项发明专利,承担2项国家课题项目,技术实力行业领先。为公共事业、互联网企业、大型企业、金融业等领域客户提供实名认证、电子签名、电子合同、数据存证及法律服务。 - - - -产品服务: - -1.电子签名: - -e签宝电子签名服务以“合法合规、对接快捷、用户体验至上”作为核心设计理念,满足《电子签名法》的要求为客户提供从数字证书申请、模板及手绘印章创建、数据流和版式文件的多样化签署服务,到事后出具证据报告,提供司法诉讼支撑等服务,以实现全流程的电子签署服务及司法证据链闭环。 - - - -2.电子合同: - -电子合同是e签宝为企业及个人用户推出的中立、公证、可信、合法合规的第三方电子合同签约服务平台,用户可以在平台上实现多方在线签订合同、协同管理及合同法律支持。 - - - -3.天印签章: - -天印签章是天谷科技针对信息化和私有化的需求,推出的旨在为行业提供专业的电子签章服务解决方案。 - - - -4.保全服务: - -e签宝联合公证处、司法鉴定中心、仲裁机构,共同推出存证保全产品,打造取证、存证、出证为一体的第三方电子数据保全平台。 - - - -e签宝优势: - -1.合规合法 - - 完全符合国家相关法律规定,为您创建安全可靠的电子签名,并成功获得国密局、公安部的认证,权威司法鉴定中心为e签宝系统进行技术评估,为e签宝用户开通电子证据鉴定的专用绿色通道。 - -2.支持国密算法 - - 国家密码管理局公布了自主研制的“椭圆曲线公钥密码算法”(SM2算法),并要求自2011年3月1日起,在建和拟建公钥密码基础设施电子认证系统和密钥管理系统应使用SM2算法。而e签宝也是国内首家支持该算法的第三方电子签名平台。 - -3.多样化签署服务 - - 为了满足各行业的业务场景需求,e签宝电子签名支持模板/手绘印章创建、自定义印章上传、二维码签署、手机盾签署、智能合同模板服务、合同作废/解除服务,以及签章记录统计等服务。同时,e签宝支持包括版式文件、流式文件、数据流等不同电子数据形态的签署。 - -4.信息安全。 - - 拥有ISO27001,可信云认证,信息系统安全等级保护三级备案。e签宝自主研发的原文沙箱技术,可实现全程不触碰合同原文、不触碰印模,保护客户的隐私,具备金融级数据安全保护能力。 - -5.全流程存证 - - 使用e签宝电子签名后,签署场景数据自动保存到e签宝360°电子证据存储中心,且支持直接传入权威公证处服务端,双重存证保障,来满足对后续司法出证、数据保全有较高要求的客户。 - -6.服务可靠。 - - 阿里云服务器,可用性高,并发能力强,7x24小时持续服务响应机制。",1,0,"published",9999,0,"2018-10-08 09:32:58","2020-06-01 12:10:14","天谷信息" -152,79,"杭州云片网络科技有限公司","hangzhouyunpianwangluokejiyoux","upload/20181008/15389639645955.png","upload/20181008/15389639675069.png","upload/20181008/15389639696820.jpg","https://www.yunpian.com/","4000892617",1200,1,"云片是国内领先的云通讯服务商 -依托成熟的云计算和通讯技术,为国内外企业用户提供优质的通讯平台服务(PaaS) -公司致力于帮助企业与客户更好的沟通,做更好的云通讯服务商","云片是国内领先的云通讯服务商 -依托成熟的云计算和通讯技术,为国内外企业用户提供优质的通讯平台服务(PaaS) -公司致力于帮助企业与客户更好的沟通,做更好的云通讯服务商 - -云片成立于2013年,总部位于杭州,并在北京、上海、深圳、武汉等地设有分支机构 -公司创始团队拥有10余年的互联网创业经验,核心团队来自阿里巴巴、腾讯、百度等公司 -目前,云片已服务Uber、Airbnb、腾讯、拉勾、知乎、北京银行、有赞、今日头条等品牌客户",1,0,"published",9999,0,"2018-10-08 10:00:12","2020-06-01 12:10:41","云片网络" -153,0,"北京国美云服科技有限公司","beijingguomeiyunfukejiyouxiang","upload/20181008/15389780276889.png","upload/20181008/15389780328146.png","upload/20181008/15389780374668.jpg","http://www.gyun.com","010-59287188",100,1,"国美云服创立于2015年,是行业领先的云计算及人工智能科技公司 -为许多地区的企业和开发者提供服务。国美云服致力于以在线公共 -服务的方式,提供安全、可靠的计算和数据处理能力,让计算和人 -工智能成为普惠科技。 -国美云服服务着金融、政务、零售、电商等众多领域的领军企业且 -一直保持着良好的运行纪录。","国美云服创立于2015年,是行业领先的云计算及人工智能科技公司 -为许多地区的企业和开发者提供服务。国美云服致力于以在线公共 -服务的方式,提供安全、可靠的计算和数据处理能力,让计算和人 -工智能成为普惠科技。 -国美云服服务着金融、政务、零售、电商等众多领域的领军企业且 -一直保持着良好的运行纪录。",1,0,"published",9999,0,"2018-10-08 13:55:01","2020-06-01 12:10:58","国美云服" -154,102,"北京小桔科技有限公司","beijingxiaojiekejiyouxiangongs","upload/20181008/15389796631759.png","upload/20181008/15389796655711.png","upload/20181008/15389796681753.png","https://www.didiyun.com/","4000590666",100,1,"滴滴自成立以来基于自身IT基础设施规模的扩大,在大规模机群的使用、管理、运维和调度上沉淀了大量的专有技术。滴滴云基于滴滴出行的业务技术和经验积累,采用领先的云计算架构、高规格服务器集群搭建、高性能资源配置机制、精细化运营模式,致力于为开发者提供简单快捷、高效稳定、高性价比、安全可靠的IT基础设施云服务。","滴滴云基于滴滴出行的业务技术和经验积累,采用领先的云计算架构、高规格服务器集群搭建、高性能资源配置机制、精细化运营模式,致力于为开发者提供简单快捷、高效稳定、高性价比、安全可靠的IT基础设施云服务。滴滴云为您提供安全稳定的数据支持、灵活高效的管理、科学的研究模型、资源的专家团队。",2,0,"published",9999,0,"2018-10-08 14:21:48","2020-06-01 12:11:12","滴滴云" -155,0,"深圳市互盟科技股份有限公司","shenshihumengkejigufenyouxiang","upload/20181008/15389809307059.png","upload/20181008/15389809328902.png","upload/20181008/15389809354479.jpg","https://www.humengyun.com/index.html","0755-88878978",100,1,"深圳市互盟科技股份有限公司(简称“互盟股份”)成立于2009年,总部位于深圳科技园。2016年挂牌新三板上市(股票代码:838935),国家高新技术企业,深圳自主创新百强中小企业,是深圳领先的数据中心+云互联网综合服务商。","深圳市互盟科技股份有限公司拥有国际T4标准的五星级数据中心及优质资源,可为用户提供IDC数据中心、云服务、光纤专线、智能运维、安全服务等立体式一站解决方案。与中国电信、中国联通、中国移动、电讯盈科、和记电讯等多家电信运营商有着长期紧密的合作关系,拥有30000+家合作客户。",1,0,"published",9999,0,"2018-10-08 14:42:58","2020-06-01 12:11:38","互盟科技" -156,0,"深圳喜盈佳企业云服务有限公司","shenxiyingjiaqiyeyunfuwuyouxia","upload/20181008/15389855521196.png","upload/20181008/15389856056796.png","upload/20181008/15389854487395.jpg","http://www.servingcloud.com","400-035-9696",100,1,"深圳喜盈佳企业云服务有限公司,构建和运行一切与发票相关的业务应用,集团管控、精细化管理为企业运转提效, -全中国,地产百强企业把喜盈佳作为他们税务管理的首选","深圳喜盈佳企业服务有限公司成立于2016年4月,核心初创团队成员来自宝钢集团、中石化集团、SAP等知名企业,拥有行业领先的资深经验,在企业应用软件、云计算技术、企业互联网产品的设计开发与市场运营等方面有着深厚的经验与创新能力。目前的业务经营获得了万科、保利地产、华夏幸福、金地集团、红星美凯龙、佳兆业等地产500强集团的认可,平台的用户数超过10000家,平台每日认证发票金额多达2亿人民币,各项业务均在稳步增长中。",1,0,"published",9999,0,"2018-10-08 16:00:45","2020-06-01 12:11:53","喜盈佳" -157,0,"深圳法大大网络科技有限公司","shenfadadawangluokejiyouxiango","upload/20181009/15390468729646.png","upload/20181009/15390469316891.png","upload/20181009/15390469409040.jpg","http://www.fadada.com","400-869-2012",100,1,"法大大”(www.fadada.com)是国内领先的第三方电子合同平台,目前主要为金融、保险、第三方支付、旅游、房地产、汽车、医疗、物流、供应链、B2B、B2C线上交易平台、人力资源管理等行业以及政府机构提供电子合同、电子文件签署及存证服务,同时整合提供司法鉴定、律师服务等增值服务。","法大大”(www.fadada.com)是国内领先的第三方电子合同平台,目前主要为金融、保险、第三方支付、旅游、房地产、汽车、医疗、物流、供应链、B2B、B2C线上交易平台、人力资源管理等行业以及政府机构提供电子合同、电子文件签署及存证服务,同时整合提供司法鉴定、律师服务等增值服务。法大大目前已通过ISO27001安全认证并获得众安保险承保,因其便捷、安全、合规的特性,目前法大大的产品和服务已被阿里巴巴、微软、百度、海尔、霍尼韦尔、支付宝、滴滴、美团、携程、同程旅游、人人车、蔚来汽车、小红书、蛋壳公寓、拍拍贷、众安保险、阳光保险、云南信托、新东方、龙湖地产、红星美凯龙、吉利石油等众多知名平台和公司所采用。",1,0,"published",9999,0,"2018-10-09 09:03:03","2020-06-01 12:14:37","深圳法大大" -158,0,"北京知道创宇信息技术有限公司","beijingzhidaochuangyuxinxijish","upload/20181009/15390477173577.png","upload/20181009/15390477191251.png","upload/20181009/15390477224838.jpg","http://www.yunaq.com","400-060-9587 / 010-57076191",1000,1,"知道创宇成立于2007年8月,由数位国际顶尖的安全专家创办,并拥有近百位国内一线安全人才的核心安全研究团队。知道创宇是国内最早提出云监测与云防御理念的网络安全公司,经过多年的积累,利用在云计算及大数据处理方面的行业领先能力,可为客户提供具备国际一流安全技术标准的可视化解决方案,提升客户网络安全监测、预警及防御能力。 -知道创宇目前正在对世界范围内90万网站提供安全加速服务,每天处理网络请求数十亿次,帮助网站拦截黑客攻击超过1亿次。为多家知名网站提供安全加速服务,保护企业网上业务安全,其中包括公安部、工信部、国家互联网应急中心、国务院中央政府采购网等国家重要网站。","北京知道创宇信息技术有限公司(以下简称知道创宇),成立于2007年8月,由数位国际顶尖的安全专家创办,并拥有近百位国内一线安全人才的核心安全研究团队。知道创宇是国内最早提出云监测与云防御理念的网络安全公司,经过多年的积累,利用在云计算及大数据处理方面的行业领先能力,可为客户提供具备国际一流安全技术标准的可视化解决方案,提升客户网络安全监测、预警及防御能力。凭借在行业中领先的技术实力及影响力,知道创宇创始人及CEO赵伟入选2012年《福布斯》30位30岁以下创业者之一,公司还被CIO杂志评选2009年度国内除阿里巴巴之外唯一一家入选亚洲最具价值企业1/20名单。 -知道创宇于2015年再次获得腾讯大范围战略投资,目前知道创宇员工规模已突破500人,其中技术人员达70%,公司总部设在北京,坐落于北京市朝阳区阜安西路望京SOHO中心T3 A座15层,在香港、上海、广东、江苏、四川、安徽、陕西、湖南、湖北、黑龙江等多地设有分公司。 -知道创宇目前正在对世界范围内90万网站提供安全加速服务,每天处理网络请求数十亿次,帮助网站拦截黑客攻击超过1亿次。为多家知名网站提供安全加速服务,同时捍卫国家网络核心资源安全、保护企业网上业务安全,其中包括公安部、工信部、国家互联网应急中心、国务院中央政府采购网等国家重要网站。知道创宇积极参与国家网络安全建设,长期为国家重大活动提供网络安全保障,多次受到相关部门的表扬与感谢,在2014年APEC会议、2015年世界反法西斯战争胜利70周年、2016年G20峰会(杭州)、2017年“一带一路”国际合作高峰论坛,以及“两会”、世界互联网大会等重大活动、会议期间,保障国家重要网站的运行安全。 -目前知道创宇已通过了国家高新技术企业等资格认证,多项产品获得北京市自主创新产品认证。因在网络安全防御方面的技术优势及做出的突出贡献,荣任CNCERT、CNNVD、国家信息安全通报中心、“十八大”网络安全应急处置队伍等专业机构的会员或支持单位,并多次受到公安部、各级地方网络安全应急处理单位的表彰和嘉奖。曾向微软、腾讯、阿里巴巴提交漏洞,而受到公开致谢。",1,0,"published",9999,0,"2018-10-09 09:16:03","2020-06-01 12:14:48","知道创宇" -159,35,"招银云创(深圳)信息技术有限公司","zhaoyinyunchuangshenxinxijishu","upload/20181009/15390503429899.png","upload/20181009/15390503465618.png","upload/20181009/15390503491534.png","http://www.mbcloud.com/#/","400-821-8728",1000,1,"招银云创公司(MBCloud)是招商银行的全资子公司,旨在将招商银行IT系统30年稳定运行的成功经验和金融IT的成熟度解决方案对金融同行开放,服务于社会。我们支撑了招行在过去30年以科技领先实现跨越式发展,也能够以同样的技术和服务支撑金融同业快速发展,推动普惠金融的发展。 -招银云创能专业、全方位涵盖金融同业机构业务及IT服务所涉及的各个领域,通过金融科技输出的模式,将金融服务内嵌,整合资源,致力服务于同业机构,全力打造垂直金融生态和横向生态闭环。我们“不仅授人以鱼,更授人以渔”,让金融同业机构收获不仅仅是产品系统和服务,还有我们丰富的经验、细节,以及后续运营过程中的辅导和经验分享,共同成长。","招银云创公司(MBCloud)是招商银行的全资子公司,旨在将招商银行IT系统30年稳定运行的成功经验和金融IT的成熟度解决方案对金融同行开放,服务于社会。我们支撑了招行在过去30年以科技领先实现跨越式发展,也能够以同样的技术和服务支撑金融同业快速发展,推动普惠金融的发展。 -招银云创能专业、全方位涵盖金融同业机构业务及IT服务所涉及的各个领域,通过金融科技输出的模式,将金融服务内嵌,整合资源,致力服务于同业机构,全力打造垂直金融生态和横向生态闭环。我们“不仅授人以鱼,更授人以渔”,让金融同业机构收获不仅仅是产品系统和服务,还有我们丰富的经验、细节,以及后续运营过程中的辅导和经验分享,共同成长。",1,0,"published",9999,0,"2018-10-09 09:59:41","2020-06-01 12:14:58","招银云创" -160,0,"享宇金信金融服务外包有限公司","xiangyujinxinjinrongfuwuwaibao","upload/20181009/15390672552275.png","upload/20181009/15390672588802.png","upload/20181009/15390673057652.png","http://xyect.com","028-8536 0088",10,1,"享宇金服创立于2014年,是一家立足风险管控,为金融机构提供专业化数据金融服务的金融科技公司,也是国内首批通过国家工信部金融风控类数据流通标准符合性测试、国内第一家通过API接口方式调取运营商脱敏数据用于个人信贷业务授信分析支撑的金融科技公司。同时也是唯一获得此资质的第三方公司。","享宇是国内最早一批真正意义上的用科技赋能金融的公司。享宇秉持“技术助力金融,做金融机构最可靠的伙伴”的宗旨,坚持以合法合规为前提,与运营商等数据源正规合作,坚持只与兴业银行、浦发银行、马上消费金融、招联消费金融等各大银行与消费金融公司等持牌金融机构进行总部级合作。 - 通过大数据、人工智能等科技手段,在业内拥有广东移动、四川移动、江苏电信、中国电信、中国联通等一批重量级合作伙伴,有着良好的市场口碑。2016年,享宇金服完成A轮千万级融资,近期A+轮融资也接近尾声。 - 截止目前,享宇金服可服务用户数量达8.5亿,覆盖全国手机号码数量约60%,服务银行、消费金融公司、保险等持牌金融机构百余家。位列中国信息通信研究院(原国家工信部电信研究院)大数据企业名单第18位。先后荣获国家“高新技术企业”“、”2017德勤-成都高新创新创业明日之星”(谷歌、腾讯、京东等均曾为此系列榜单企业);四川省经信委授予享宇“四川省大数据产业重点企业”称号,同时摘得“2016年度成都高新区优秀科技金融服务机构”, “四川移动2017年度价值链最佳合作伙伴”等多项荣誉。 - 作为金融科技公司,享宇金服从消费者、从企业的角度,去满足需求解决问题,利用人工智能算法与大数据技术,从金融产品规划设计、客群分层、盈利测算,打造标准化的产品,打通高效低成本的线上获客渠道,针对C端和B端两方需求去做匹配,提升效率,让金融机构更好地决策,风控管理和运营过程更加智能化,客户营销管理更加精准,真正实现用科技赋能金融。 - 为此,围绕“个人及场景公司与金融行业的链接者”战略定位,享宇金服以技术思维为导向,从数据风控切入,构建了移动金融、策略风控、“蜂巢”数据风控、以及区块链个人授权联盟链四大业务板块。",0,0,"draft",9999,0,"2018-10-09 14:42:29","2020-06-01 12:15:32","享宇金服" -161,0,"同方有云(北京)科技有限公司","tongfangyouyunbeijingkejiyouxi","upload/20181009/15390675929867.png","upload/20181009/15390675964316.png","upload/20181009/15390676936966.png","https://www.tfcloud.com/","400-898-5401",100,1,"同方有云(北京)科技有限公司,清华同方集团旗下云计算品牌,业界领先的高品质场景化云服务和行业化云服务提供商,同时也是中国市场重要的云计算解决方案提供商。同方有云依托清华同方集团在“云、管、端”强大的研发和综合技术实力,以及清华同方集团在全国优质渠道服务支撑体系,完全具备“可信、开放、安全”三大核心优势,并在深耕用户需求上,秉持产品快速定制、贴身应需服务的理念,推出适合行业特性的产品与服务,业务场景已覆盖包含教育、金融、政府、医疗、企业服务等在内的诸多行业。","同方有云(北京)科技有限公司,清华同方集团旗下云计算品牌,业界领先的高品质场景化云服务和行业化云服务提供商,同时也是中国市场重要的云计算解决方案提供商。 -同方有云依托清华同方集团在“云、管、端”强大的研发和综合技术实力,以及清华同方集团在全国优质渠道服务支撑体系,完全具备“可信、开放、安全”三大核心优势,并在深耕用户需求上,秉持产品快速定制、贴身应需服务的理念,推出适合行业特性的产品与服务,业务场景已覆盖包含教育、金融、政府、医疗、企业服务等在内的诸多行业。 -在政务云行业尤为注目,同方有云向政府行业提供面向服务的政务云解决方案,弹性地为各政务部门提供各种计算环境,为政务应用提供高可用的框架和支撑,有效降低建设和运行成本,促进政府信息资源开放共享,实现政府部门、企事业单位和社会公众更加便捷地获得信息服务。 -同方有云拥有40多项计算机软件著作权,国家级高新技术企业,并通过CMMI3、ISO9001、ISO27001、ISO20000、ISO22301、工信部软件测评、ITSS云服务认证、可信云解决方案、公安行业软件测评等权威认证,同方云产品基于世界领先的云计算技术构建,持续为行业客户提供功能完整的全栈式一体化云平台体验。",1,0,"published",9999,0,"2018-10-09 14:48:53","2020-06-01 12:15:43","同方有云" -162,124,"上海云砺信息科技有限公司","shanghaiyunxinxikejiyouxiangon","upload/20190515/15578916982169.jpg","upload/20190515/15578914982665.png","upload/20190515/15578911133877.jpg","http://www.xforceplus.com/","400-630-4000",999,0,"上海云砺信息科技有限公司成立于2015年6月,是专业的供应链信息协同及增值税发票管理云平台解决方案提供商,致力于企业云计算软件服务领域行业解决方案的研发及应用,为众多大型企业提供基于互联网和云创新的软件、数据服务。公司总部设于上海,在北京、深圳、武汉、南京、嘉兴等城市设立了分公司。上海云砺拥有的具备完全自主知识产权的“票易通发票管理及供应链协同平台“,是为企业提供供应链信息协同服务和增值税发票管理解决方案的核心产品。产品推出伊始,凭借云计算应用技术方面的巨大优势以及对大型企业供应链协同业务的深刻理解,迅速获得众多世界五百强及各行业标杆企业的认可,并在较快的时间内达成合作、投入部署和应用。","上海云砺信息科技有限公司(以下简称:“上海云砺”或“公司”)成立于2015年6月,是专业的供应链信息协同及增值税发票管理云平台解决方案提供商,致力于企业云计算软件服务领域行业解决方案的研发及应用,为众多大型企业提供基于互联网和云创新的软件、数据服务。 -上海云砺总部设于上海,在北京、深圳、武汉、南京、嘉兴等城市设立了分公司。目前员工总数超过500人。 - 上海云砺拥有的具备完全自主知识产权的“票易通发票管理及供应链协同平台“(以下简称:票易通平台),是为企业提供供应链信息协同服务和增值税发票管理解决方案的核心产品。产品推出伊始,凭借云计算应用技术方面的巨大优势以及对大型企业供应链协同业务的深刻理解,迅速获得众多世界五百强及各行业标杆企业的认可,并在较快的时间内达成合作、投入部署和应用。 -票易通已经为万科、保利、沃尔玛、华润集团、锦江、阿里巴巴、康帕斯、麦当劳、汉堡王、宝钢等众多大型企业提供了优质的增值税发票服务,为企业有效减少了人工成本,大大降低了税务风险以及税金占用率,帮助企业更快适应新的税务形势,提高工作效率,提升财务管理精度。 -截至2018年底,上海云砺已经完成第三轮融资。目前公司估值35亿元人民币。行业内领先的产品和优秀的团队是公司打动投资人的首要因素。融资完成后,公司将会在投资方的全力支持下,走入一条规范经营、高速发展的快车道。融资所获资金,将全部投入技术研发以及市场开拓。公司在未来3年内将获得爆发式增长。 -公司自成立以来一直坚持以创新为驱动,非常重视自主知识产权开发,目前已经获得二十余项软件著作权等知识产权的登记。并荣获 2017年第六届中国创新创业大赛荣获全国二等奖,“2017年度互联网+最佳产品奖”,“2017年税企直连最佳平台奖”,获得“宝山区2017年度经济工作最佳成长奖”,在“2018宝山区经济工作会议暨优化营商环境推进大会”荣获“2017企业创新典范奖”, CEO吴云荣膺“2017最佳财智人物”,入选2018年“上海市青年创业英才开发计划”,入选上海首批“千帆行动”青年企业家计划,受科技部邀请参加2018年第三届金砖国家青创赛,斩获第三名的优异成绩。2018年被授予“宝山区产业互联网创新示范企业”,获得中国直通硅谷创新创业大赛企业组三等奖。企业受到新闻联播、上海电视台、北京财经频道、《首席财务官》等多家权威媒体的多次报道。 -公司的核心团队来自宝信软件、百度、阿里、华为、SAP、麦肯锡、埃森哲、Matrix Care、EMC等知名企业,核心团队成员毕业于上海交大、复旦、北大、美国MIT等国内外知名高校,对于企业应用软件、云计算技术、人工智能、企业互联网产品的设计、开发管理与市场运营方面有深厚的经验和创新力,70%以上的人员为高端技术研发及服务咨询人才。",1,0,"published",9999,0,"2019-05-15 11:41:44","2020-06-01 12:15:58","上海云砺" -163,126,"浪潮云信息技术股份公司","langchaoyunxinxijishugufengong",NULL,"upload/20190808/15652422744034.png","upload/20190808/15652422851164.jpg","https://cloud.inspur.com/",NULL,1,11,"浪潮云信息技术股份公司","\""连续多年政府市场占有率第一、公有云市场前三; -国家级多项安全资质认证,安全资质能力最全最多的云服务商。 -自主可控的安全可靠云,国产化产品服务,保障关键系统完全自主可控。 -拥有国家重点扶持的跨行业跨领域工业互联网平台,面向九大行业、七大领域提供一体化解决方案。 -截止目前,浪潮云遍布全国的7大核心云数据中心以及50个地市云数据中心,为全国23个省140+地市政府以及74万+企业提供云计算服务,承载了10000+政府部门、超过4万个应用,为全国245万公务员提供便捷服务,造福4.7亿老百姓,获得155个地市企业上云服务商身份,81个地市工业互联网服务商身份,推动百万家企业数字化转型。\""",1,0,"published",9999,0,"2021-09-29 17:38:03","2021-09-29 17:38:03","浪潮信息" -164,0,"平安云","pinganyun8606","upload/20190808/15652438051404.jpg","upload/20190808/15652438935460.jpg","upload/20190808/15652442275622.jpg","https://www.pinganyun.com/","400-151-8800",1,0,""平安云诞生于平安集团,由平安科技自主研发的平安云已经建设成为金融行业内最大的云平台, -涵盖平安集团95%以上的业务公司,支撑80%的业务系统投产。并以金融为起点,深度服务于金融、医疗、智慧城市、房产、汽车五大生态圈, -作为平安服务的综合输出平台为全行业提供IaaS(基础设施服务)、PaaS(通用平台服务)、SaaS(软件应用服务)全栈式云服务。" -",""平安云诞生于平安集团,由平安科技自主研发的平安云已经建设成为金融行业内最大的云平台, -涵盖平安集团95%以上的业务公司,支撑80%的业务系统投产。并以金融为起点,深度服务于金融、医疗、智慧城市、房产、汽车五大生态圈, -作为平安服务的综合输出平台为全行业提供IaaS(基础设施服务)、PaaS(通用平台服务)、SaaS(软件应用服务)全栈式云服务。" -",3,0,"published",9999,0,"2019-08-08 14:04:06","2020-06-01 11:11:14","平安云" -165,0,"比格云","bigeyun7854","upload/20190808/15652458925100.png","upload/20190808/15652459037492.png","upload/20190808/15652460014564.png","http://www.biggeryun.com","400-920-8028",1,1,"比格云是一家专注高性能云主机开发和运营的公司,是由从业10年以上的云计算程序员打造的国内新一代高性能公有云服务平台。比格云核心技术团队成员主要来自盛大、亚马逊、微软等国内外高端技术团队,曾经推出国内较早一代公有云之一,并得到市场验证。比格云的运营和运维团队见证了十年多来中国云计算市场的起源和高速增长,拥有丰富而成熟的云计算产品运营和服务经验。比格云的目标是做新一代的高性能云主机。我们坚持用完美主义的工匠精神,不断推出高性能的云主机产品,为用户提供更快、更稳、更便捷的公有云服务。 -","比格云隶属于上海盛霄云计算技术有限公司,成立于2011年,注册资本1000万元,总部设立在上海,位于上海张江高科技园区,并相继在北京、重庆、无锡、常州和东台设立服务和测试中心。公司经过多年的发展,分别在上海、北京和无锡建立了国内高端的IDC高标准数据中心,网络采用骨干节点5线BGP线路、全网万兆光纤双链路冗余架构,硬件上配备了电信级网络设备和网络入侵防护设备,构建了365天不间断的稳定运行保障体系,真正做到用户数据安全、网络稳定可用。 -",1,0,"published",9999,0,"2019-08-08 14:34:03","2020-06-01 11:11:14","比格云" -166,0,"北京神州云通信技术有限公司","beijingshenzhouyuntongxinjishu","upload/20190808/15652470143661.png","upload/20190808/15652470532511.png","upload/20190808/15652469297680.jpg","http://www.cn-data.com","400-60-98980",1,1,"神州云云平台 ,技术领先的云计算服务平台 , 全系列云产品 , 云服务器虚拟主机 , 可信云认证 ,可靠的私有云解决方案,服务可用性高达99.99%,并备有真正的灾难恢复设计,全面的定制化私有云解决具有特色优势的专属云解决方案,让企业可优化资源配置,减少成本开支之余,更多自主控制,并享有快速扩容的特性,更为安全可靠,免除企业的后顾之忧。 -","神州云云平台 ,技术领先的云计算服务平台 , 全系列云产品 , 云服务器虚拟主机 , 可信云认证 ,可靠的私有云解决方案,服务可用性高达99.99%,并备有真正的灾难恢复设计,全面的定制化私有云解决具有特色优势的专属云解决方案,让企业可优化资源配置,减少成本开支之余,更多自主控制,并享有快速扩容的特性,更为安全可靠,免除企业的后顾之忧。 -",1,0,"published",9999,0,"2019-08-08 14:54:13","2020-06-01 12:16:14","神州云" -167,37,"紫光云技术有限公司","ziguangyunjishuyouxiangongsi86","202101/1611914555523736376.png","upload/20190808/15652486664217.png","upload/20190808/15652486701144.jpg","https://www.unicloud.com/","400-6888-311",1,1,"紫光云面向政企行业客户,以公有云平台为支撑,以运营服务为引擎,以生态发展为龙头,深度构建IaaS领域,重点突破行业场景化PaaS能力,全面形成SaaS生态,打造一朵“芯云一体、授信安全、场景驱动、混合交付”的云,持续推动产业转型与升级。","紫光云正式进军公有云市场,成为云服务商之一,成为公共云服务提供商和数字化合伙人,助力“数字中国”建设,是紫光云的目标。",5,0,"published",9999,0,"2021-05-28 16:46:33","2021-06-02 17:07:59","紫光云" -168,0,"安徽听见科技有限公司","anhuitingjiankejiyouxiangongsi","upload/20190808/15652518453885.png","upload/20190808/15652518505656.png","upload/20190808/15652518617531.png","https://www.iflyrec.com/","0551-65309099",1,1,"讯飞听见是由中文语音产业领导者科大讯飞基于其高水平的智能语音及语言技术,推出的以语音转文字为核心功能的系列产品和服务。 -",""讯飞听见是由中文语音产业领导者科大讯飞基于其高水平的智能语音及语言技术,推出的以语音转文字为核心功能的系列产品和服务。 - 可解决企事业单位日常会议、大型发布会、课程培训、媒体节目制作等各种场景下的音频转写问题。让各行各业的人不再为速记费用昂贵、整理录音复杂、查找重点困难、角色辨认模糊、录音质量低劣等方方面面的录音机整理问题而苦恼。依托科大讯飞深耕多年的自然语言处理、声纹识别、语音识别等核心语音技术,经过专业团队的匠心雕琢,讯飞听见形成了以智能会议系统、智能文稿唱词系统、讯飞听见网站、智能会议服务、听见能力授权服务等一系列智能语音转写技术为核心的产品和服务体系,可满足多场景、多终端、多形式的语音转文字需求,致力于全方位提高知识管理的效率。" -",1,0,"published",9999,0,"2019-08-08 16:12:00","2020-06-01 12:16:35","听见科技" -169,0,"北京众签科技有限公司","beijingzhongqiankejiyouxiangon","upload/20190808/15652527595081.png","upload/20190808/15652527978097.png","upload/20190808/15652532979933.jpg","http://www.51signing.com","4000006923",1,1,"众签科技是国内领先的第三方电子合同,电子签约,电子签名,电子签章,电子存证,电子认证云服务平台,为用户提供便捷、有效的全流程电子合同签署及存证服务 -",""北京众签科技有限公司于2013年成立,是一家专业的电子合同+服务提供商,产品和服务聚焦电子合同、电子签章、区块链存证、智能仲裁以及基于区块链的贸易金融平台,为互联网金融、银行、保险、供应链、电商、旅游、长租等行业用户提供信任基础服务。 - 公司总部位于北京,在上海、深圳、厦门、杭州、徐州、南京、武汉、成都、青岛等地设有分公司。创始团队来自清华大学,目前已拥有一支100余人的专业团队。2018年7月,众签完成7000万元A1轮融资,由清志杰资本领投,点亮资本、分布式资本跟投。 - 用合约重构交易。众签始终坚持以人为本,以科技为驱动力,以客户为中心,持续深耕技术服务和解决方案,全方位打造基于“电子合同+区块链多方存证+全面法律保障服务+多重安全保护措施”的电子合同签约生态体系,帮助用户提高合同签署效率,降低网络交易信任成本,推进国内无纸化办公进程,提升中国互联网经济运行效率!" -",1,0,"published",9999,0,"2019-08-08 16:35:42","2020-06-01 12:16:48","北京众签" -170,0,"贵州航天云网科技有限公司","guizhouhangtianyunwangkejiyoux","upload/20190808/15652541307901.png","upload/20190808/15652541335412.png","upload/20190808/15652541378393.png","http://www.gz-icloud.com.cn/","4000636007",1,1,"贵州航天云网科技有限公司是中国航天科工集团公司下属单位,是由航天云网科技发展有限责任公司、中国航天科工集团公司第十研究院、云上贵州大数据产业发展有限公司共同出资成立;是贵州省人民政府、贵州省经信委、贵阳国家高新区引进的重点高科技大数据企业、国家高新技术企业;是国家大数据(贵州)试验区首批重点大数据企业(核心业态)和贵州省科技型种子企业、高新技术企业;是贵州智能制造产业联盟理事长和秘书长单位、贵州区块链产业技术创新联盟理事长和秘书长单位、贵州大数据政产学研用产业联盟副理事长及副秘书长单位、中国云服务联盟理事单位,贵州区块链标准工作组成员单位、贵州省智能制造专家咨询委员会成员。 -","贵州航天云网科技有限公司是中国航天科工集团公司下属单位(11大军工央企之一,本身具备实施军用项目的资格),是由航天云网科技发展有限责任公司、中国航天科工集团公司第十研究院、云上贵州大数据产业发展有限公司共同出资成立;是贵州省人民政府、贵州省经信委、贵阳国家高新区引进的重点高科技大数据企业、国家高新技术企业;是国家大数据(贵州)试验区首批重点大数据企业(核心业态)和贵州省科技型种子企业、高新技术企业;是贵州智能制造产业联盟理事长和秘书长单位、贵州区块链产业技术创新联盟理事长和秘书长单位、贵州大数据政产学研用产业联盟副理事长及副秘书长单位、中国云服务联盟理事单位,贵州区块链标准工作组成员单位、贵州省智能制造专家咨询委员会成员。核心业务包括建设运营贵州工业云平台(贵州大数据试点项目7+N朵云之一);智能制造、协同制造、云制造底层软件开发;区块链技术在工业领域的应用与研究;云端企业智能化、智慧化改造;产品、制造资源共享与协作配套;工业大数据及其应用;云制造应用软件开放式“双创”;工业互联网数据与平台安全;工业互联网标准架构及标准拟制等。 -",1,0,"published",9999,0,"2019-08-08 16:49:31","2020-06-01 12:16:58","航天云网" -171,39,"杭州度言软件有限公司","hangzhouduyanruanjianyouxiango","upload/20190808/15652548052845.png","upload/20190808/15652548561992.png","upload/20190808/15652549173476.png","http://www.duyansoft.com","0571-28204707",1,1,"度言软件创始团队掌握国际领先的语音通信技术,凭借近二十年积累的技术及运营经验,打造出安全可信且具有自主知识产权的智能云联络中心,为国内外企业客户提供极致的客户服务体验。 -","度言软件创始团队掌握国际领先的语音通信技术,凭借近二十年积累的技术及运营经验,打造出安全可信且具有自主知识产权的智能云联络中心,为国内外企业客户提供极致的客户服务体验。 -",1,0,"published",9999,0,"2019-08-08 17:02:27","2020-06-01 12:17:10","度言软件" -172,0,"嘉兴太美医疗科技有限公司","jiaxingtaimeiyiliaokejiyouxian","upload/20190808/15652574607749.png","upload/20190808/15652574649338.png","upload/20190808/15652574681331.jpg","http://www.taimeitech.com/","400-699-1906",1,1,"太美医疗科技是行业领先的生命科学领域技术解决方案提供商,业务覆盖医药研发、药物警戒、医学事务、市场准入和市场营销等领域。 -","太美医疗科技为医药行业提供创新技术解决方案,主要覆盖临床研究、药物警戒、医学事务、市场营销等领域。研发的云平台率先实现全行业协作,制药企业、服务商、医院、监管机构、患者等相关用户在平台上进行数据交换、流程协作,实现标准统一和价值升级。同时公司基于这个平台融合专业服务,为客户提供整体解决方案。 -",1,0,"published",9999,0,"2019-08-08 17:44:51","2020-06-01 12:17:22","太美医疗" -173,0,"ONES","ones7282","upload/20190808/15652579069600.png","upload/20190808/15652579478012.png","upload/20190808/15652579851905.jpg","https://ones.ai/","400-188-1518",1,1,"ONES 成立于2015年,专注于企业级研发管理工具及解决方案。 - -ONES 企业级研发管理工具包含 ONES Project(项目进度管理)、 ONES Plan(项目集管理)、 ONES Wiki(知识库管理)、 ONES TestCase(测试用例与测试计划管理)、ONES Pipeline(持续集成与交付管理)等专业研发管理协作工具,以此产品矩阵贯穿整个研发流程,促进工程师及产品开发的各角色成员进行良好的协作,提升个人创造力与团队效率,从而提升产品交付质量,帮助企业更好更快发布产品。 - -","ONES 成立于2015年,专注于企业级研发管理工具及解决方案。 - -ONES 企业级研发管理工具包含 ONES Project(项目进度管理)、 ONES Plan(项目集管理)、 ONES Wiki(知识库管理)、 ONES TestCase(测试用例与测试计划管理)、ONES Pipeline(持续集成与交付管理)等专业研发管理协作工具,以此产品矩阵贯穿整个研发流程,促进工程师及产品开发的各角色成员进行良好的协作,提升个人创造力与团队效率,从而提升产品交付质量,帮助企业更好更快发布产品。 -",1,0,"published",9999,0,"2019-08-08 17:53:47","2020-06-01 11:11:14","ONES" -174,0,"浙江华坤道威数据科技有限公司","zhejianghuakundaoweishujukejiy","upload/20190808/15652586107329.png","upload/20190808/15652585161181.png","upload/20190808/15652586634659.png","http://geeker.worken.cn","无",1,1,"华坤道威是一家运用人工智能和大数据等前沿技术,为企业提供全程数据智能解决方案的应用专家。凭借多年金融、教育、房产家居等行业经验,并以自主研发的数聚客)、数聚房、全域精准智投平台等数据智能应用产品,被中国国际大数据大会授予“中国大数据领域最佳大数据服务供应商”奖 。 -","华坤道威是一家运用人工智能和大数据等前沿技术,为企业提供全程数据智能解决方案的应用专家。凭借多年金融、教育、房产家居等行业经验,并以自主研发的数聚客)、数聚房、全域精准智投平台等数据智能应用产品,被中国国际大数据大会授予“中国大数据领域最佳大数据服务供应商”奖 。 -",1,0,"published",9999,0,"2019-08-08 18:04:26","2020-06-01 12:17:34","华坤道威" -175,110,"智慧芽信息科技(苏州)有限公司","zhihuiyaxinxikejisuzhouyouxian","upload/20190808/15652589627426.png","upload/20190808/15652589656016.png","upload/20190808/15652589748210.jpg","https://www.zhihuiya.com/","400-694-4481",1,1,""PatSnap智慧芽成立于2007年,作为一家立足于科技的研发创新信息与管理提供商,智慧芽凭借多年积累的人工智能技术和大数据处理能力,始终致力于为创新者提供更好的信息情报产品和服务。 -" -",""PatSnap智慧芽成立于2007年,作为一家立足于科技的研发创新信息与管理提供商,智慧芽凭借多年积累的人工智能技术和大数据处理能力,始终致力于为创新者提供更好的信息情报产品和服务。 - -通过搜集、整理和分析全球数以百亿计的创新数据,智慧芽帮助全世界研发人员和面向市场的创新人员轻松获取技术竞争情报和市场洞察。创立至今,已有全球50多个国家8000多家客户在智慧芽的赋能下,实现高效创新,引领竞争。" -",1,0,"published",9999,0,"2019-08-08 18:09:58","2020-06-01 12:17:45","智慧芽" -176,0,"中通天鸿(北京)通信科技股份有限公司","zhongtongtianhongbeijingtongxi","upload/20190808/15652594768596.jpg","upload/20190808/15652595095609.jpg","upload/20190808/15652596066629.jpg","http://www.icsoc.net","400 616 5600",1,1,"中通天鸿(北京)通信科技股份有限公司创立于2010年,是中国领先的云计算呼叫中心平台及人工智能科技公司。中通天鸿聚焦于企业的“客户服务”场景,其资源云业务依托于基础运营商骨干网,通信云业务打通企业与用户之间的连接,客服云业务构建云上的呼叫中心平台,数据云业务为企业实现以数据为核心的客服系统管理平台。",""中通天鸿(北京)通信科技股份有限公司创立于2010年,是中国领先的云计算呼叫中心平台及人工智能科技公司。中通天鸿聚焦于企业的“客户服务”场景,其资源云业务依托于基础运营商骨干网,通信云业务打通企业与用户之间的连接,客服云业务构建云上的呼叫中心平台,数据云业务为企业实现以数据为核心的客服系统管理平台。基于云计算、人工智能和大数据技术,中通天鸿为企业提供呼叫中心CTI-PLUS、工单、客户管理、在线客服、IM机器人、智能知识库、智能质检、BI大数据平台、语音机器人等产品。 -中通天鸿总部设立于北京,在武汉和长春设有两个技术研发中心,北京、上海、广州设立销售中心,打造“以客户为中心”的行销网络。目前公司员工200多人,汇集了一批来自互联网、通信、IT等行业的资深专家与优秀人才。 -" -",1,0,"published",9999,1,"2020-03-23 13:40:49","2019-08-08 18:20:52","" -177,23,"宁夏西云数据科技有限公司","ningxiaxiyunshujukejiyouxiango","202003/1585011946600174352.jpg",NULL,NULL,"http://www.nwcdcloud.cn/Index.aspx","0955-8815101",0,0,"宁夏西云数据科技有限公司(以下简称“西云数据”)成立于2015年2月,注册资本 5000万元人民币,是一家致力于向中国客户提供卓越的增值电信服务的提供商,主营业务为互联网数据中心服务(IDC)、互联网接入服务(ISP)以及云计算等。西云数据总部设在宁夏中卫,志在以先进技术、优质资源和高品质服务,助力宁夏中卫市政府建设和发展西部云基地,向广大中国企业和开发者客户提供卓越的增值电信服务。","",0,0,"published",9999,0,"2020-03-23 12:31:56","2020-06-01 12:17:58","宁夏西云" -178,52,"中金数据(武汉)超算技术有限公司","zhongjinshujuwuhanchaosuanjish","202004/1585726019448826279.jpg",NULL,NULL,"http://www.centrincloud.com/","027-83388253",0,0,"武汉超算中心是武汉市云计算、大数据、超级计算、人工智能和互联网+等创新产业的公共服务基础设施。公司致力于打造一个以数据中心为基础,以云计算、超级计算、大数据为核心经营内容的信息化资源聚集与产业聚集地。 -中金数据在武汉临空港经济技术开发区的国家网络安全人才与创新基地,投资建设了占地面积310亩,规划建筑面积约50万平米,总体规划建设2.5万个机架、装机容量达30万台服务器的武汉大数据中心,项目总投资为105亿,建成后超级计算服务能力达到1000万亿次/秒,存储容量可达1万PB。","",0,0,"published",9999,0,"2020-03-23 17:49:38","2020-06-01 12:13:11","武汉超算中心" -179,53,"中金金融认证中心有限公司","zhongjinjinrongrenzhengzhongxi","202004/1585728949834248296.jpg",NULL,NULL,"http://www.cfca.com.cn/","400-880-9888",0,0,"中金金融认证中心有限公司(即中国金融认证中心China Financial Certification Authority,简称CFCA),是由中国人民银行于1998年牵头组建、经国家信息安全管理机构批准成立的国家级权威安全认证机构,是国家重要的金融信息安全基础设施之一。 -  在《中华人民共和国电子签名法》颁布后,CFCA成为首批获得电子认证服务许可的电子认证服务机构。截至目前,超过2400家金融机构使用CFCA的电子认证服务,在使用数字证书的银行中占98%的份额","",0,0,"published",9999,0,"2020-03-23 17:51:59","2020-06-01 12:13:41","中国金融认证中心" -180,54,"广州宇中网络科技有限公司","guangzhouyuzhongwangluokejiyou","202004/1585729557464555126.jpg",NULL,NULL,"","",0,0,"广州宇中网络科技有限公司致力于发展成为全球领先的互联网整合营销服务运营商,为各大行业、企业提供先进的互联网推广、电商营销、O2O营销、数据库精准营销的整合营销解决方案,并为企业的各类互联网及线下分销渠道提供应用系统开发、线上推广、线下拓展、服务运营、支付结算、电子销售工具、数据库服务。公司核心创始管理团队成员由全球500强企业、知名的互联网企业的中高层管理、运营及核心技术人才组成,分别拥有海外教育、国际教练认证、个人创业等丰富的跨行业从业、教育背景。目前,公司已获得国内知名的创投基金投资,未来项目发展前景广阔。","",0,0,"published",9999,0,"2020-03-23 17:55:01","2020-06-01 12:13:58","宇中网络" -181,55,"上海亘岩网络科技有限公司","shanghaigenyanwangluokejiyouxi","202004/1585730688651387388.jpg",NULL,NULL,"https://www.qiyuesuo.com/index.html","400-888-9792",0,0,"“契约锁”(www.qiyuesuo.com)成立于2016年,是上海亘岩网络科技有限公司联合数字证书认证中心、权威公证处、知名律协一起全力打造的电子签约和印章管理的一体化服务平台;契约锁实现电子签约和实体印章一体化管控;经过几年创新发展,契约锁已成为新一代电子签约与实体印章管控方案的提供者!","",0,0,"published",9999,0,"2020-03-23 17:57:31","2020-06-01 12:14:14","亘岩网络" -182,65,"三快-美团云","sankuaimeituanyun6607","202003/1585126607603120451.png",NULL,NULL,"www.mtyun.com","4000800170",0,0,"美团云(北京三快云计算有限公司)是美团公司于2013年推出的公有云计算服务平台,致力于为用户提供高效、稳定、可靠的云计算服务。","",0,0,"draft",9999,1,"2020-03-25 17:41:30","0000-00-00 00:00:00","" -183,68,"新网数码","xinwangshuma9958","202003/1585129958694776754.png",NULL,NULL,"http://www.xinnet.com","4008182233",0,0,"新网跟随互联网的原点起来,新网作为互联网基础应用服务提供商,推动着互联网潮流在中国的蓬勃发展… 24年峥嵘岁月,新网历经一代人的努力,从域名出发,发展成为具有:域名注册管理、云计算、企业通讯管理等综合业务的科技服务公司。期间我们为京东、新浪等数千万家企业客户提供了互联网基础应用服务,并和我们生态合作伙伴一起,见证了中国互联网从模仿到引领,从概念到真正改变人们生活的进程。 展望明天,新网仍将秉承“敢想,更敢为”的经营理念,持续强化我们在云计算领域战略优势,并积极拓展企业信息自动化与Saas市场前沿地位。与数万家生态合作伙伴携手,为中国企业的信息化发展提供更优质、高效的基础设施服务。","",0,0,"draft",9999,0,"2020-03-25 17:52:38","2020-06-01 11:11:14","新网数码" -184,72,"盈佳云创","yingjiayunchuang0626","202003/1585130626728412505.png",NULL,NULL,"http://www.servingcloud.com/","4000888600",0,0,"盈佳云创 于2016年4月成立于深圳, -是一家帮助大型集团企业建立供应商协同管理的SaaS服务企业, -为供应商和核心企业之间提供合同、产值、发票、结算、供应链金融等管理和协同服务。 -经过3年的发展,盈佳云创已为35000家供应商与数十家核心企业提供了长期优质的服务, -在解决大型企业的管理问题的同时,","",0,0,"draft",9999,0,"2020-03-25 18:03:46","2020-06-01 11:11:14","盈佳云创" -185,73,"亿云信息","yiyunxinxi0809","202003/1585130809088437163.png",NULL,NULL,"http://www.evayinfo.com/","0531-82605484",0,0,"山东亿云信息技术有限公司是山东省科学院孵化的国家高新技术企业,公司致力于大数据、云计算、人工智能、区块链等前沿技术的研发,秉承技术驱动服务理念,专注于政务信息化生态的构建,打造了面向政务服务和行业服务的云平台、业务中台和数据中台三个核心平台,在智慧城市、数字政府、人才服务、大数据支撑应用及云服务等领域为政府及企事业单位提供咨询、规划、建设、运维、运营等全方位的解决方案、产品和服务。","",0,0,"published",9999,0,"2020-03-25 18:06:49","2020-07-30 15:13:16","亿云信息" -186,74,"懒猫银联","lanmaoyinlian0990","202003/1585130990235247160.png",NULL,NULL,"www.lanmaoly.com","010-57648793",0,0,"懒猫联银是一家专注于“监管科技”的科技企业。在科技金融突飞猛进的当前,致力于通过科技手段帮助监管部门落地监管政策和措施,有效提升监管科技效率及水平;同时帮助被监管对象合规经营,有效降低合规成本的同时提升业务运营效率。从而推进科技金融健康可持续发展。","",0,0,"draft",9999,0,"2020-03-25 18:09:50","2020-06-01 11:11:14","懒猫银联" -187,75,"武汉烽火信息","wuhanfenghuoxinxi1190","202003/1585131190663383433.png",NULL,NULL,"http://www.whfhjc.com.cn/","+86-27-67840335",0,0,"武汉烽火信息集成技术有限公司成立于2002年,是上市公司烽火通信科技股份有限公司的全资子公司。公司主要从事智慧城市、云计算和大数据领域的咨询规划、产品和解决方案提供、工程建设以及运营运维服务等。公司资质全面,技术实力雄厚,拥有计算机信息系统集成大型壹级资质证书、涉及国家秘密的计算机系统集成甲级资质、公路交通工程及建筑专业承包综合证书...","",0,0,"draft",9999,0,"2020-03-25 18:13:10","2020-06-01 12:12:40","烽火信息" -188,76,"烽火通信","fenghuotongxin1378","202003/1585131378887896436.png",NULL,NULL,"http://www.fiberhome.com/","",0,0,"烽火通信科技股份有限公司(股票代码:600498)是信息通信网络产品与解决方案提供商。自1999年成立至今,烽火通信始终专注于全球信息与通信事业的进步与发展,积累了对人类信息通信生活的深刻理解和创造力。 -  烽火通信肩负“最大限度挖掘数字连接价值,造福人类社会”使命,凭借在信息和通信领域的深厚积累,坚持“客户导向、诚信敬业、持续创新、增量发展”的核心价值观,致力于让人人享有通信技术发展带来的美好。","",0,0,"draft",9999,0,"2020-03-25 18:16:18","2020-06-01 11:11:14","烽火通信" -189,77,"蓝盾","landun1627","202003/1585131627124360347.png",NULL,NULL,"http://www.bluedon.com/","020-85526663",0,0,"公司始终以自主创新为发展源动力,以技术研发领先优势抢占市场,经过十多年的探索和积累,已掌握了信息安全领域内的主要核心技术,并实现了全线安全产品的人工智能化。公司目前掌握的主要技术处于国内领先地位,凭借领先的技术实力,公司先后承担并实施了包括国家公安部科技攻关项目在内的数十项国家级、部级、省市区级的重点信息安全科研项目,并为国家公安部等部委制定行业技术标准发挥了重要作用。","",0,0,"published",9999,0,"2020-03-25 18:20:27","2021-07-06 15:32:48","蓝盾" -190,78,"云端网络","yunduanwangluo1726","202003/1585131726209790030.png",NULL,NULL,"www.verycloud.cn","400-005-1023",0,0,"公司坚持走自主研发的道路,以技术、服务、管理创新为支撑,展开全方位的服务理念。我们自主研发的VeryCDN GSLB全球负载均衡系统、VeryCDN加速系统、超 级云端等产品已获得国家版权局颁发的15项软件著作权证书。并成功推出VeryCloud与 VeryCDN面向不同规模客户的两个云服务平台。产品及其解决方案可为用户 提供从网络链路资源到服务器资源的全方位优化和提升,充分提高IT资源使用效 率,降低成本,提升系统可靠性,全面减少和消除各类故障所带来的损失,大大 降低用户的数据和运营等风险。","",0,0,"draft",9999,0,"2020-03-25 18:22:06","2020-06-01 11:11:14","云端网络" -191,81,"中通天鸿","zhongtongtianhong3048","202003/1585133048466719527.png",NULL,NULL,"http://www.icsoc.net/","400-668-3333",0,0,"中通天鸿聚焦于企业客户服务领域,秉承“让客户服务便捷高效”的理念,坚持自主创新、开放合作,为5000+企业提供客服系统平台服务。合作客户包含近百家全球500强企业,业务领域涉及互联网、金融保险、电子商务、IT 电子、传统制造、物流运输、O2O服务、汽车出行、教育培训等行业。","",0,0,"draft",9999,0,"2020-03-25 18:44:08","2020-07-27 13:37:14","中通天鸿" -192,82,"蓝汛通信","lanxuntongxin3471","202003/1585133471684207937.png",NULL,NULL,"http://www.chinacache.com/","4006850196",0,0,"","",0,0,"draft",9999,0,"2020-03-25 18:51:11","2020-06-01 11:11:14","蓝汛通信" -193,83,"汇智软件","huizhiruanjian3595",NULL,NULL,NULL,"http://www.wizcloud.cn/","4006716816",0,0,"汇智软件凭借自身所积累的丰富的研发、运维、集成及高科技园区中小企业IT服务经验,打造专业服务科技型中小企业的“园区云”,构建绿色、创新的高科技产业“智慧园区”,成为国内一流的产业园区IT综合服务商、产业园区智慧化整体解决方案提供商。","",0,0,"draft",9999,0,"2020-03-25 18:53:15","2020-06-01 11:11:14","汇智软件" -194,84,"沃丰时代","wofengshidai6840","202003/1585136840489329585.png",NULL,NULL,"http://www.udesk.cn/","4000-567-808",0,0,"国内第一的智能客户体验解决方案提供商、智能客服领域的独角兽、SaaS领域的明星企业。依托云计算、大数据、人工智能技术,Udesk成功将全维度的智能系统应用到企业客户服务的各个场景,致力于用技术帮助企业建立体验经济时代的核心竞争力,通过卓越服务成就卓越企业。真正实现客户服务对企业经营的全面赋能","",0,0,"draft",9999,0,"2020-03-25 19:47:20","2020-06-01 11:11:14","沃丰时代" -195,90,"网心科技","wangxinkeji7302","202003/1585197302242831243.png",NULL,NULL,"www.onethingtech.net","400-109-8880",0,0,"网心科技以“Forward志在前行”为企业理念,拥有不断超越的创业激情和强大的研发实力。研发团队占公司总人数80%以上,其中1/3研发力量来自BAT、微软等核心业务领域的资深骨干级专家,具有丰富的技术研发和运营经验,不断为行业和客户创造超出期待的产品体验。","",0,0,"draft",9999,0,"2020-03-26 12:35:02","2020-06-01 11:11:14","网心科技" -196,93,"容联云","ronglianyun4317","202003/1585284317657632939.png",NULL,NULL,"www.yuntongxun.com","4006101019",0,0,"北京容联易通信息技术有限公司(简称容联),国家高新技术企业,专业的智能通讯云服务商。 -容联总部位于北京,在上海、广州、深圳、武汉、成都、杭州、贵阳、东京等地设有分支机构。目前拥有员工超过1000人。拥有专利申请数十项、软著数百件,通过软件CMMI5认证。","",0,0,"draft",9999,0,"2020-03-27 12:45:17","2020-06-01 11:11:14","容联云" -197,94,"同盾科技","tongdunkeji4543","202003/1585284544392038089.png",NULL,NULL,"www.tongdun.cn","4000689796",0,0,"同盾科技总部位于浙江杭州,是国内专业的智能分析决策服务提供商,将人工智能与业务场景深度结合,为金融、互联网、物流、政务等行业提供智能信贷风控、智能反欺诈、智能运营等智能决策产品&服务。截至目前,已累计有超过一万家企业客户选择了同盾的产品及服务。","",0,0,"draft",9999,0,"2020-03-27 12:49:03","2020-06-01 11:11:14","同盾科技" -198,95,"迅联云","xunlianyun4681","202003/1585284681545905321.png",NULL,NULL,"http://www.xunliancloud.com/","4008363566",0,0,"专注发票数字化助力企业数字化转型,利用智慧校检实现合规合法化,通过数据可视化实现高质量管理。基于财务供应链协同(开票协同、全票种收票、收票查验、专票认证)连接产业链上下游,作为企业连接器实现计划、订单、对账、收发货协同的智慧供应链网络。","",0,0,"draft",9999,0,"2020-03-27 12:51:21","2020-06-01 11:11:14","迅联云" -199,96,"道一云","daoyiyun4807","202003/1585284807866969414.png",NULL,NULL,"wbg.do1.com.cn","4001112626",0,0,"道一云 是集OA、CRM、HR的一站式智能办公平台。拥有表单流程、CRM、培训考试、任务分派、费控报销、人事考核、企业云盘、年会活动等二十多个标准应用。基于企业微信,助力企业轻松搭建移动工作平台及营销服务平台,帮助企业实现智能化、移动化的高效工作方式,节省管理成本。","",0,0,"draft",9999,0,"2020-03-27 12:53:27","2020-06-01 11:11:14","道一云" -200,97,"伏泰科技","futaikeji4996","202003/1585284996945179498.png",NULL,NULL,"http://www.vortexinfo.cn/","4001899019",0,0,"","",0,0,"draft",9999,0,"2020-03-27 12:56:36","2020-06-01 11:11:14","伏泰科技" -201,98,"北京安证通信息科技股份有限公司","anzhengtong5223","202003/1585285223396533554.png",NULL,NULL,"http://www.esa2000.com/","4008111965",0,0,"北京安证通信息科技股份有限公司成立于2003年,是国内最早从事电子签章技术开发与应用推广的高科技企业之一。公司坐落于北京中关村软件园,是北京市认定的双软认证企业和高新技术企业。","",0,0,"published",9999,0,"2020-03-27 13:00:23","2020-08-04 12:04:42","安证通" -202,99,"铜牛信息","tongniuxinxi5395","202003/1585285395461186968.png",NULL,NULL,"http://www.topnewinfo.cn/","4006262222",0,0,"北京铜牛信息科技股份有限公司由北京时尚控股,为北京市国资委下属二级企业,是北京市高新技术企业和软件企业。公司于2013年7月5日正式登陆“新三板”(股票代码:430243)成为公众公司。2014年8月首批做市成为新三板成分指数和做市指数样本股,2016年6月首批成功进入新三板创新层。公司通过了ISO9001质量管理体系认证和ISO27001信息安全管理体系认证,获得了国家工业和信息化部颁发的增值电信业务全国经营许可证,是专业的互联网数据中心服务、互联网接入服务、高端IT服务和云计算服务提供商。","",0,0,"draft",9999,0,"2020-03-27 13:03:15","2020-07-29 10:15:29","铜牛信息" -203,44,"楚天云","chutianyun9119","202004/1585719119210691059.png",NULL,NULL,"http://www.chutianyun.gov.cn/","027-65527900",0,0,"楚天云工程作为湖北省委、省政府加快智慧湖北建设的重大工程,是集云计算、大数据于一体的全省统一云基础设施综合服务平台和数据交换枢纽平台。","",0,0,"published",9999,0,"2020-04-01 13:31:59","2020-06-01 11:11:14","楚天云" -204,127,"福建省数字福建云计算运营有限公司","fujianshengshuzifujianyunjisua","202004/1585807751001284403.jpeg",NULL,NULL,"","",0,0,"福建省数字福建云计算运营有限公司成立于2015年,是福建省电子信息集团的全资子公司,注册资本5.64亿元。 数字福建云计算公司投资13.3亿,按照国际最高T4标准建造了数字福建云计算中心(商务云)。 - 公司面向全国企事业单位提供高可靠的机柜租赁、机房租赁、IT运维、咨询等数据中心专业服务;基于“海丝乐云”,提供高安全的企业级云计算服务,聚焦集团云专区、国企云专区、安全云专区等云专区建设,推动行业混合云落地;同时战略布局超算中心、人工智能等领域。","",0,0,"published",9999,0,"2020-04-02 14:09:11","2020-06-01 12:12:12","数字福建" -205,131,"北京世纪互联宽带数据中心有限公司","beijingshijihuliankuandaishuju","202004/1587003466391356790.png",NULL,NULL,"https://www.21vianet.com/","400-651-9966",0,0,"世纪互联(纳斯达克:VNET)成立于1996年,是全球具有重要影响力的网络空间基础设施服务提供商之一,也是中国领先的第三方中立数据中心运营商之一。世纪互联致力于提供业界领先的数据中心服务、中立的云运营及中立的混合IT服务,打造具有核心技术、超大规模运营能力、高附加值的网络空间基础设施运营平台。 -截至目前,世纪互联在全国20多个城市运营超过50个数据中心,拥有超过36000个机柜,端口容量达2000G以上,具有优质的网络可靠性和稳定性,并已为全球超过5000家优质企业客户打造了业务发展的稳固基石,其中包括近百家独角兽、超级独角兽或上市公司,积累了难以复制的成功运营经验。","",0,0,"published",220,0,"2020-04-15 19:44:43","2020-06-01 12:04:04","世纪互联" -206,133,"北京合力亿捷科技股份有限公司","beijingheliyijiekejigufenyouxi","202006/1591690169225475388.jpg",NULL,NULL,"https://www.hollycrm.com","4006-816-505",0,0,"北京合力亿捷科技股份有限公司(简称:合力亿捷)股票代码:833629,始建于2002年,是专注于通讯领域的软件和整体解决方案的科技公司,致力于帮助企业实现内外沟通的大连接。公司业务范围包括:呼叫中心系统、智能客服系统、企业级项目管理平台、集团业务支撑软件等。 -合力亿捷服务的客户覆盖电信、金融、教育、保险、汽车、速递物流、电子商务、生产制造等十几个领域,包括中国联通、EMS、58同城、好未来、智联招聘、阳光保险、蔚来汽车等众多知名企业,每天有超过8000万人通过合力亿捷的产品获得资讯和服务。","",0,0,"published",9999,0,"2020-06-09 16:09:29","2021-08-12 14:42:32","合力亿捷" -207,139,"北京公采云信息技术有限公司","beijinggongcaiyunxinxijishuyou","202007/1594262517405911576.jpeg",NULL,NULL,"http://www.gcycloud.cn/","400-666-0839",0,0,"公司长期致力于采购行业信息化系统的开发和建设,核心产品“公采云”平台,为政府、高效、国企客户提供解决方案及服务。以“功能完善、资源共享、规范透明、安全高效”为总体目标,提供全采购业务覆盖,全流程电子化,全采购主体参与的互联网+政府采购一体化云平台解决方案。 -公司现承担财政部中国政府采购网、中央政府采购网、福建全省政府采购网上公开系统、甘肃省政府采购网上网上等12个省部级政府以及数十家国央企、100多所高校采购的信息化建设及运维工作。 -现有研发和技术人员超过500人,是国内专注于政府采购信息化领域的最大规模团队。平台支持公有云(SaaS)和私有云(政务云)的建设运营模式,同时与国内主流云厂商均有合作。","",0,0,"published",9999,0,"2020-07-09 10:39:50","2021-08-12 14:43:25","北京公采云" -208,141,"上海会畅通讯股份有限公司","shanghaihuichangtongxungufenyo","202007/1594263854809660781.png",NULL,NULL,"https://www.bizconf.cn/","400-062-1818",0,0,"会畅通讯(股票代码:300578)成立于2006 年,秉承“沟通创造价值”的服务理念,专注于为企业提供高品质的视频会议、网络直播、电话会议和网络会议等云视频会议服务,并在全球范围内与国际运行商开展战略合作,服务网络遍布全球150多个国家和地区,向用户提供7*24小时的中英双语优质服务,真正实现“为客户全球服务,服务于全球客户”。 -会畅通讯的客户群体遍布各个行业及领域,70%的世界500强在华企业都选用会畅通讯的云视频平台作为日常工作和沟通的重要工具。目前公司在北京、上海、广州设有分支机构,并在中国超过三分之一的省份和直辖区域设有服务团队。","",0,0,"published",9999,0,"2020-07-09 11:04:14","2021-08-12 14:43:39","会畅通讯" -209,136,"浙江百应科技有限公司","zhejiangbaiyingkejiyouxiangong","202007/1594266711095761459.png",NULL,NULL,"https://www.byai.com/","400-0235-100",0,0,"百应科技成立于2016年9月,是一家领先的人工智能创新企业。旗下核心产品为企业级AI中台(BOTLAB)和数智化客户关系管理(AICRM)云平台,致力于为企业提供营销、销售、服务、运营、管理一站式数智化解决方案,百应通过自主研发的ASR、NLP、TTS、多模态对话、知识图谱、机器学习等核心技术,以智能服务机器人(语音机器人、文字机器人、数字虚拟机器人)为基础,提供智能营销云、智能销售云、智能服务云、智能决策云、以及数智化中台五大板块产品矩阵。","",0,0,"published",9999,0,"2020-07-09 11:51:51","2021-08-12 14:43:52","百应科技" -210,135,"首信云技术有限公司","shouxinyunjishuyouxiangongsi32","202007/1594272132953345519.png",NULL,NULL,"","85511155-5501",0,0,"首信云技术有限公司是一家专业从事云计算业务的高科技企业。首信云坚持“助力提升城市智慧创新发展能力,让城市更智慧,让生活更便捷”的发展愿景,为客户提供基于云的一体化服务及行业领先通用SaaS、共性业务、数据化平台服务;安全、可信的云基础设施服务;实用、便捷的云定制化服务,全面助力提升政府、企业的管理、运营与服务能力。通过云上应用的协同、产业数据的汇聚,首信云为政府整合化服务、产业协同发展、智慧城市的创新服务贡献力量。","",0,0,"published",9999,0,"2020-07-09 13:22:12","2021-08-12 14:44:19","首信云" -211,134,"北京贰零四玖云计算数据技术服务有限公司","beijingerlingsijiuyunjisuanshu",NULL,NULL,NULL,"www.boncloud.com","010-84866066",0,0,"BONCLOUD是依托北京东方国信科技股份有限公司先进的云计算技术和多年沉淀丰富的软件产品,结合优质数据中心资源,在深入理解政府、运营商、互联网、传统企业不同场景业务需求的基础上,全力打造的云服务平台,平台提供弹性计算、存储,网络、CDN等安全稳定的云计算资源。 -云业务运营及相关客户服务工作由东方国信参股公司北京贰零四玖云计算数据技术服务有限公司负责开展。","",0,0,"published",9999,0,"2020-07-09 14:52:18","2021-08-12 14:44:27","贰零四玖" -212,137,"北京数字认证股份有限公司","beijingshuzirenzhenggufenyouxi","202007/1594368494693090428.png",NULL,NULL,"https://www.bjca.cn/","4009197888",0,0,"北京数字认证股份有限公司(简称“数字认证”)成立于2001年2月,是北京市国有资产经营有限责任公司控股的国有企业,是国内领先的信息安全解决方案提供商,主要业务为电子认证服务、电子认证产品及可管理的信息安全服务。2016年12月,公司在创业板成功上市,股票简称:数字认证,股票代码:300579。 -作为领先的电子认证企业,数字认证遵照《中华人民共和国电子签名法》的要求和管理规定,为用户提供涵盖电子认证服务和电子认证产品的整体解决方案,建立起覆盖全国的电子认证服务网络和较完善的电子认证产品体系。应用领域覆盖政府、金融、医疗卫生、彩票、电信等市场,并在医疗信息化、网上保险、互联网彩票等新兴应用领域建立了市场领先优势。","",0,0,"published",9999,0,"2020-07-10 15:38:25","2021-08-12 14:44:34","数字认证" -213,138,"尚阳科技股份有限公司","shangyangkejigufenyouxiangongs","202007/1595390867484745312.png",NULL,NULL,"www.systec.com.cn","400-0220-266",0,0,"尚阳科技股份有限公司(简称:尚阳股份,证券代码:835888)是以客户体验为中心的智能服务商,专业提供新IT信息技术解决方案的综合服务,致力于成为卓越的智能服务提供商,协助客户加速创新发展。 -尚阳股份依托近20年行业经验,专注于混合云和视频云等专业领域。以软件技术为核心,针对不同行业客户,提供公有云、私有云、混合云解决方案,定制化IT系统集成解决方案和针对性的IT综合运维服务。以满足客户需求为己任,凭借最佳实践行业经验及技术实力推出自主研发的Umeet智能会议平台、物联网智能数据平台、多云管理平台。","",0,0,"published",9999,0,"2020-07-22 11:53:20","2021-08-12 14:47:45","尚阳科技" -214,142,"北京容联易通信息技术有限公司","beijingronglianyitongxinxijish","202007/1595577650714942162.jpg",NULL,NULL,"www.yuntongxun.com","400-610-1019",0,0,"容联以云化和智能化的方式,为企业客户提供全面的通讯服务。包括PaaS通讯能力(语音、短信等)、CC(云客服与云联络中心)、UC(IM即时通讯云、融合通讯、视频与会议)、行业新通讯解决方案和“通讯+AI”服务,助力企业提高沟通体验和经营效率,驱动中国企业通讯产业实现互联网化、云计算化、能力化、融合化和智能化。","",0,0,"published",9999,0,"2020-07-24 16:00:50","2021-08-12 14:47:58","容联易通" -216,147,"同盾科技有限公司","tongdunkejiyouxiangongsi8418","202007/1595582251602835371.png",NULL,NULL,"https://www.tongdun.cn","400-068-9796",0,0,"同盾科技是中国智能分析和决策领域领军企业,以人工智能、云计算、大数据三大核心技术体系为基础,基于对数据的探索洞察和深刻理解,将深度学习、联邦学习等领先技术与业务场景深度融合,为金融、保险、互联网、政务、零售、物流等行业提供智能分析与决策服务,赋能并激发客户,帮助客户做出更佳决策。截至目前,累计已有超过一万家客户选择了同盾的产品及服务。","",0,0,"published",9999,0,"2020-07-24 16:13:38","2021-08-12 14:48:09","同盾科技" -218,125,"上海云励科技有限公司","shanghaiyunlikejiyouxiangongsi","202007/1595816895937036775.jpg",NULL,NULL,"https://www.xforceplus.com/","4006304000",0,0,"上海云励科技有限公司成立于 2019 年 7 月 25 日,是专业的供应链信息协同及增值税发票管理云平台解决方案提供商,公司主营技术开发及将技术产品市场化运营,目前已面世的增值税发票管理及供应链协同平台,为数百家中心用户及数十万家中心用户相关供应商提供稳定服务。公司核心团队来自宝钢集团、中石化集团、SAP、麦肯锡、埃森哲、阿里巴巴、百度、普华永道、Splunk等知名企业,在技术研发及产品运营方面拥有丰富的经验及资源。公司的主要为众多大型企业提供基于互联网和云创新的软件、数据服务。2019年,公司获得淡马锡、高瓴等知名投资机构合计超7000万美金的投资。","",0,0,"published",9999,0,"2020-07-27 10:16:19","2021-08-12 14:48:25","云励科技" -220,144,"中通天鸿(北京)通信科技股份有限公司","zhongtongtianhong8527","202007/1595828703593912694.jpg",NULL,NULL,"https://www.icsoc.net","4006165600",0,0,"中通天鸿创立于2010年,是国内领先的全场景智能呼叫中心解决方案提供商。公司聚焦于企业与用户的连接交互,通过将自身积累的通信技术与ABC技术融合,为企业提供覆盖售前、售中和售后全场景的解决方案,推动企业呼叫中心数字化,实现智慧服务、智能营销和科学管理,激发服务的商业价值,帮助企业降本、增收、提效,让呼叫中心从成本中心走向利润中心和价值中心。 - -中通天鸿聚焦于企业客户服务领域,秉承“让客户服务便捷高效”的理念,坚持自主创新、开放合作,为5000+企业提供客服系统平台服务。合作客户包含近百家全球500强企业,业务领域涉及互联网、金融保险、电子商务、IT 电子、传统制造、物流运输、O2O服务、汽车出行、教育培训等行业。","",0,0,"published",9999,0,"2020-07-27 13:42:07","2021-08-12 14:48:46","中通天鸿" -221,146,"苏州市伏泰信息科技股份有限公司","suzhoushifutaixinxikejigufenyo","202007/1595916465111159135.png",NULL,NULL,"http://www.vortexinfo.cn/","4001899019",0,0,"苏州市伏泰信息科技股份有限公司成立于2005年,注册资金3838.3252万元,是一家专业从事智慧环境与互联网+垃圾分类运营解决方案和第三方监管服务的高新技术企业。公司以政府环境IT解决方案、环境治理企业大数据与云计算解决方案、互联网+垃圾分类运营解决方案为主营业务,囊括市政环卫、焚烧餐厨、水治理、危废、环境监测、环境修复的综合性服务商,具备成熟的项目咨询、规划、建设、运营一体化经验。","",0,0,"published",9999,0,"2020-07-28 14:07:44","2021-08-12 14:49:06","伏泰科技" -222,148,"深圳复临科技有限公司","shenzhenfulinkejiyouxiangongsi","202007/1595932655385422997.png",NULL,NULL,"https://ones.ai/","400-188-1518",0,0,"深圳复临科技有限公司 (ONES)成立于 2015 年,专注于企业级研发管理工具及解决方案。 -ONES 企业级研发管理工具包括 ONES Project(项目进度管理)、ONES Plan(项目集管理)、ONES Wiki(知识库管理)、ONES TestCase(测试用例与测试计划管理)、ONES Pipelline(持续集成与交付管理)、ONES Account(企业级账号目录)、ONES Desk(工单管理)等专业研发管理协作工具,以此产品矩阵贯穿整个研发流程,促进工程师及产品开发的各角色成员进行良好的协作,提升个人创造力与团队效率,从而提升产品交付质量,帮助企业更好更快发布产品。","",0,0,"published",9999,0,"2020-07-28 18:37:35","2021-08-12 14:49:19","复临科技" -223,149,"云之端网络(江苏)股份有限公司","yunzhiduanwangluojiangsugufeny","202007/1595986768923033034.jpg",NULL,NULL,"www.verycloud.cn","4000051023",0,0,"云之端网络(江苏)股份有限公司(以下简称“云端网络”),创立于2006年, 2015年12月新三板上市。旗下超过10家独立自建机房以及200多家合作机房、遍布全国的网络资源。云端网络是国内互联网数据领域的专业服务提供商,为传统企业转型互联网和互联网企业提供数据接入(IDC)、数据加速(CDN)、数据传输(ISP)、数据存储(云服务)、数据安全以及大数据分析应用,从客户端到服务端的各项数据需求提供一站式专业定制化的解决方案。 -公司拥有超过百人的技术团队,有多名云计算、数据安全、网络等领域的技术专家,技术精湛、吃苦耐劳、敬业爱岗以及高度负责的工作态度体现在为用户服务的每一个环节,保证为客户提供专业、快捷、畅通的服务。","",0,0,"published",9999,0,"2020-07-29 09:39:28","2021-08-12 14:49:37","云端网络" -224,150,"辽宁途隆科技有限公司","liaoningtulongkejiyouxiangongs","202007/1595986989476718313.png",NULL,NULL,"www.tulong.com","024-88585199",0,0,"辽宁途隆科技有限公司(简称途隆科技)始创于2014年,是一家集研发、运维、服务于一体的高科技IT企业,主要致力于网络安全产品、企业云计算的研究和开发。能够向企事业单位及个人用户提供国内领先的各类整体解决方案、安全产品及安全服务,为互联网信息时代的国家、企业、个人保驾护航。公司秉持开源创新、责任担当、专业专注、自我进步、价值共享、团队意识为核心价值观,致力于为用户提供极致的产品体验。","",0,0,"published",9999,0,"2020-07-29 09:43:09","2021-08-12 14:52:44","途隆安全云" -225,151,"杭州朗和科技有限公司","hangzhoulanghekejiyouxiangongs","202010/1604048416966893781.png",NULL,NULL,"https://www.163yun.com/","",0,0,"","",0,0,"published",9999,0,"2020-10-30 17:00:16","2020-11-03 11:57:04","杭州朗和" -226,152,"北京首钢自动化信息技术有限公司","beijingshougangzidonghuaxinxij","202101/1610348068306529199.png",NULL,NULL,"www.sgai.com.cn","01088292500",0,0,"北京首钢自动化信息技术有限公司是首钢集团旗下自动化、通信、信息化专业技术公司,是集咨询规划、系统集成、软件开发、云计算服务于一体的高新技术企业。具备承担大型企业自动化、信息化“交钥匙”工程的整体能力。","",0,0,"published",9999,0,"2021-01-11 14:54:28","2021-08-12 14:53:02","首钢自动化" -227,153,"联通(江苏)产业互联网有限公司","liantongjiangsuchanyehulianwan","202101/1610353365326067623.png",NULL,NULL,"","",0,0,"联通(江苏)产业互联网有限公司聚合产业链,依托互联网平台技术,通过集成、全云化运营及大数据能力,聚焦政务、工业制造、教育、医卫、交通旅游、生态环境六大行业,为客户提供从连接、云平台到大数据应用的一站式解决方案。","",0,0,"published",9999,0,"2021-01-11 16:22:44","2021-08-12 14:54:13","江苏联通" -228,154,"厦门亿联网络技术股份有限公司","xiamenyilianwangluojishugufeny","202101/1610357919855940240.png",NULL,NULL,"https://www.ylyun.com/portal/","",0,0,"亿联网络技术股份有限公司(股票代码:300628)是面向全球的企业通信上市公司,提供国际品质、技术领先、体验友好的云+端视频会议、IP语音通信及协作解决方案,且与微软等国际品牌达成长期深度的战略合作。亿联网络以研发为核心,坚持自主创新,拥有多项音视频核心技术专利,致力于让沟通更简单、更高效,帮助各类企事业单位提高效率和竞争力,其自主品牌Yealink畅销美国、英国、澳大利亚等140多个国家和地区,SIP话机市场占有率为全球第一(Frost&Sullivan数据),用户满意度全球第一(Eastern Management Group调研数据)。","",0,0,"published",9999,0,"2021-01-11 17:38:39","2021-08-12 14:53:45","亿联网络" -229,155,"招商局金融科技有限公司","zhaoshangjujinrongkejiyouxiang","202101/1610417884520393280.png",NULL,NULL,"https://www.cmft.com/","",0,0,"招商局金融科技有限公司隶属于中央直接管理的国有重要骨干企业招商局集团,致力于大数据、云计算、人工智能等前沿技术研究,提供金融科技行业解决方案,促进招商局实体产业和金融产业融合,助力打造招商局金融生态圈。","",0,0,"published",9999,0,"2021-01-12 10:18:04","2021-08-12 14:54:02","招商金科" -230,156,"北京矩阵分解科技有限公司","beijingjuzhenfenjiekejiyouxian","202101/1610418939528161102.png",NULL,NULL,"www.letsign.com","",0,0,"电子牵是巨量引擎旗下面向企业和个人的一站式文件签署&管理平台,为企业和个人提供快捷、灵活、安全、合规的文件签约 SaaS 服务。","",0,0,"published",9999,0,"2021-01-12 10:35:39","2021-08-12 14:54:24","矩阵分解" -231,158,"腾讯科技(上海)有限公司","tengxunkejishanghaiyouxiangong","202101/1610430181114920720.png",NULL,NULL,"https://wetest.qq.com","",0,0,"腾讯WeTest是由腾讯官方推出的一站式品质开放平台,致力于质量标准建设、产品质量提升,为移动开发者提供兼容性测试、云真机、性能测试、安全防护等优秀研发工具,为百余行业提供解决方案,覆盖产品在各阶段的测试需求。","",0,0,"published",9999,0,"2021-01-12 13:43:01","2021-08-12 14:55:00","腾讯科技" -232,159,"深圳市众云数据技术有限公司","shenzhenshizhongyunshujujishuy","202101/1610438595223566264.png",NULL,NULL,"www.xyclouds.cn","",0,0,"XYclouds托管云平台由深信服合作伙伴众云数据提供运营,专注于为用户提供简单、安全、省心的一站式托管云服务。 -在业务上,XYclouds托管云基于深信服业界领先的安全及云计算技术,可交付包括但不限于云数据中心的建设咨询及托管、安全托管、用户数据、业务容灾、IT架构设计及业务咨询等服务。 -在技术上,托管云整合了有利于用户业务的多种技术方案,让用户根据自身业务需要随需选择,以极少的资金即可开展自身业务,帮助用户享受业务云化的便利。 -在服务上,托管云依托丰富的技术专家团队为用户提供7*24小时的快速响应服务,让用户聚焦自身业务价值,无后顾之忧,为用户业务发展保驾护航。","",0,0,"published",9999,0,"2021-01-12 16:03:15","2021-08-12 14:57:03","深圳众云" -233,160,"戴尔科技集团","daierkejijituan5579","202101/1611125579528014092.jpg",NULL,NULL,"https://www.delltechnologies.com/zh-cn/cloud/index.htm#scroll=off","400-884-6610",0,0,"戴尔科技云平台(Dell Technologies Cloud Platform,简称DTCP) 是将集团旗下Dell EMC和VMware在硬件、软件、服务及消费方案的优势整合在一起,在硬件管理和自动化运维等方面进行了全新研发,实现了多云环境下一致性的管理使用体验和通用的管理方法,可以有针对性地解决不同行业、不同场景的上云问题,消除复杂性的同时,提高运营效率。","",0,0,"published",9999,0,"2021-01-20 14:52:59","2021-08-12 15:13:34","戴尔" -234,161,"西安未来国际信息股份有限公司","xianweilaiguojixinxigufenyouxi",NULL,NULL,NULL,"http://www.ourfuture.cn","029-88236551",0,0,"西安未来国际信息股份有限公司创立于1997年,注册资本1.279亿元。未来国际专注于采用先进信息技术为社会治理现代化和社会公共服务提供专业信息技术综合服务,向客户提供包括规划咨询、设计研发、系统集成、运行维护、运营管理等在内的全程信息化服务。是国内最早成功与政务信息化实现整体服务外包、最早实现统筹集约的全省“政务云”服务企业、全国大数据和云服务产业的龙头企业之一,在全国电子政务类企业中排名前三,是10余个国家部委、20多个省(直辖市、自治区)、60多个省会城市和地级市、上百个各级行业主管部门的信息化服务提供商,被中央网信办、国家发改委、国家工信部等行业主管部门列为重点企业或试点单位。","",0,0,"published",9999,0,"2021-01-28 09:45:47","2021-08-12 15:13:58","未来信息" -235,162,"中电云计算有限公司","zhongdianyunjisuanyouxiangongs","202103/1617184099894161888.png",NULL,NULL,"https://cecloud.com/","",0,0,"中国电子云隶属于中国电子(CEC),采用被誉为“中国架构”的“PK”体系(飞腾CPU+麒麟操作系统),并加入“S-Security”立体防护的安全可信链,重点面向政府、公共服务机构、央企国企等提供专属公有云服务,为政企客户数字化转型提供安全保障。","",0,0,"published",20,0,"2021-03-31 17:48:19","2021-04-08 13:45:08","" -236,176,"深圳价值在线信息科技股份有限公司","shenzhenjiazhizaixianxinxikeji","202107/1626402180439292689.png",NULL,NULL,"www.valueonline.cn","400-880-3388",0,0,"价值在线致力于为上市公司、拟上市公司、金融机构、监管机构等主体提供 AI+SAAS服务产品——易董,填补了我国资本市场合规科技与监管科技双空白。 -聚合资本市场合规监管专业知识,打造集知识管理、合规流程控制、大数据挖掘、合规共创为一体的智能合规平台。","",0,0,"published",9999,0,"2021-07-16 10:23:00","2021-08-12 15:14:05","价值在线" -237,174,"中国银联股份有限公司","zhongguoyinliangufenyouxiangon","202107/1626761950280975365.jpeg",NULL,NULL,"https://yun.unionpay.com/","021-20638828",0,0,"中国银联是中国银行卡联合组织,通过银联跨行交易清算系统,实现商业银行系统间的互联互通和资源共享,保证银行卡跨行、跨地区和跨境的使用。中国银联网络遍布中国城乡,并已延伸至亚洲、欧洲、美洲、大洋洲、非洲等170个国家和地区。银联云是中国银联股份有限公司的云服务品牌,以“源于开源、安全可控、立足金融”为核心理念与服务宗旨,致力于将历经多年自身生产实践与检验的金融云架构、云服务、解决方案等开放给客户。为客户提供从云资源服务、云平台服务到云共享服务的多方位服务,以及包含行业云、私有云、混合云等在内的多场景服务。同时,结合银联在金融云上的长期研究、实践和技术积累,为客户提供了一站式金融云解决方案。","",0,0,"published",9999,0,"2021-07-16 10:51:48","2021-08-12 15:16:39","银联云" -238,167,"上海赛连信息科技有限公司","shanghaisailianxinxikejiyouxia","202107/1626405398175268111.png",NULL,NULL,"https://www.xylink.com/","400-900-3567",0,0,"小鱼易连是中国领先的高品质云视频专家,专注为政府、公检法司、教育、金融、医疗等行业及企业用户提供“云、端、AI、开发平台”四位一体的云视频解决方案,具有全网络环境下超高清、低延时、安全、可靠等特点。小鱼易连在中国网络会议市场的占有率排名保持首位(根据IDC《中国视频会议与协作市场追踪报告(2019年和2020年》),是中国云视频行业的领军企业。","",0,0,"published",9999,0,"2021-07-16 11:16:38","2021-08-12 15:17:01","赛连科技" -239,172,"北京分贝金服科技有限公司","beijingfenbeijinfukejiyouxiang","202107/1626421862531301568.jpg",NULL,NULL,"https://www.fenbeitong.com","400-817-6868",0,0,"分贝通是下一代企业支出管理平台,从商旅场景侧切入,沿着商旅、费控、到支付,实现了“费控+支付+场景”的三合一模式创新,用数字化的下一代支出管理方式替代企业旧有的报销模式,改变员工垫资、报销等痛点问题,赋能企业员工和C端支付一样的体验。现帮助海底捞、汤臣倍健、金山云、新氧、友臣、我爱我家等数字化标杆企业和数千家高成长企业升级企业支付体验,实现企业降本增效,赋能企业财务数字化转型升级。","",0,0,"published",9999,0,"2021-07-16 15:51:02","2021-08-12 15:17:12","分贝金服" -240,175,"中国移动通信集团福建有限公司","zhongguoyidongtongxinjituanfuj","202107/1626426380602395711.png",NULL,NULL,"暂无","10086",0,0,"中国移动通信集团福建有限公司成立于1999年10月9日;公司以“正德厚生、臻于至善”为核心价值观,以“创无限通信世界、做信息社会栋梁”为公司使命,公司的愿景是成为卓越品质的创造者,坚持以需求为导向,务求实效,积极推进“以移动信息化推动国民经济和社会信息化”的战略实践,不断致力于政府、企业等的信息化建设。","",0,0,"published",9999,0,"2021-07-16 17:06:20","2021-08-12 15:17:30","福建移动" -241,178,"山东恒远智能科技有限公司","shandonghengyuanzhinengkejiyou","202107/1626664842953209483.jpg",NULL,NULL,"www.sdever.com","4009001358",0,0,"山东恒远智能科技是一家专注于行业级工业数据采集与工业数字化系统产品研发的创新型工业互联网服务公司。2016年成立至今,迅速成长为国内首家装备制造业数字化工厂平台级综合解决方案与工业互联网产品服务商,国家级高新技术企业,拥有相关知识产权与专利80余项,连续4个全国工业互联网优秀应用案例,国家首批工信部全国优秀工业APP解决方案,2018年由《互联网周刊》与eNet研究院发布全国工业互联网服务商TOP100排名第45位。","",0,0,"published",9999,0,"2021-07-19 11:20:42","2021-08-12 15:18:16","恒远科技" -242,169,"上海驻云信息科技有限公司","shanghaizhuyunxinxikejiyouxian","202107/1626773828664018032.png",NULL,NULL,"https://www.cloudcare.cn/","400-882-3320",0,0,"上海驻云信息科技有限公司成立于2013年,是国内领先的企业级智能数据服务平台提供商,致力于将普惠的计算能力落实到每家企业,让智能数据驱动企业核心业务。 -历经多年云计算技术服务的积累和沉淀,驻云推出核心技术产品——DataFlux(云时代的系统可观测平台)。DataFlux 配置简单,开箱即用,支持全云平台、200+技术栈和按量付费,可帮助客户快速构建面向云原生时代的可观测能力,实现对云、云原生、应用及业务的统一监测需求。","",0,0,"published",9999,0,"2021-07-20 17:37:08","2021-08-12 15:18:28","驻云科技" -243,166,"天津市天河计算机技术有限公司","tianjinshitianhejisuanjijishuy","202107/1626829225937488468.png",NULL,NULL,"https://www.nscc-tj.cn/","022-65375583",0,0,"天津市天河计算机技术有限公司是国家超级计算天津中心的独资子公司。建有云计算中心、电子政务中心、国家发改委的大数据技术和应用工程实验室等。天津市天河计算机技术有限公司享有国家超级计算天津中心具有的超级计算、云计算和大数据平台和环境资源,能够提供同等的技术支持。","",0,0,"published",9999,0,"2021-07-21 09:00:25","2021-08-12 15:18:50","天河云" -244,170,"云从科技集团股份有限公司","yuncongkejijituangufenyouxiang","202108/1629946088018505977.png",NULL,NULL,"https://www.cloudwalk.com/","400-151-5992",0,0,"云从科技集团股份有限公司,简称“云从科技(Cloudwalk)”,总部位于广州,由周曦创立于2015年,孵化于中国科学院,是一家人工智能科技企业。业务涵盖智慧金融、智慧治理、智慧出行、智慧商业等领域,为客户提供个性化、场景化、行业化的智能服务。作为首个同时承建三大国家平台,并参与国家及行业标准制定的人工智能领军企业,也是国家新基建发展的中坚代表。云从多年深耕行业,先后布局智慧金融、智慧治理、智慧出行及智慧商业等四大业务领域,每天为全球3亿人次用户带来智慧、便捷和人性化的AI生活体验。","",0,0,"draft",9999,0,"2021-07-21 14:41:34","2021-08-26 10:48:08","云从科技" -245,171,"北京创世云科技股份有限公司","beijingchuangshiyunkejigufenyo","202107/1626850806520713062.png",NULL,NULL,"https://www.chuangcache.com","400-1199909",0,0,"北京创世云科技股份有限公司成立于2016年7月,以技术创新为核心,通过聚合互联网各类计算、存储、流量资源,打造内容分发、视频直播、实时音视频等产品,为电子商务、金融、教育、社交、游戏、娱乐等行业客户提供便捷、安全的网络服务。 - -公司拥有底蕴深厚的研发团队,并由行业资深人士组成的销售团队负责公司产品的深度落地,自创立以来,产品快速升级迭代,功能逐步完善丰富,营业收入保持快速增长,并且与腾讯、百度、爱奇艺、京东、苏宁等国内知名客户建立了良好的合作关系,产品和服务能力得到了广泛认可。","",0,0,"published",9999,0,"2021-07-21 15:00:06","2021-08-12 15:19:22","创世云" -246,164,"财智共享(北京)技术服务有限公司","caizhigongxiangbeijingjishufuw","202107/1626933948790199267.png",NULL,NULL,"http://www.smarts.online/","15234484445",0,0,"财智共享(北京)技术服务有限公司成立于2017年11月,是一家立足数字化审计业务服务,以解决审计过程中工作效率低下、业务流程不可控为前提,以前端及时应用为手段的科技公司,建立安全、合规、集约、高效的审计业务平台。公司在函证业务创新、业务与数据安全保障、区块链技术应用、人工智能技术应用等领域有大量的技术积累,在业务创新、科技应用两大方面保持业内领先优势。","",0,0,"published",9999,0,"2021-07-22 14:05:48","2021-08-12 15:19:39","财智共享" -247,173,"浙江大华技术股份有限公司","zhejiangdahuajishugufenyouxian","202107/1626953438987871632.png",NULL,NULL,"www.dahuatech.com","400-6728-166",0,0,"浙江大华技术股份有限公司,是全球领先*的以视频为核心的智慧物联解决方案提供商和运营服务商,现拥有18000多名员工,研发人员占比超50%,产品覆盖全球180个国家和地区。公司以技术创新为基础,围绕客户服务持续构建全链路的技术开放体系,全面拓展城市级、企业级市场的物联数据应用与深度,为城市、企业、家庭数字化转型提供一站式智慧物联服务与解决方案。依托对智慧物联的深入洞察与布局,公司持续探索新兴业务,延展了机器视觉、机器人、视讯协作、智慧无人机、智慧消防、汽车技术、智慧存储、智慧安检、智慧显控、智慧控制等创新业务。","",0,0,"draft",9999,0,"2021-07-22 19:06:47","2021-08-16 13:53:59","浙江大华" -248,179,"安超云软件有限公司","anchaoyunruanjianyouxiangongsi","202108/1627890522408818006.jpg",NULL,NULL,"","",0,0,"厦门安超云软件有限公司于2019年05月30日成立。","",0,0,"published",9999,0,"2021-08-02 15:48:42","2021-08-02 17:03:43","安超云" -249,180,"北京天融信科技股份有限公司","beijingtianrongxinkejigufenyou","202108/1627890857175605391.jpg",NULL,NULL,"","",0,0,"","",0,0,"published",9999,0,"2021-08-02 15:54:17","2021-08-12 15:20:12","天融信" -250,181,"深信服科技股份有限公司","shenxinfukejigufenyouxiangongs","202108/1627893737767416712.jpg",NULL,NULL,"","",0,0,"","",0,0,"published",9999,0,"2021-08-02 16:42:17","2021-08-12 15:20:23","深信服" -251,184,"新厂商名字","xinchangshangmingzi7999","202108/1628747999525866366.jpeg",NULL,NULL,"新厂商url","电话",0,0,"简介","",0,0,"published",9999,0,"2021-08-12 13:59:59","2021-08-12 14:01:11","" -252,183,"北京蔷薇灵动科技有限公司","beijingqiangweilingdongkejiyou","202108/1628751537014533981.png",NULL,NULL,"http://www.dynarose.com/","400-069-8066",0,0,"北京蔷薇灵动科技有限公司(简称蔷薇灵动)成立于2017年1月,专注于网络安全领域微隔离技术的前沿探索与研究,凭借专业的产品与服务为数据中心用户提供东西向(内部)流量解决方案,是国内微隔离市场的技术标准定义者和开创者。 -公司自主研发的蔷薇灵动蜂巢自适应微隔离安全平台是国内领先的与基础架构无关,能为混合云提供无差别微隔离(microsegmentation)服务的软件定义安全产品。","",0,0,"published",9999,0,"2021-08-12 14:58:57","2021-08-12 15:20:33","蔷薇灵动" -253,185,"中国联合网络通信有限公司软件研究院","zhongguolianhewangluotongxinyo","202108/1628769669404827124.png",NULL,NULL,"http://www.chinaunicom.com.cn/index.html","",0,0,"中国联通软件研究院是中国联通集团的直属单位,于2015年7月1日成立,负责中国联通集团的全域IT自主研发,专注于中国联通B域系统研发、生产运营、支撑响应等核心任务,本部位于北京,目前设哈尔滨、济南、广州、西安、南京五个分院,现有人员队伍规模近2000人。","",0,0,"draft",9999,0,"2021-08-12 20:01:09","2021-08-12 20:00:54","联通软研院" -254,186,"华为云计算技术有限公司",NULL,"202108/1628771901779490091.png",NULL,NULL,"https://www.huaweicloud.com/","",0,0,"华为云致力于提供稳定可靠、安全可信、可持续创新的云服务,赋能应用、使能数据、做智能世界的“黑土地”","",0,0,"published",9999,1,"2021-08-12 20:38:21","2021-08-12 20:39:00","华为云计算技术有限公司" -255,0,"新疆数字兵团信息产业发展有限责任公司",NULL,"20210826/FE5621DCB283BDB02C5073E7F366711A.png",NULL,NULL,NULL,NULL,0,0,NULL,NULL,0,0,"published",9999,0,"2021-08-26 17:04:29","2021-08-26 17:04:29","新疆数字兵团" -256,188,"深圳齐心好视通云计算有限公司","shenzhenqixinhaoshitongyunjisu","202108/1630293212082669836.png",NULL,NULL,"www.hst.com","400-9900-968",0,0,"深圳齐心好视通云计算有限公司是国内知名的云计算产品和服务提供商,隶属于齐心集团(股票代码:002301)。 公司拥有多项创新核心技术优势,是国内荣获工信部颁发的多方通信服务牌照的云计算服务商,先后获得“德勤高科技高成长中国50强”、连续七年荣获“中国优秀软件产品奖”、中国企业服务“商业创新企业奖”、“协同办公企业服务奖”等,并通过国家高新技术企业认证、双软认证、ISO9001认证、CMMI3认证,中国信息安全认证中心、国家信息安全工程技术研究中心网络安全技术中心、以及国家等保三级认证等。","",0,0,"published",9999,0,"2021-08-30 11:13:32","2021-08-30 11:12:33","" -257,0,"杭州安恒信息技术股份有限公司 ",NULL,"20210830/35D075DCC941CF201844A951476D12A3.jpeg",NULL,NULL,NULL,NULL,0,0,"杭州安恒信息技术股份有限公司,于2007年05月15日成立。法定代表人吴卓群, 是由范渊先生创办的国家级高新技术企业,网络与信息安全产品和服务提供商。国内总部设在杭州及北京,并在上海、南京、广州、深圳、成都、重庆、济南、西安等多个城市设有分支机构。",NULL,0,0,"draft",9999,0,"2021-08-30 17:22:38","2021-08-30 17:30:58","安恒信息" -258,189,"杭州安恒信息技术股份有限公司","hangzhouanhengxinxijishugufeny","202108/1630316311305420804.jpeg",NULL,NULL,"https://www.dbappsecurity.com.cn/","4006059110",0,0,"杭州安恒信息技术股份有限公司(简称:安恒信息)成立于2007年,科创板股票代码:688023。 -自成立以来一直专注于网络信息安全领域,公司秉承“助力安全中国,助推数字经济”的企业使命,以“数字经济的安全基石”为企业定位,将“诚信正直,成就客户,责任至上,开放创新,以人为本,共同成长”作为企业的价值观,致力于成为一家具有优秀企业文化和社会责任感的新时代网络信息安全产品和服务提供商。","",0,0,"published",9999,0,"2021-08-30 17:38:31","2021-08-30 17:46:27","" -259,191,"上海数讯信息技术有限公司","shanghaishuxunxinxijishuyouxia","202109/1632289659034574249.png",NULL,NULL,"https://www.shuxun.net/","4000009890",0,0,"数讯云服务涵盖:托管私有云,支持混合部署,多云部署,上海北京多点灾备支持,满足企业多层次配置需求。 -数讯为用户提供一站式整体通信解决方案,包括数据中心,云服务,网络,线路,管理服务,增值服务等。","",0,0,"published",9999,0,"2021-09-22 13:47:39","2021-09-22 13:47:44","" -260,0,"网络通信与安全紫金山实验室","wangluotongxinyuanquanzijinsha","20210929/E6DF7493C82121DC947394A02644FC3B.png",NULL,NULL,NULL,NULL,0,0,"网络通信与安全紫金山实验室",NULL,0,0,"published",9999,0,"2021-09-29 17:29:49","2021-09-29 17:58:04","紫金山实验室" -261,0,"三一集团有限公司","sanyijituanyouxiangongsi","20210929/35EDD6A6C3BFA4817443E8185E4F3FE6.jpg",NULL,NULL,NULL,NULL,0,0,"三一集团有限公司",NULL,0,0,"published",9999,0,"2021-09-29 17:34:39","2021-09-29 17:34:39","三一集团" -262,0,"广州市品高软件股份有限公司","guangzhoushipingaoruanjiangufe","20210929/39A5EEB6042D21B77ACA438177A175FB.png",NULL,NULL,NULL,NULL,0,0,"广州市品高软件股份有限公司",NULL,0,0,"published",9999,0,"2021-09-29 17:35:22","2021-09-29 17:35:22","品高软件" -263,0,"济南浪潮数据技术有限公司","jinanlangchaoshujujishuyouxian","20210929/51DBA290D292B8E4CE38679C724546A1.png",NULL,NULL,NULL,NULL,0,0,"济南浪潮数据技术有限公司",NULL,0,0,"published",9999,0,"2021-09-29 17:37:04","2021-09-29 17:37:04","浪潮数据" -264,0,"新华三技术有限公司","xinhuasanjishuyouxiangongsi",NULL,NULL,NULL,NULL,NULL,0,0,"新华三技术有限公司",NULL,0,0,"published",9999,0,"2021-09-29 17:39:01","2021-09-29 17:39:01","新华三" -265,0,"苏州博纳讯动软件有限公司","suzhoubonaxundongruanjianyouxi",NULL,NULL,NULL,NULL,NULL,0,0,"苏州博纳讯动软件有限公司",NULL,0,0,"published",9999,0,"2021-09-29 17:39:48","2021-09-29 17:39:48","博云" -266,0,"中国工商银行股份有限公司","zhongguogonghsangyinhanggufeny","20210929/B842893114243723246FBA225D795045.png",NULL,NULL,NULL,NULL,0,0,"中国工商银行股份有限公司",NULL,0,0,"published",9999,0,"2021-09-29 19:13:12","2021-09-29 19:13:12","工商银行" -267,193,"三一集团有限公司","sanyijituanyouxiangongsi0207","202109/1632920207559109535.jpg",NULL,NULL,"https://www.sanygroup.com","",0,0,"三一主营业务是以“工程”为主题的装备制造业,主导产品为 : 混凝土机械、挖掘机械、起重机械、筑路机械、桩工机械、风电设备、港口机械、石油装备、煤炭装备、装配式建筑 PC 机械等全系列产品。目前,三一混凝土机械稳居世界第一品牌,大吨位起重机械、履带起重机械、桩工机械、掘进机械、港口机械稳居中国第一。","",0,0,"published",9999,0,"2021-09-29 20:56:47","2021-10-08 15:12:46","三一集团" diff --git a/tests/import/manu.json b/tests/import/manu.json deleted file mode 100644 index ad850131..00000000 --- a/tests/import/manu.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "mapping": { - "name": "name", - "short_name": "smalltitle", - "rank": "priority" - }, - "option": { - "strict": true - } -} diff --git a/tests/imports/order.imp.json b/tests/imports/order.imp.json deleted file mode 100644 index 1fa5186b..00000000 --- a/tests/imports/order.imp.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "title": "导入订单数据", - "process": "scripts.rules.ImportData", - "columns": [ - { - "label": "订单号", - "name": "order_sn", - "match": ["订单号", "订单", "order_sn", "id"], - "rules": ["scripts.rules.order_sn"], - "primary": true - }, - { - "label": "用户", - "name": "user.name", - "match": ["用户名称", "姓名", "user_id"], - "rules": ["scripts.rules.FmtUser"], - "nullable": true - }, - { - "label": "性别", - "name": "user.sex", - "match": ["性别", "gender", "sex"], - "rules": [], - "nullable": true - }, - { - "label": "年龄", - "name": "user.age", - "match": ["年龄", "age"], - "rules": [], - "nullable": true - }, - { - "label": "手机号", - "name": "mobile", - "match": ["手机号", "手机", "mobile"], - "rules": ["scripts.rules.mobile"] - }, - { - "label": "商品", - "name": "skus[*].name", - "match": ["商品", "商品名称", "goods", "skus", "sku_id", "goods_id"], - "rules": ["scripts.rules.FmtGoods"] - }, - { - "label": "数量", - "name": "skus[*].amount", - "match": ["数量", "amount"], - "rules": ["scripts.rules.FmtGoods"] - }, - { - "label": "单价", - "name": "skus[*].price", - "match": ["单价", "price", "价格"], - "rules": ["scripts.rules.FmtGoods"] - }, - { - "label": "总价", - "name": "total", - "match": ["总价", "total", "total_price"], - "rules": ["scripts.rules.FmtGoods"] - }, - { - "label": "备注", - "name": "remark", - "match": [] - } - ], - "option": { - "autoMatching": true, - "chunkSize": 3, - "logging": "error", - "mappingPreview": "auto", - "dataPreview": "auto", - "templateLink": "/somelinke/download/xxx" - }, - "rules": { - "scripts.rules.order_sn": "订单号验证", - "scripts.rules.mobile": "手机号验证", - "scripts.rules.FmtUser": "用户数据匹配验证", - "scripts.rules.FmtGoods": "商品数据匹配验证" - } -} diff --git a/tests/langs/zh-cn/flows/hello.flow.yml b/tests/langs/zh-cn/flows/hello.flow.yml deleted file mode 100644 index e15a69fa..00000000 --- a/tests/langs/zh-cn/flows/hello.flow.yml +++ /dev/null @@ -1 +0,0 @@ -Latest: 最新信息 diff --git a/tests/langs/zh-cn/global.yml b/tests/langs/zh-cn/global.yml deleted file mode 100644 index 61133bfe..00000000 --- a/tests/langs/zh-cn/global.yml +++ /dev/null @@ -1,7 +0,0 @@ -Demo: 演示 -Phone: 电话号码 -ZipCode: 邮政编码 -Uptime: 更新时间 -Action: 动作 -Yao: YAO -Xiang: 象传 diff --git a/tests/langs/zh-cn/models/demo.mod.yml b/tests/langs/zh-cn/models/demo.mod.yml deleted file mode 100644 index 8eb7cc67..00000000 --- a/tests/langs/zh-cn/models/demo.mod.yml +++ /dev/null @@ -1 +0,0 @@ -SN: 编码 diff --git a/tests/langs/zh-cn/tables/demo.yml b/tests/langs/zh-cn/tables/demo.yml deleted file mode 100644 index 17bcd44b..00000000 --- a/tests/langs/zh-cn/tables/demo.yml +++ /dev/null @@ -1,10 +0,0 @@ -ID: 序号 -SN: 编码 -Name: 姓名 -Kind: 类型 -Cat: 猫 -Dog: 狗 -Description: 介绍 -Keywords: 关键词 -Please input the SN: 请输入编码 -type the keywords...: 关键词... diff --git a/tests/langs/zh-hk/charts/lang.yml b/tests/langs/zh-hk/charts/lang.yml deleted file mode 100644 index 0f0a4714..00000000 --- a/tests/langs/zh-hk/charts/lang.yml +++ /dev/null @@ -1,10 +0,0 @@ -Start: 開始時間 -End: 結束時間 -City: 城市 -Please select a start time: 請選擇開始時間 -Please select an end time: 請選擇結束時間 -Industry: 行業 -Billing: 計費 -Merge Reulst: 合併結果 -Parameter: 參數 -Merge: 合併 diff --git a/tests/langs/zh-hk/flows/hello.flow.yml b/tests/langs/zh-hk/flows/hello.flow.yml deleted file mode 100644 index e15a69fa..00000000 --- a/tests/langs/zh-hk/flows/hello.flow.yml +++ /dev/null @@ -1 +0,0 @@ -Latest: 最新信息 diff --git a/tests/langs/zh-hk/global.yml b/tests/langs/zh-hk/global.yml deleted file mode 100644 index c820c37e..00000000 --- a/tests/langs/zh-hk/global.yml +++ /dev/null @@ -1,7 +0,0 @@ -Demo: 演示 -Phone: 電話號碼 -ZipCode: 郵政編碼 -Uptime: 更新時間 -Action: 動作 -Yao: YAO -Xiang: 像傳 diff --git a/tests/langs/zh-hk/models/demo.mod.yml b/tests/langs/zh-hk/models/demo.mod.yml deleted file mode 100644 index 30134633..00000000 --- a/tests/langs/zh-hk/models/demo.mod.yml +++ /dev/null @@ -1 +0,0 @@ -SN: 編碼 diff --git a/tests/langs/zh-hk/pages/lang.yml b/tests/langs/zh-hk/pages/lang.yml deleted file mode 100644 index 0f0a4714..00000000 --- a/tests/langs/zh-hk/pages/lang.yml +++ /dev/null @@ -1,10 +0,0 @@ -Start: 開始時間 -End: 結束時間 -City: 城市 -Please select a start time: 請選擇開始時間 -Please select an end time: 請選擇結束時間 -Industry: 行業 -Billing: 計費 -Merge Reulst: 合併結果 -Parameter: 參數 -Merge: 合併 diff --git a/tests/langs/zh-hk/tables/demo.yml b/tests/langs/zh-hk/tables/demo.yml deleted file mode 100644 index 42d52b75..00000000 --- a/tests/langs/zh-hk/tables/demo.yml +++ /dev/null @@ -1,10 +0,0 @@ -ID: 序號 -SN: 編碼 -Name: 姓名 -Kind: 類型 -Description: 介绍 -Keywords: 關鍵詞 -Cat: 貓 -Dog: 狗 -Please input the SN: 請輸入編碼 -type the keywords...: 關鍵詞... diff --git a/tests/libs/action.json b/tests/libs/action.json deleted file mode 100644 index 0e9348ad..00000000 --- a/tests/libs/action.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "保存": { - "type": "button", - "props": { - "label": "保存" - } - }, - "删除": { - "type": "button", - "props": { - "label": "保存" - } - } -} diff --git a/tests/libs/apis/table.json b/tests/libs/apis/table.json deleted file mode 100644 index 3cf780c3..00000000 --- a/tests/libs/apis/table.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Search": { - "default": [null, null, "{{$in.0}}"] - } -} diff --git a/tests/libs/assign.json b/tests/libs/assign.json deleted file mode 100644 index 81e0fcfb..00000000 --- a/tests/libs/assign.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "保存": { - "type": "button", - "props": { - "label": "保存", - "api": "/api/xiang/workflow/assign/save" - } - }, - "提交": { - "type": "button", - "props": { - "label": "提交", - "api": "/api/xiang/workflow/assign/submit" - } - }, - "同意": { - "type": "button", - "props": { - "label": "同意", - "api": "/api/xiang/workflow/assign/resolve" - } - }, - "驳回并重填": { - "type": "button", - "props": { - "label": "驳回并重填", - "api": "/api/xiang/workflow/assign/reject/reset" - } - }, - "驳回并关闭": { - "type": "button", - "props": { - "label": "驳回并关闭", - "api": "/api/xiang/workflow/assign/reject/reset" - } - }, - "审批同意": { - "type": "button", - "props": { - "label": "审批同意", - "api": "/api/xiang/workflow/assign/terminal" - } - }, - "关闭": { - "type": "button", - "props": { - "label": "关闭", - "api": "/api/xiang/workflow/terminal" - } - } -} diff --git a/tests/libs/column.json b/tests/libs/column.json deleted file mode 100644 index 4d34bcbc..00000000 --- a/tests/libs/column.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "Label": { - "__comment": "column.Lable('ID',':id')", - "label": "{{$in.0}}", - "view": { - "props": { - "value": "{{$in.1}}" - } - } - }, - "Image": { - "__comment": "column.Image('图标',':icon', 40)", - "label": "{{$in.0}}", - "view": { - "type": "image", - "props": { - "value": "{{$in.1}}", - "width": "{{$in.2}}", - "height": "{{$in.2}}" - } - }, - "edit": { - "type": "upload", - "props": { - "value": "{{$in.1}}", - "filetype": "image", - "multiple": false, - "maxCount": 1 - } - } - }, - "创建时间": { - "__comment": "column.创建时间", - "label": "创建时间", - "view": { - "name": "label", - "props": { - "value": ":created_at", - "format": "YYYY年MM月DD日 @hh:mm:ss" - } - } - }, - "商务负责人": { - "label": "商务负责人", - "view": { - "name": "label", - "props": { - "value": ":kind.name" - } - }, - "edit": { - "type": "select", - "props": { - "value": ":kind.id", - "tree": true, - "searchable": true, - "remote": { - "api": "/api/kind/tree", - "query": { - "select": ["id", "name"], - "keyword": "where_name_like" - } - } - } - } - } -} diff --git a/tests/libs/filter.json b/tests/libs/filter.json deleted file mode 100644 index f8717d55..00000000 --- a/tests/libs/filter.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "关键词": { - "__comment": "filter.关键词('where.name.match')", - "label": "关键词", - "bind": "{{$in.0}}", - "input": { - "type": "input", - "props": { - "placeholder": "请输入关键词" - } - } - }, - "排序": { - "__comment": "filter.排序()", - "label": "排序方式", - "bind": "order", - "input": { - "type": "select", - "props": { - "placeholder": "排序方式", - "options": [ - { "label": "手动顺序", "value": "rank" }, - { "label": "最近更新", "value": "updated_at.desc" }, - { "label": "最近创建", "value": "created_at.desc" } - ] - } - } - }, - "客户状态": { - "__comment": "filter.客户状态()", - "label": "状态", - "bind": "where.status.in", - "input": { - "type": "select", - "props": { - "placeholder": "选择厂商状态", - "mode": "multiple", - "options": [ - { "value": "已注册" }, - { "value": "待收录" }, - { "value": "待审核" }, - { "value": "已禁用" } - ] - } - } - } -} diff --git a/tests/libs/pages/static.json b/tests/libs/pages/static.json deleted file mode 100644 index 65092d2e..00000000 --- a/tests/libs/pages/static.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "Page": { - "primary": "{{$in.0}}", - "layout": { - "filters": [ - { "name": "开始时间", "width": 6 }, - { "name": "结束时间", "width": 6 } - ], - "charts": [ - { "type": "line", "props": {} }, - { "type": "bar", "props": {} } - ] - }, - "actions": {} - } -} diff --git a/tests/libs/render.json b/tests/libs/render.json deleted file mode 100644 index f2261d0e..00000000 --- a/tests/libs/render.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Image": { - "type": "image", - "props": { - "value": "{{$in.0}}", - "width": "{{$in.1}}", - "height": "{{$in.2}}" - } - } -} diff --git a/tests/models/address.json b/tests/models/address.json deleted file mode 100644 index fde1bd5b..00000000 --- a/tests/models/address.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "name": "地址", - "table": { - "name": "address", - "comment": "地址表", - "engine": "InnoDB" - }, - "columns": [ - { "name": "id", "type": "ID" }, - { - "label": "用户", - "name": "user_id", - "type": "bigInteger", - "comment": "所属用户", - "index": true - }, - { - "label": "省份", - "name": "province", - "type": "string", - "length": 200, - "comment": "省份", - "index": true - }, - { - "label": "城市", - "name": "city", - "type": "string", - "length": 200, - "comment": "城市", - "index": true - }, - { - "label": "地址", - "name": "location", - "type": "string", - "length": 400, - "comment": "详细地址", - "index": true - }, - { - "label": "状态", - "name": "status", - "type": "enum", - "default": "enabled", - "option": ["enabled", "disabled"], - "comment": "状态 enabled 开启, disabled 关闭", - "index": true - } - ], - "relations": {}, - "option": { "timestamps": true, "soft_deletes": true }, - "values": [ - { - "user_id": 1, - "province": "北京市", - "city": "丰台区", - "location": "银海星月9号楼9单元9层1024室" - }, - { - "user_id": 1, - "province": "北京市", - "city": "丰台区", - "location": "北京国家数字出版基地A103" - }, - { - "user_id": 2, - "province": "北京市", - "city": "海淀区", - "location": "华严北里7号楼7单元7层2048室" - }, - { - "user_id": 3, - "province": "山东省", - "city": "威海市", - "location": "威海市6号楼6单元6层1056室" - } - ] -} diff --git a/tests/models/customer.mod.json b/tests/models/customer.mod.json deleted file mode 100644 index 8ccca686..00000000 --- a/tests/models/customer.mod.json +++ /dev/null @@ -1,230 +0,0 @@ -{ - "name": "客户", - "table": { - "name": "customer", - "comment": "客户表", - "engine": "InnoDB" - }, - "columns": [ - { - "label": "ID", - "name": "id", - "type": "ID", - "comment": "ID" - }, - { - "label": "来源", - "name": "channel_id", - "type": "bigInteger", - "comment": "客户来源" - }, - { - "label": "公司名称", - "name": "name", - "type": "string", - "length": 200, - "comment": "公司全称", - "unique": true - }, - { - "label": "公司简称", - "name": "short_name", - "type": "string", - "length": 200, - "comment": "简称", - "nullable": true, - "index": true - }, - { - "name": "credit_no", - "type": "string", - "length": 200, - "comment": "统一社会信用代码", - "nullable": true, - "unique": true - }, - { - "name": "oper_name", - "type": "string", - "length": 50, - "comment": "法人代表", - "nullable": true, - "index": true - }, - { - "label": "注册资本(万元)", - "name": "reg_capi", - "type": "integer", - "comment": "注册资本(万元)", - "nullable": true, - "index": true - }, - { - "label": "注册时间", - "name": "opened_at", - "type": "date", - "comment": "注册时间", - "nullable": true, - "index": true - }, - { - "label": "省份", - "name": "province", - "type": "string", - "length": 50, - "comment": "总部所在省份", - "nullable": true, - "index": true - }, - { - "label": "城市", - "name": "city", - "type": "string", - "length": 100, - "comment": "总部所在城市", - "nullable": true, - "index": true - }, - { - "label": "地址", - "name": "address", - "type": "string", - "length": 255, - "comment": "总部地址", - "nullable": true, - "index": true - }, - { - "label": "经度", - "name": "lng", - "type": "unsignedDecimal", - "precision": 12, - "scale": 6, - "comment": "经度", - "nullable": true, - "index": true - }, - { - "label": "纬度", - "name": "lat", - "type": "unsignedDecimal", - "comment": "纬度", - "precision": 12, - "scale": 6, - "nullable": true, - "index": true - }, - { - "label": "经营状态", - "name": "company_status", - "type": "string", - "length": 255, - "comment": "经营状态", - "nullable": true, - "index": true - }, - { - "label": "LOGO", - "name": "logo", - "type": "json", - "comment": "LOGO", - "nullable": true - }, - { - "label": "简介", - "name": "summary", - "type": "string", - "length": 600, - "comment": "简介", - "nullable": true, - "index": true - }, - { - "label": "官网", - "name": "link", - "type": "string", - "length": 200, - "comment": "官网地址", - "nullable": true - }, - { - "label": "联系人", - "name": "contact_name", - "type": "string", - "length": 200, - "comment": "联系人姓名", - "nullable": true, - "index": true - }, - { - "label": "联系人职务", - "name": "contact_title", - "type": "string", - "length": 200, - "comment": "联系人职务", - "nullable": true, - "index": true - }, - { - "label": "联系电话", - "name": "contact_mobile", - "type": "string", - "comment": "联系电话", - "nullable": true, - "index": true - }, - { - "label": "联系微信", - "name": "contact_wechat", - "type": "string", - "comment": "联系微信", - "nullable": true, - "index": true - }, - { - "label": "介绍", - "name": "desc", - "type": "text", - "comment": "介绍", - "nullable": true - }, - { - "label": "领域", - "name": "fields", - "type": "json", - "comment": "服务领域(多选)", - "nullable": true, - "index": true - }, - { - "label": "行业", - "name": "industries", - "type": "json", - "comment": "行业覆盖(多选)", - "nullable": true, - "index": true - }, - { - "label": "标签", - "name": "tags", - "type": "json", - "comment": "标签(多选)", - "nullable": true, - "index": true - }, - { - "label": "关系", - "name": "relation", - "type": "enum", - "default": "正在接洽", - "option": ["已有合作", "正在接洽", "潜在客户"], - "comment": "合作关系: 已有合作, 正在接洽, 潜在客户", - "index": true - } - ], - "relations": {}, - "option": { - "timestamps": true, - "soft_deletes": true - } -} diff --git a/tests/models/demo.mod.json b/tests/models/demo.mod.json deleted file mode 100644 index e827cdcd..00000000 --- a/tests/models/demo.mod.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "::Demo", - "connector": "trace", - "table": { "name": "user_trace", "comment": "::Demo" }, - "columns": [ - { "label": "ID", "name": "id", "type": "ID" }, - { - "label": "::SN", - "name": "user_sn", - "type": "string", - "length": 42, - "index": true - }, - { - "label": "::Uptime", - "name": "uptime", - "type": "datetime", - "index": true - }, - { - "label": "::Action", - "name": "action", - "type": "enum", - "option": ["进入", "离开"], - "index": true - } - ], - "indexes": [ - { - "name": "user_sn_action_unique", - "type": "unique", - "columns": ["user_sn", "action"] - } - ] -} diff --git a/tests/models/foo/bar.mod.json b/tests/models/foo/bar.mod.json deleted file mode 100644 index 9ee753fe..00000000 --- a/tests/models/foo/bar.mod.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "用户角色", - "table": { - "name": "foo_bar", - "comment": "用户角色关系表", - "engine": "InnoDB" - }, - "columns": [ - { "name": "id", "type": "ID" }, - { - "label": "用户ID", - "name": "user_id", - "type": "bigInteger", - "comment": "所属用户", - "index": true - }, - { - "label": "角色ID", - "name": "role_id", - "type": "bigInteger", - "comment": "角色ID", - "index": true - }, - { - "label": "状态", - "name": "status", - "type": "enum", - "default": "enabled", - "option": ["enabled", "disabled"], - "comment": "状态 enabled 开启, disabled 关闭", - "index": true - } - ], - "relations": {}, - "option": { "timestamps": true, "soft_deletes": true }, - "values": [ - { "user_id": 1, "role_id": 1 }, - { "user_id": 1, "role_id": 2 }, - { "user_id": 2, "role_id": 3 }, - { "user_id": 2, "role_id": 4 }, - { "user_id": 3, "role_id": 1 } - ] -} diff --git a/tests/models/friends.json b/tests/models/friends.json deleted file mode 100644 index b56fd5ca..00000000 --- a/tests/models/friends.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "好友", - "table": { - "name": "friends", - "comment": "好友关系表", - "engine": "InnoDB" - }, - "columns": [ - { "name": "id", "type": "ID" }, - { - "label": "用户ID", - "name": "user_id", - "type": "bigInteger", - "comment": "所属用户", - "index": true - }, - { - "label": "好友用户ID", - "name": "friend_id", - "type": "bigInteger", - "comment": "好友用户ID", - "index": true - }, - { - "label": "类型", - "name": "type", - "type": "enum", - "default": "monther", - "option": ["monther", "father", "friend"], - "comment": "类型 monther 母亲, father 父亲, friend 朋友", - "index": true - }, - { - "label": "状态", - "name": "status", - "type": "enum", - "default": "enabled", - "option": ["enabled", "disabled"], - "comment": "状态 enabled 开启, disabled 关闭", - "index": true - } - ], - "relations": {}, - "option": { "timestamps": true, "soft_deletes": true }, - "values": [ - { - "user_id": 1, - "friend_id": 2, - "type": "monther" - }, - { - "user_id": 1, - "friend_id": 3, - "type": "father" - }, - { - "user_id": 2, - "friend_id": 3, - "type": "friend" - } - ] -} diff --git a/tests/models/kind.json b/tests/models/kind.json deleted file mode 100644 index 53f53de9..00000000 --- a/tests/models/kind.json +++ /dev/null @@ -1,589 +0,0 @@ -{ - "name": "服务类型", - "table": { - "name": "kind", - "comment": "服务类型", - "engine": "InnoDB" - }, - "columns": [ - { "name": "id", "type": "ID" }, - { - "name": "category_id", - "type": "integer", - "length": 128, - "comment": "gw_category ID(废弃)", - "nullable": true, - "index": true - }, - { - "name": "type_id", - "type": "integer", - "length": 128, - "comment": "gw_type ID(废弃)", - "nullable": true, - "index": true - }, - { - "name": "name", - "type": "string", - "comment": "名称", - "index": true - }, - { - "name": "parent", - "type": "bigInteger", - "length": 128, - "comment": "父类型ID", - "nullable": false, - "default": 0, - "index": true - }, - { - "name": "status", - "type": "enum", - "option": ["enabled", "disabled"], - "comment": "状态 enabled 开启, disabled 关闭", - "default": "enabled", - "index": true - }, - { - "name": "rank", - "type": "bigInteger", - "length": 128, - "comment": "排序", - "nullable": false, - "default": 9999, - "index": true - } - ], - "relations": { - "parent": { - "type": "hasOne", - "key": "parent", - "model": "Kind", - "foreign": "id" - }, - "children": { - "type": "hasMany", - "key": "id", - "model": "Kind", - "foreign": "parent" - }, - "nodes": { - "type": "treeNodes", - "key": "id", - "model": "Kind", - "parent": "parent", - "max_level": 256 - } - }, - "option": { "timestamps": true, "soft_deletes": true }, - "values": [ - { - "id": 1, - "name": "云服务", - "category_id": null, - "type_id": null, - "rank": 1, - "parent": 0 - }, - - { - "id": 2, - "name": "基础服务", - "category_id": null, - "type_id": null, - "rank": 1, - "parent": 1 - }, - { - "id": 3, - "name": "云主机", - "category_id": null, - "type_id": 4, - "rank": 1, - "parent": 2 - }, - { - "id": 4, - "name": "对象存储", - "category_id": null, - "type_id": 5, - "rank": 2, - "parent": 2 - }, - { - "id": 5, - "name": "云数据库", - "category_id": null, - "type_id": 6, - "rank": 3, - "parent": 2 - }, - { - "id": 6, - "name": "块存储", - "category_id": null, - "type_id": 7, - "rank": 4, - "parent": 2 - }, - { - "id": 7, - "name": "应用托管容器", - "category_id": null, - "type_id": 8, - "rank": 5, - "parent": 2 - }, - { - "id": 8, - "name": "云缓存", - "category_id": null, - "type_id": 9, - "rank": 6, - "parent": 2 - }, - { - "id": 9, - "name": "本地负载均衡", - "category_id": null, - "type_id": 10, - "rank": 7, - "parent": 2 - }, - { - "id": 10, - "name": "全局负载均衡", - "category_id": null, - "type_id": 13, - "rank": 8, - "parent": 2 - }, - { - "id": 11, - "name": "云分发", - "category_id": null, - "type_id": 11, - "rank": 9, - "parent": 2 - }, - { - "id": 12, - "name": "企业级SaaS", - "category_id": null, - "type_id": 12, - "rank": 10, - "parent": 2 - }, - { - "id": 13, - "name": "云桌面", - "category_id": null, - "type_id": 14, - "rank": 11, - "parent": 2 - }, - { - "id": 14, - "name": "云备份", - "category_id": null, - "type_id": 17, - "rank": 12, - "parent": 2 - }, - { - "id": 15, - "name": "GPU云主机", - "category_id": null, - "type_id": 18, - "rank": 13, - "parent": 2 - }, - { - "id": 16, - "name": "物理云主机", - "category_id": null, - "type_id": 20, - "rank": 14, - "parent": 2 - }, - { - "id": 17, - "name": "智能云", - "category_id": 41, - "type_id": null, - "rank": 2, - "parent": 2 - }, - - { - "id": 18, - "name": "软件和开发", - "category_id": null, - "type_id": null, - "rank": 2, - "parent": 0 - }, - { - "id": 19, - "name": "虚拟化及管理", - "category_id": 59, - "type_id": null, - "rank": 1, - "parent": 18 - }, - { - "id": 20, - "name": "容器解决方案", - "category_id": 65, - "type_id": null, - "rank": 2, - "parent": 18 - }, - { - "id": 21, - "name": "微服务解决方案", - "category_id": 76, - "type_id": null, - "rank": 3, - "parent": 18 - }, - { - "id": 22, - "name": "serverless解决方案", - "category_id": 82, - "type_id": null, - "rank": 4, - "parent": 18 - }, - { - "id": 23, - "name": "云管理和云运营", - "category_id": null, - "type_id": null, - "rank": 3, - "parent": 0 - }, - { - "id": 24, - "name": "混合云", - "category_id": null, - "type_id": null, - "rank": 1, - "parent": 23 - }, - { - "id": 25, - "name": "混合云解决方案", - "category_id": 88, - "type_id": null, - "rank": 1, - "parent": 24 - }, - { - "id": 26, - "name": "混合云安全", - "category_id": 671, - "type_id": null, - "rank": 2, - "parent": 24 - }, - { - "id": 27, - "name": "多云管理", - "category_id": 94, - "type_id": null, - "rank": 2, - "parent": 23 - }, - { - "id": 28, - "name": "金牌运维", - "category_id": 120, - "type_id": null, - "rank": 3, - "parent": 23 - }, - { - "id": 29, - "name": "研发运营一体化", - "category_id": 443, - "type_id": null, - "rank": 4, - "parent": 23 - }, - { - "id": 30, - "name": "MSP", - "category_id": 112, - "type_id": null, - "rank": 5, - "parent": 23 - }, - { - "id": 31, - "name": "安全与保险", - "category_id": null, - "type_id": null, - "rank": 4, - "parent": 0 - }, - { - "id": 32, - "name": "风险管理", - "category_id": 141, - "type_id": null, - "rank": 1, - "parent": 31 - }, - { - "id": 33, - "name": "云服务用户数据保护", - "category_id": 152, - "type_id": null, - "rank": 2, - "parent": 31 - }, - { - "id": 34, - "name": "业务风控", - "category_id": null, - "type_id": null, - "rank": 3, - "parent": 31 - }, - { - "id": 35, - "name": "内容安全", - "category_id": 159, - "type_id": null, - "rank": 1, - "parent": 34 - }, - { - "id": 36, - "name": "反交易欺诈", - "category_id": 164, - "type_id": null, - "rank": 2, - "parent": 34 - }, - { - "id": 37, - "name": "反信贷欺诈", - "category_id": 165, - "type_id": null, - "rank": 3, - "parent": 34 - }, - { - "id": 38, - "name": "反营销欺诈", - "category_id": 166, - "type_id": null, - "rank": 4, - "parent": 34 - }, - { - "id": 39, - "name": "反钓鱼欺诈", - "category_id": 167, - "type_id": null, - "rank": 5, - "parent": 34 - }, - { - "id": 40, - "name": "云主机安全", - "category_id": 184, - "type_id": null, - "rank": 4, - "parent": 31 - }, - { - "id": 41, - "name": "态势感知", - "category_id": 190, - "type_id": null, - "rank": 5, - "parent": 31 - }, - { - "id": 42, - "name": "云保险", - "category_id": 272, - "type_id": null, - "rank": 6, - "parent": 31 - }, - { - "id": 43, - "name": "云网&云边", - "category_id": null, - "type_id": null, - "rank": 5, - "parent": 0 - }, - { - "id": 44, - "name": "云平台网络能力", - "category_id": 102, - "type_id": null, - "rank": 1, - "parent": 43 - }, - { - "id": 45, - "name": "SD-WAN", - "category_id": 106, - "type_id": null, - "rank": 2, - "parent": 43 - }, - { - "id": 46, - "name": "物联网", - "category_id": 234, - "type_id": null, - "rank": 3, - "parent": 43 - }, - { - "id": 47, - "name": "行业云", - "category_id": null, - "type_id": null, - "rank": 6, - "parent": 0 - }, - { - "id": 48, - "name": "政务", - "category_id": null, - "type_id": null, - "rank": 1, - "parent": 47 - }, - { - "id": 49, - "name": "政务云综合水平评估", - "category_id": 215, - "type_id": null, - "rank": 1, - "parent": 48 - }, - { - "id": 50, - "name": "可信政务云评估", - "category_id": 216, - "type_id": null, - "rank": 2, - "parent": 48 - }, - { - "id": 51, - "name": "金融", - "category_id": 225, - "type_id": null, - "rank": 2, - "parent": 47 - }, - { - "id": 52, - "name": "开源治理", - "category_id": null, - "type_id": null, - "rank": 7, - "parent": 0 - }, - { - "id": 53, - "name": "面向开源用户企业", - "category_id": 196, - "type_id": null, - "rank": 1, - "parent": 52 - }, - { - "id": 54, - "name": "面向自发开源企业", - "category_id": 202, - "type_id": null, - "rank": 2, - "parent": 52 - }, - { - "id": 55, - "name": "开源项目评估", - "category_id": 711, - "type_id": null, - "rank": 3, - "parent": 52 - }, - { - "id": 56, - "name": "开源工具评估", - "category_id": 720, - "type_id": null, - "rank": 4, - "parent": 52 - }, - { - "id": 57, - "name": "检测平台", - "category_id": null, - "type_id": null, - "rank": 8, - "parent": 0 - }, - { - "id": 58, - "name": "云主机分级", - "category_id": 371, - "type_id": null, - "rank": 1, - "parent": 57 - }, - { - "id": 59, - "name": "监管支撑", - "category_id": null, - "type_id": null, - "rank": 9, - "parent": 0 - }, - { - "id": 60, - "name": "企业上云效果成熟度", - "category_id": 250, - "type_id": null, - "rank": 1, - "parent": 59 - }, - { - "id": 61, - "name": "综合信用评估", - "category_id": null, - "type_id": null, - "rank": 2, - "parent": 59 - }, - { - "id": 62, - "name": "云服务企业", - "category_id": 261, - "type_id": null, - "rank": 1, - "parent": 61 - }, - { - "id": 63, - "name": "CDN服务企业", - "category_id": 271, - "type_id": null, - "rank": 2, - "parent": 61 - } - ] -} diff --git a/tests/models/manu.json b/tests/models/manu.json deleted file mode 100644 index 354a8986..00000000 --- a/tests/models/manu.json +++ /dev/null @@ -1,125 +0,0 @@ -{ - "name": "厂商", - "table": { - "name": "manu", - "comment": "厂商表", - "engine": "InnoDB" - }, - "columns": [ - { "name": "id", "type": "ID" }, - { - "label": "名称", - "name": "name", - "type": "string", - "length": 200, - "comment": "全称", - "unique": true - }, - { - "label": "简称", - "name": "short_name", - "type": "string", - "length": 200, - "comment": "简称", - "index": true - }, - { - "label": "类型", - "name": "type", - "type": "enum", - "option": ["服务商", "采购商", "其他"], - "comment": "厂商类型 服务商, 采购商, 其他", - "default": "服务商", - "index": true - }, - { - "label": "LOGO", - "name": "logo", - "type": "string", - "length": 200, - "comment": "LOGO", - "nullable": true - }, - { - "label": "简介", - "name": "summary", - "type": "string", - "length": 600, - "comment": "简介", - "nullable": true, - "index": true - }, - { - "label": "官网", - "name": "link", - "type": "string", - "length": 200, - "comment": "官网地址", - "nullable": true - }, - { - "label": "联系人", - "name": "contact_name", - "type": "string", - "length": 200, - "comment": "联系人", - "nullable": true, - "index": true - }, - { - "label": "联系电话", - "name": "contact_mobile", - "type": "string", - "comment": "联系电话", - "nullable": true, - "index": true - }, - { - "label": "详情", - "name": "desc", - "type": "text", - "comment": "介绍", - "nullable": true - }, - { - "label": "排序", - "name": "rank", - "type": "integer", - "default": 9999999, - "comment": "查询排序" - }, - { - "label": "状态", - "name": "status", - "type": "enum", - "default": "enabled", - "option": ["enabled", "disabled"], - "comment": "状态 enabled 开启, disabled 关闭", - "index": true - } - ], - "relations": {}, - "option": { "timestamps": true, "soft_deletes": true }, - "values": [ - { "id": 1, "name": "北京云道天成科技有限公司", "short_name": "云道天成" }, - { - "id": 2, - "name": "象传高新(北京)数字科技有限公司", - "short_name": "象传高新" - }, - { - "id": 3, - "name": "象传智数(北京)软件科技有限公司", - "short_name": "象传智数" - }, - { - "id": 4, - "name": "象传智慧(北京)软件科技有限公司", - "short_name": "象传智慧" - }, - { "id": 5, "name": "象传数据服务引擎", "short_name": "服务引擎" }, - { "id": 6, "name": "象传数据应用开发平台", "short_name": "应用开发平台" }, - { "id": 7, "name": "象传数据解决方案", "short_name": "解决方案" }, - { "id": 8, "name": "象传数据企业服务", "short_name": "企业服务" } - ] -} diff --git a/tests/models/role.json b/tests/models/role.json deleted file mode 100644 index 2ca7aa9a..00000000 --- a/tests/models/role.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "name": "角色", - "table": { - "name": "role", - "comment": "角色表", - "engine": "InnoDB" - }, - "columns": [ - { "name": "id", "type": "ID" }, - { - "label": "名称", - "name": "name", - "type": "string", - "length": 200, - "comment": "名称", - "index": true - }, - { - "label": "标签", - "name": "label", - "type": "string", - "length": 200, - "comment": "标签", - "index": true - }, - { - "label": "权限", - "name": "permission", - "type": "JSON", - "comment": "权限" - }, - { - "label": "状态", - "name": "status", - "type": "enum", - "default": "enabled", - "option": ["enabled", "disabled"], - "comment": "状态 enabled 开启, disabled 关闭", - "index": true - } - ], - "relations": {}, - "option": { "timestamps": true, "soft_deletes": true }, - "values": [ - { - "label": "超级管理员", - "name": "admin", - "status": "enabled", - "permission": { "read": ["t1", "t2", "t3"] } - }, - { - "label": "编辑", - "name": "editor", - "status": "enabled", - "permission": { "read": ["t1", "t2", "t3"] } - }, - { - "label": "用户", - "name": "user", - "status": "enabled", - "permission": { "read": ["t1", "t2", "t3"] } - }, - { - "label": "访客", - "name": "any", - "status": "disabled", - "permission": { "read": ["t1", "t2", "t3"] } - } - ] -} diff --git a/tests/models/service.json b/tests/models/service.json deleted file mode 100644 index 61a71f74..00000000 --- a/tests/models/service.json +++ /dev/null @@ -1,169 +0,0 @@ -{ - "name": "云服务库", - "table": { - "name": "service", - "comment": "云服务表", - "engine": "InnoDB" - }, - "columns": [ - { "name": "id", "type": "ID" }, - { - "name": "short_name", - "type": "string", - "length": 200, - "comment": "简称", - "nullable": true, - "index": true - }, - { - "name": "name", - "type": "string", - "length": 200, - "comment": "服务名称", - "index": true - }, - { - "name": "city", - "type": "string", - "length": 50, - "comment": "城市", - "nullable": true, - "index": true - }, - { - "name": "kind_id", - "type": "bigInteger", - "length": 200, - "comment": "服务类型", - "index": true - }, - { - "name": "manu_id", - "type": "bigInteger", - "length": 200, - "comment": "所属厂商", - "index": true - }, - { - "name": "registed_at", - "type": "string", - "length": 200, - "comment": "注册地", - "nullable": true, - "index": true - }, - { - "name": "link", - "type": "string", - "length": 200, - "comment": "官网地址", - "nullable": true - }, - { - "name": "contact_name", - "type": "string", - "length": 200, - "comment": "联系人", - "nullable": true, - "index": true - }, - { - "name": "contact_mobile", - "type": "string", - "comment": "联系电话", - "nullable": true, - "index": true - }, - { - "name": "fields", - "type": "json", - "comment": "JSON|服务领域(多选)", - "nullable": true, - "index": true - }, - { - "name": "price_options", - "type": "json", - "length": 500, - "nullable": true, - "comment": "订阅方式(多选)", - "index": true - }, - { - "name": "industries", - "type": "json", - "comment": "JSON|行业覆盖(多选)", - "nullable": true, - "index": true - }, - { - "name": "rank", - "type": "integer", - "default": 9999999, - "comment": "查询排序" - }, - { - "name": "status", - "type": "enum", - "default": "enabled", - "option": ["enabled", "disabled"], - "comment": "状态 enabled 开启, disabled 关闭", - "index": true - } - ], - "relations": { - "manu": { - "type": "hasOne", - "model": "manu", - "key": "id", - "foreign": "manu_id", - "query": { "select": ["id", "name", "short_name", "status"] } - }, - "kind": { - "type": "hasOne", - "model": "kind", - "key": "id", - "foreign": "kind_id", - "query": { "select": ["id", "name"] } - } - }, - "option": { "timestamps": true, "soft_deletes": true }, - "values": [ - { - "name": "腾讯云主机CVM", - "city": "北京", - "short_name": "云主机", - "kind_id": 3, - "manu_id": 1, - "industries": ["教育", "能源"], - "price_options": ["按月订阅"] - }, - { - "name": "腾讯云磁盘", - "city": "广州", - "short_name": "云磁盘", - "kind_id": 6, - "manu_id": 1, - "industries": ["政府", "能源"], - "price_options": ["按月订阅"] - }, - { - "name": "阿里主机", - "city": "上海", - "short_name": "云主机", - "kind_id": 3, - "manu_id": 2, - "industries": ["教育", "医疗"], - "price_options": ["按月订阅"] - }, - { - "name": "UCloud云主机", - "short_name": "云主机", - "city": "上海", - "kind_id": 3, - "manu_id": 3, - "industries": ["软件", "医疗"], - "price_options": ["按月订阅"] - } - ] -} diff --git a/tests/models/user.json b/tests/models/user.json deleted file mode 100644 index 96c8d8a6..00000000 --- a/tests/models/user.json +++ /dev/null @@ -1,396 +0,0 @@ -{ - "name": "用户", - "table": { - "name": "user", - "comment": "用户表", - "engine": "InnoDB" - }, - "columns": [ - { "label": "ID", "name": "id", "type": "ID" }, - { - "label": "厂商", - "name": "manu_id", - "type": "bigInteger", - "length": 50, - "comment": "所属厂商", - "nullable": true, - "index": true, - "validations": [ - { - "method": "typeof", - "args": ["integer"], - "message": "{{input}}类型错误, {{label}}应为数字" - }, - { - "method": "min", - "args": [0], - "message": "{{label}}应大于0" - } - ] - }, - { - "label": "类型", - "name": "type", - "type": "enum", - "option": ["admin", "staff", "user"], - "comment": "账号类型 admin 管理员, staff 员工, user 用户", - "default": "staff", - "index": true, - "validations": [ - { - "method": "typeof", - "args": ["string"], - "message": "{{input}}类型错误, {{label}}应该为字符串" - }, - { - "method": "enum", - "args": ["admin", "staff", "user"], - "message": "{{input}}不在许可范围, {{label}}应该为 admin/staff/user" - } - ] - }, - { - "label": "手机号", - "name": "mobile", - "type": "string", - "length": 50, - "comment": "手机号", - "index": true, - "crypt": "AES", - "validations": [ - { - "method": "typeof", - "args": ["string"], - "message": "{{input}}类型错误, {{label}}应该为字符串" - }, - { - "method": "pattern", - "args": ["^1[3-9]\\d{9}$"], - "message": "{{input}}格式错误" - } - ] - }, - { - "label": "登录密码", - "name": "password", - "type": "string", - "length": 256, - "comment": "登录密码", - "crypt": "PASSWORD", - "index": true, - "validations": [ - { - "method": "typeof", - "args": ["string"], - "message": "{{input}}类型错误, {{label}}应该为字符串" - }, - { - "method": "minLength", - "args": [6], - "message": "{{label}}应该由6-18位,大小写字母、数字和符号构成" - }, - { - "method": "maxLength", - "args": [18], - "message": "{{label}}应该由6-18位,大小写字母、数字和符号构成" - }, - { - "method": "pattern", - "args": ["[0-9]+"], - "message": "{{label}}应该至少包含一个数字" - }, - { - "method": "pattern", - "args": ["[A-Z]+"], - "message": "{{label}}应该至少包含一个大写字母" - }, - { - "method": "pattern", - "args": ["[a-z]+"], - "message": "{{label}}应该至少包含一个小写字母" - }, - { - "method": "pattern", - "args": ["[@#$&*]+"], - "message": "{{label}}应该至少包含一个符号" - } - ] - }, - { - "label": "姓名", - "name": "name", - "type": "string", - "length": 80, - "comment": "姓名", - "index": true, - "validations": [ - { - "method": "typeof", - "args": ["string"], - "message": "{{input}}类型错误, {{label}}应该为字符串" - }, - { - "method": "minLength", - "args": [2], - "message": "{{label}}至少需要2个字" - }, - { - "method": "maxLength", - "args": [40], - "message": "{{label}}不能超过20个字" - } - ] - }, - { - "label": "身份证号码", - "name": "idcard", - "type": "string", - "length": 256, - "comment": "身份证号码", - "crypt": "AES", - "nullable": true, - "index": true, - "validations": [ - { - "method": "typeof", - "args": ["string"], - "message": "{{input}}类型错误, {{label}}应该为字符串" - }, - { - "method": "pattern", - "args": ["^(\\d{18})|(\\d{14}X)$"], - "message": "{{label}}格式错误" - } - ] - }, - { - "label": "账户余额", - "name": "balance", - "type": "integer", - "length": 20, - "comment": "账户余额(冗余)", - "default": 0, - "index": true, - "validations": [ - { - "method": "typeof", - "args": ["integer"], - "message": "{{input}}类型错误, {{label}}应为数字" - }, - { - "method": "min", - "args": [0], - "message": "{{label}}应大于0" - } - ] - }, - { - "label": "API Key", - "name": "key", - "type": "string", - "length": 256, - "comment": "API Key", - "nullable": true, - "unique": true, - "validations": [ - { - "method": "typeof", - "args": ["string"], - "message": "{{input}}类型错误, {{label}}应该为字符串" - }, - { - "method": "pattern", - "args": ["^[0-9A-Za-z@#$&*]{8}$"], - "message": " {{label}}应该由8位,大小写字母、数字和符号构成" - } - ] - }, - { - "label": "API 密钥", - "name": "secret", - "type": "string", - "length": 256, - "nullable": true, - "crypt": "AES", - "comment": "API 密钥", - "index": true, - "validations": [ - { - "method": "typeof", - "args": ["string"], - "message": "{{input}}类型错误, {{label}}应该为字符串" - }, - { - "method": "pattern", - "args": ["^[0-9A-Za-z@#$&*]{32}$"], - "message": "{{label}}应该由32位,大小写字母、数字和符号构成" - } - ] - }, - { - "label": "简历", - "name": "resume", - "type": "text", - "comment": "简历", - "nullable": true - }, - { - "label": "扩展信息", - "name": "extra", - "type": "json", - "comment": "扩展信息", - "nullable": true - }, - { - "label": "状态", - "comment": "用户状态 enabled 有效, disabled 无效", - "name": "status", - "type": "enum", - "default": "enabled", - "option": ["enabled", "disabled"], - "index": true, - "validations": [ - { - "method": "typeof", - "args": ["string"], - "message": "{{input}}类型错误, {{label}}应该为字符串" - }, - { - "method": "enum", - "args": ["enabled", "disabled"], - "message": "{{input}}不在许可范围, {{label}}应该为 enabled/disabled" - } - ] - } - ], - "relations": { - "manu": { - "type": "hasOne", - "model": "manu", - "key": "id", - "foreign": "manu_id", - "select": ["name", "short_name", "type"] - }, - "addresses": { - "type": "hasMany", - "model": "address", - "key": "user_id", - "foreign": "id", - "query": { - "select": ["province", "city", "location", "status"], - "pagesize": 20 - } - }, - "mother": { - "type": "hasOneThrough", - "links": [ - { - "type": "hasOne", - "model": "friends", - "key": "user_id", - "foreign": "user.id", - "query": { - "select": ["status", "type", "friend_id"], - "wheres": [ - { - "column": "type", - "value": "monther" - } - ] - } - }, - { - "type": "hasOne", - "model": "user", - "key": "id", - "foreign": "user_mother_friends.friend_id", - "query": { - "select": ["name", "id", "status", "type", "secret", "extra"], - "withs": { - "manu": { "name": "manu" }, - "roles": { "name": "roles" }, - "address": { "name": "address" } - } - } - } - ] - }, - "roles": { - "type": "hasManyThrough", - "links": [ - { - "type": "hasMany", - "model": "user_roles", - "key": "user_id", - "foreign": "id", - "query": { - "select": ["status"], - "pagesize": 20 - } - }, - { - "type": "hasOne", - "model": "role", - "key": "id", - "foreign": "role_id", - "query": { - "select": ["name", "label", "permission"] - } - } - ] - } - }, - "values": [ - { - "name": "管理员", - "manu_id": 1, - "type": "admin", - "idcard": "230624198301170015", - "mobile": "13900001111", - "password": "cvSK@RY6", - "key": "FB3fxCeQ", - "secret": "XMTdNRVigbgUiAPdiJCfaWgWcz2PaQXw", - "status": "enabled", - "extra": { "sex": "男" } - }, - { - "name": "员工", - "manu_id": 1, - "type": "staff", - "idcard": "23082619820207024X", - "mobile": "13900002222", - "password": "qV@uT1DI", - "key": "JDh2ZiUt", - "secret": "wBeYjL7FjbcvpAdBrxtDFfjydsoPKhRN", - "status": "enabled", - "extra": { "sex": "女" } - }, - { - "name": "用户", - "manu_id": 2, - "type": "user", - "idcard": "23082619820207004X", - "mobile": "13900003333", - "password": "qV@uT1DI", - "key": "XZ12MiPz", - "secret": "wBeYjL7FjbcvpAdBrxtDFfjydsoPKhRN", - "status": "enabled", - "extra": { "sex": "女" } - } - ], - "indexes": [ - { - "comment": "厂商用户", - "name": "manu_id_mobile_unique", - "columns": ["manu_id", "mobile"], - "type": "unique" - }, - { - "comment": "简历全文检索", - "name": "resume_fulltext", - "columns": ["resume"], - "type": "fulltext" - } - ], - "option": { "timestamps": true, "soft_deletes": true } -} diff --git a/tests/models/user_roles.json b/tests/models/user_roles.json deleted file mode 100644 index 10a57c42..00000000 --- a/tests/models/user_roles.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "用户角色", - "table": { - "name": "user_roles", - "comment": "用户角色关系表", - "engine": "InnoDB" - }, - "columns": [ - { "name": "id", "type": "ID" }, - { - "label": "用户ID", - "name": "user_id", - "type": "bigInteger", - "comment": "所属用户", - "index": true - }, - { - "label": "角色ID", - "name": "role_id", - "type": "bigInteger", - "comment": "角色ID", - "index": true - }, - { - "label": "状态", - "name": "status", - "type": "enum", - "default": "enabled", - "option": ["enabled", "disabled"], - "comment": "状态 enabled 开启, disabled 关闭", - "index": true - } - ], - "relations": {}, - "option": { "timestamps": true, "soft_deletes": true }, - "values": [ - { "user_id": 1, "role_id": 1 }, - { "user_id": 1, "role_id": 2 }, - { "user_id": 2, "role_id": 3 }, - { "user_id": 2, "role_id": 4 }, - { "user_id": 3, "role_id": 1 } - ] -} diff --git a/tests/pages/lang.page.json b/tests/pages/lang.page.json deleted file mode 100644 index f2553516..00000000 --- a/tests/pages/lang.page.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "label": "::Demo", - "version": "1.0.0", - "description": "::Demo", - "nodes": [ - { - "name": "行业", - "engine": "xiang", - "query": { - "select": [ - "city", - ":COUNT(id) as 数量", - "industries[*](string 50) as industry" - ], - "from": "$service", - "wheres": [ - { "field": "created_at", ">": "?:$from.0" }, - { "field": "created_at", "<": "?:$to" } - ], - "orders": "数量 desc", - "limit": 100 - } - }, - { - "name": "计费", - "engine": "xiang", - "query": { - "select": ["city", ":COUNT(id) as 数量", "price_options[*] as option"], - "from": "$service", - "wheres": [ - { "field": "created_at", ">": "?:$from.0" }, - { "field": "created_at", "<": "?:$to" } - ], - "orders": "数量 desc", - "limit": 100 - } - }, - { - "name": "合并结果", - "process": "xiang.helper.ArrayPluck", - "args": [ - ["城市", "行业", "计费"], - { - "行业": { "key": "city", "value": "数量", "items": "{{$res.行业}}" }, - "计费": { "key": "city", "value": "数量", "items": "{{$res.计费}}" } - } - ] - } - ], - "output": { - "::Merge": "{{$res.合并结果}}", - "::Billing": "{{$res.计费}}", - "::Industry": "{{$res.行业}}", - "::Parameter": "{{$in}}" - }, - "apis": { - "data": { - "disable": false, - "guard": "-", - "default": [{ "from": "1980-01-02", "to": "2050-12-31" }] - }, - "setting": { "disable": false, "guard": "-" } - }, - "filters": { - "::Start": { - "label": "::Start", - "bind": "from", - "input": { - "type": "date", - "props": { - "placeholder": "::Please select a start time" - } - } - }, - "::End": { - "label": "::End", - "bind": "to", - "input": { - "type": "date", - "props": { - "placeholder": "::Please select an end time" - } - } - } - }, - "page": { - "primary": "::City", - "layout": { - "filters": [ - { "name": "::Start", "width": 6 }, - { "name": "::End", "width": 6 } - ], - "charts": [ - { "type": "line", "props": {} }, - { "type": "bar", "props": {} } - ] - }, - "actions": {} - } -} diff --git a/tests/pages/service/compare.data.js b/tests/pages/service/compare.data.js deleted file mode 100644 index ead9aa82..00000000 --- a/tests/pages/service/compare.data.js +++ /dev/null @@ -1,3 +0,0 @@ -function main(args, out, res) { - return { args: args }; -} diff --git a/tests/pages/service/compare.page.json b/tests/pages/service/compare.page.json deleted file mode 100644 index 5478c7c6..00000000 --- a/tests/pages/service/compare.page.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "label": "指标对比", - "version": "1.0.0", - "description": "指标对比用于测试", - "nodes": [ - { - "name": "行业", - "engine": "xiang", - "query": { - "debug": false, - "select": [ - "city", - ":COUNT(id) as 数量", - "industries[*](string 50) as industry" - ], - "from": "$service", - "wheres": [ - { "field": "created_at", ">": "?:$from.0" }, - { "field": "created_at", "<": "?:$to" } - ], - "orders": "数量 desc", - "limit": 100 - } - }, - { - "name": "计费", - "engine": "xiang", - "query": { - "select": ["city", ":COUNT(id) as 数量", "price_options[*] as option"], - "from": "$service", - "wheres": [ - { "field": "created_at", ">": "?:$from.0" }, - { "field": "created_at", "<": "?:$to" } - ], - "orders": "数量 desc", - "limit": 100 - } - }, - { - "name": "脚本", - "script": "data", - "outs": ["{{$out}}", "1"] - }, - { - "name": "合并结果", - "process": "xiang.helper.ArrayPluck", - "args": [ - ["城市", "行业", "计费"], - { - "行业": { "key": "city", "value": "数量", "items": "{{$res.行业}}" }, - "计费": { "key": "city", "value": "数量", "items": "{{$res.计费}}" } - } - ] - } - ], - "output": { - "合并": "{{$res.合并结果}}", - "计费": "{{$res.计费}}", - "行业": "{{$res.行业}}", - "参数": "{{$in}}", - "脚本": "{{$res.脚本}}", - "page": true - }, - "apis": { - "data": { - "disable": false, - "guard": "-", - "default": [{ "from": "1980-01-02", "to": "2050-12-31" }] - }, - "setting": { - "disable": false, - "guard": "-" - } - }, - "filters": { - "开始时间": { - "label": "开始时间", - "bind": "from", - "input": { - "type": "date", - "props": { - "placeholder": "请选择开始时间" - } - } - }, - "结束时间": { - "label": "结束时间", - "bind": "to", - "input": { - "type": "date", - "props": { - "placeholder": "请选择结束时间" - } - } - } - }, - "page": { - "primary": "城市", - "layout": { - "filters": [ - { "name": "开始时间", "width": 6 }, - { "name": "结束时间", "width": 6 } - ], - "charts": [ - { "type": "line", "props": {} }, - { "type": "bar", "props": {} } - ] - }, - "actions": {} - } -} diff --git a/tests/plugins/Makefile b/tests/plugins/Makefile deleted file mode 100644 index 3134af36..00000000 --- a/tests/plugins/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -all: clean plugin - -plugin: - go build -o ./dist/user ./user - chmod +x ./dist/user - -clean: - rm -rf ./dist \ No newline at end of file diff --git a/tests/plugins/python/README.md b/tests/plugins/python/README.md deleted file mode 100644 index 65a0ef8e..00000000 --- a/tests/plugins/python/README.md +++ /dev/null @@ -1 +0,0 @@ -# Python 插件 diff --git a/tests/plugins/user/user.go b/tests/plugins/user/user.go deleted file mode 100644 index 3dceb1ee..00000000 --- a/tests/plugins/user/user.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "encoding/json" - "io" - "os" - "path" - - "github.com/yaoapp/kun/grpc" - "github.com/yaoapp/kun/maps" -) - -// user Here is a real implementation of plugin that writes to a local file with -// the key name and the contents are the value of the key. -type user struct{ grpc.Plugin } - -// Exec 读取 -func (user *user) Exec(name string, args ...interface{}) (*grpc.Response, error) { - user.Logger.Debug("message from user.Exec") - v := maps.MakeMap() - v.Set("name", name) - v.Set("args", args) - bytes, err := json.Marshal(v) - if err != nil { - return nil, err - } - return &grpc.Response{Bytes: bytes, Type: "map"}, nil -} - -func main() { - var output io.Writer = os.Stderr - var logroot = os.Getenv("GOU_TEST_PLG_LOG") - if logroot != "" { - logfile, err := os.Create(path.Join(logroot, "user.log")) - if err == nil { - output = logfile - } - } - plugin := &user{} - plugin.SetLogger(output, grpc.Trace) - grpc.Serve(plugin) -} diff --git a/tests/schedules/mail.sch.json b/tests/schedules/mail.sch.json deleted file mode 100644 index fc9c1084..00000000 --- a/tests/schedules/mail.sch.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "每分钟发送一封邮件", - "schedule": "*/1 * * * *", - "process": "scripts.mail.Send", - "args": [null, "$ENV.SEND_MAIL_TEST_MAIL"] -} diff --git a/tests/schedules/sendmail.sch.json b/tests/schedules/sendmail.sch.json deleted file mode 100644 index b6d6e0bc..00000000 --- a/tests/schedules/sendmail.sch.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "每分钟发送一封邮件", - "schedule": "*/1 * * * *", - "task": "mail", - "args": ["$ENV.SEND_MAIL_TEST_MAIL"] -} diff --git a/tests/scripts/helper.js b/tests/scripts/helper.js deleted file mode 100644 index 05b95d1f..00000000 --- a/tests/scripts/helper.js +++ /dev/null @@ -1,18 +0,0 @@ -function HexToStringString() { - return Process("xiang.helper.HexToString", "ab"); -} - -function HexToStringBytes(data) { - return Process("xiang.helper.HexToString", data); -} - -function Buffer() { - var buffer = new Uint8Array(2); - buffer[0] = 0x0; - buffer[1] = 0x1; - return BufferToString(buffer); -} - -function BufferToString(buffer) { - return String.fromCharCode.apply(null, new Uint8Array(buffer)); -} diff --git a/tests/scripts/mail.js b/tests/scripts/mail.js deleted file mode 100644 index cc8a4fb4..00000000 --- a/tests/scripts/mail.js +++ /dev/null @@ -1,53 +0,0 @@ -var id = 1024; - -/** - * Generate job id - * @returns - */ -function NextID() { - id = id + 1; - console.log(`NextID: ${id}`); - return id; -} - -function Send(id, mail, flag) { - for (var i = 1; i <= 3; i++) { - Process("xiang.sys.Sleep", 200); - Process("tasks.mail.Progress", id, i, 3, "unit-test"); - } - if (flag) { - console.log(`flag: ${flag}`); - Process("xiang.sys.Sleep", 2000); - } - console.log( - `Send: ${JSON.stringify({ foo: "bar", mail: mail, flag: flag || "-" })}` - ); - return { foo: "bar", mail: mail, flag: flag || "-" }; -} - -/** - * OnAdd add event - * @param {*} id - */ -function OnAdd(id) { - console.log(`OnAdd: #${id}`); -} - -/** - * OnProgress - * @param {*} id - * @param {*} current - * @param {*} total - * @param {*} message - */ -function OnProgress(id, current, total, message) { - console.log(`OnProgress: #${id} ${message} ${current}/${total} `); -} - -function OnSuccess(id, res) { - console.log(`OnSuccess: #${id} ${JSON.stringify(res)}`); -} - -function OnError(id, err) { - console.log(`OnError: #${id} ${err}`); -} diff --git a/tests/scripts/rules.js b/tests/scripts/rules.js deleted file mode 100644 index 3e380f07..00000000 --- a/tests/scripts/rules.js +++ /dev/null @@ -1,37 +0,0 @@ -// 校验数值 -function order_sn(value, row) { - value = value || null; - row = row || {}; - if (value == null) { - return false; - } - return row; -} - -function FmtUser(value, row) { - row[9] = "自动添加备注 @From " + value; - return row; -} - -function FmtGoods(value, row) { - return row; -} - -function mobile(value, row) { - return row; -} - -function ImportData(columns, data) { - last = columns.length - 1; - ignore = 0; - failure = 0; - if (data.length > 1) { - failure = 1; - } - for (var i in data) { - if (data[i][last] == false) { - ignore = ignore + 1; - } - } - return [failure, ignore]; -} diff --git a/tests/scripts/service.js b/tests/scripts/service.js deleted file mode 100644 index 2be2d8fd..00000000 --- a/tests/scripts/service.js +++ /dev/null @@ -1,17 +0,0 @@ -function BeforeSearch(param, page, pagesize) { - return [param, page, pagesize]; -} - -function AfterSearch(data) { - var newData = data || {}; - var items = data.data || []; - for (var i in items) { - items[i]["city"] = `ID: ${items[i]["id"]} 城市: ${items[i]["city"]} `; - var res = Process("models.service.Find", items[i]["id"], {}); - // console.log( - // `id:${items[i]["id"]} ${res["name"]} page: ${newData["page"]} pagesize: ${newData["pagesize"]} pagecnt: ${newData["pagecnt"]} next:${newData["next"]}` - // ); - } - newData["data"] = items; - return newData; -} diff --git a/tests/scripts/socket.js b/tests/scripts/socket.js deleted file mode 100644 index c22fc669..00000000 --- a/tests/scripts/socket.js +++ /dev/null @@ -1,34 +0,0 @@ -function onData(data, recvLen) { - console.log(`Data: ${data} ${recvLen}`); - log.Trace("onData: %v %v", data, recvLen); -} - -function onError(err) { - console.log(`Error: ${err} `); -} - -function onClosed(data, err) { - console.log(`Closed: ${data} ${err} `); -} - -function onConnected(option) { - console.log("onConnected", option); -} - -// Convert a hex string to a byte array -function hexToBytes(hex) { - for (var bytes = [], c = 0; c < hex.length; c += 2) { - bytes.push(parseInt(hex.substr(c, 2), 16)); - } - return bytes; -} - -// Convert a byte array to a hex string -function bytesToHex(bytes) { - for (var hex = [], i = 0; i < bytes.length; i++) { - var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i]; - hex.push((current >>> 4).toString(16)); - hex.push((current & 0xf).toString(16)); - } - return hex.join(""); -} diff --git a/tests/scripts/time.js b/tests/scripts/time.js deleted file mode 100644 index 99f1d089..00000000 --- a/tests/scripts/time.js +++ /dev/null @@ -1,13 +0,0 @@ -function now() { - return new Date().toISOString().split("T")[0]; -} - -function lastYear() { - var d = new Date(); - d.setFullYear(d.getFullYear() - 1); - return d.toISOString().split("T")[0]; -} - -function hello(name) { - return "name:" + name; -} diff --git a/tests/scripts/tree.js b/tests/scripts/tree.js deleted file mode 100644 index 8e99c477..00000000 --- a/tests/scripts/tree.js +++ /dev/null @@ -1,5 +0,0 @@ -function main(args, out, res) { - return { - tree: {}, - }; -} diff --git a/tests/scripts/websocket.js b/tests/scripts/websocket.js deleted file mode 100644 index dd5182ba..00000000 --- a/tests/scripts/websocket.js +++ /dev/null @@ -1,91 +0,0 @@ -function Hello() { - var ws = new WebSocket("ws://127.0.0.1:5093/websocket/chat", "p0"); - var response = ws.push("Hello World"); - return response; -} - -const host = "127.0.0.1:5099"; - -/** - * WebSocket Client EventMode(daemon) - */ -function Event() { - var url = `ws://${host}/websocket/chat`; - var ws = new WebSocket(url, "yao-chat-01"); - - // Connection opened - ws.on("open", function (event) { - ws.send("Hello Server!", event.data); - }); - - // Listen for messages - ws.on("message", function (event) { - console.log("Message from server ", event.data); - ws.close(200, "Bye"); - }); - - // Listen for error - ws.on("error", (event) => { - console.log("Message from server ", event); - }); - - // Listen for close - ws.on("close", (event) => { - console.log("The connection has been closed successfully"); - }); -} - -/** - * WebSocket Client PushMode - */ -function Push() { - var url = `ws://${host}/websocket/chat`; - var ws = new WebSocket(url, "yao-chat-01"); - var message = ws.push("Hello Server!"); - console.log(message); -} - -/** - * WebSocket Client JWT Auth - */ -function Token() { - var token = "xxx"; - var url = `ws://${host}/websocket/chat?token=${token}`; - var ws = new WebSocket(url, "yao-chat-01"); - var message = ws.push("Hello Server!"); - console.log(message); -} - -/** - * WebSocket Client Basic Auth - */ -function Basic() { - // Basic Auth username:password - var user = "test"; - var password = "WsTest123**"; - var url = `ws://${user}:${password}@${host}/websocket/chat?token=${token}`; - var ws = new WebSocket(url, "yao-chat-01"); - var message = ws.push("Hello Server!"); - console.log(message); -} - -function onData(data, recvLen) { - console.log(`Data: ${data} ${recvLen}`); - log.Trace("onData: %v %v", data, recvLen); - if (data[0] == "1") { - Process("websocket.Close", "message"); - } -} - -function onError(err) { - console.log(`Error: ${err} `); -} - -function onClosed(data, err) { - console.log(`Closed: ${data} ${err} `); -} - -function onConnected(option) { - console.log("onConnected", option); - Process("websocket.Write", "message", "1|Hello World"); -} diff --git a/tests/services/cmd.srv.json b/tests/services/cmd.srv.json deleted file mode 100644 index 844a2386..00000000 --- a/tests/services/cmd.srv.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "Service for receiving RFID", - "description": "Service for receiving RFID", - "version": "0.9.2", - "restart": "on-failure", - "requires": ["syslog"], - "after": ["syslog"], - "error": "/var/log/test.err", - "output": "/var/log/test.log", - "workdir": "~", - "command": "tail", - "args": ["-f", "/dev/null"], - "user": "root", - "group": "root" -} diff --git a/tests/services/process.srv.json b/tests/services/process.srv.json deleted file mode 100644 index 5c32b997..00000000 --- a/tests/services/process.srv.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "Service for receiving RFID", - "description": "Service for receiving RFID", - "version": "0.9.2", - "restart": "on-failure", - "requires": ["syslog"], - "after": ["syslog"], - "error": "/var/log/test.err", - "output": "/var/log/test.log", - "workdir": "~", - "process": "xiang.sys.ping", - "args": [], - "user": "root", - "group": "root" -} diff --git a/tests/sockets/rfid.sock.json b/tests/sockets/rfid.sock.json deleted file mode 100644 index 1da22f0a..00000000 --- a/tests/sockets/rfid.sock.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "RFID Receiver (Server Mode)", - "description": "RFID Receiver", - "mode": "server", - "version": "0.0.1", - "protocol": "tcp", - "host": "0.0.0.0", - "port": "3019", - "event": { - "data": "scripts.socket.onData", - "closed": "scripts.socket.onClosed", - "connected": "scripts.socket.onConnected", - "error": "scripts.socket.onError" - }, - "keep": 0, - "buffer": 60 -} diff --git a/tests/sockets/rfid_client.sock.json b/tests/sockets/rfid_client.sock.json deleted file mode 100644 index a66fdda7..00000000 --- a/tests/sockets/rfid_client.sock.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "RFID Receiver (Client Mode)", - "description": "RFID Receiver", - "mode": "client", - "version": "0.9.2", - "protocol": "tcp", - "host": "192.168.31.33", - "port": "3019", - "event": { - "data": "scripts.socket.onData", - "closed": "scripts.socket.onClosed", - "connected": "scripts.socket.onConnected", - "error": "scripts.socket.onError" - }, - "buffer": 60, - "keep": 0, - "attempts": 9, - "attempt_after": 10 -} diff --git a/tests/stores/cache.lru.json b/tests/stores/cache.lru.json deleted file mode 100644 index d2f22bd6..00000000 --- a/tests/stores/cache.lru.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "LRU Cache", - "description": "LRU cache", - "type": "lru", - "option": { "size": 1024 } -} diff --git a/tests/stores/data.mongo.json b/tests/stores/data.mongo.json deleted file mode 100644 index 6cf36699..00000000 --- a/tests/stores/data.mongo.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "MongoDB Key-Value store", - "description": "MongoDB Key-Value store", - "connector": "mongo", - "option": {} -} diff --git a/tests/stores/share.redis.json b/tests/stores/share.redis.json deleted file mode 100644 index 9bb69850..00000000 --- a/tests/stores/share.redis.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Redis Cache", - "description": "Redis cache", - "connector": "redis", - "option": {} -} diff --git a/tests/studio/table.js b/tests/studio/table.js deleted file mode 100644 index 925de552..00000000 --- a/tests/studio/table.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Create table - * @param {*} name - */ -function Make(name) { - var fs = new FS("dsl"); - var model = GetModel(name); - var table = JSON.stringify({ - name: `${model.name} Admin`, - actions: { bind: { model: name } }, - }); - fs.WriteFile(`/tables/auto/${name}.tab.json`, table); -} - -/** - * Get Model - * @param {*} name - * @returns - */ -function Model(name) { - var fs = new FS("dsl"); - var file = `/models/${name}.mod.json`; - var data = fs.ReadFile(file); - return JSON.parse(data); -} - -/** - * for unit tests - * @returns - */ -function Ping() { - return "PONG"; -} - -/** - * for unit tests - * @param {...any} args - * @returns - */ -function UnitTest(...args) { - if (args.length > 0 && args[0] == "throw-test") { - throw new Exception("I'm a teapot", 418); - } - return args; -} diff --git a/tests/tables/assign.tab.json b/tests/tables/assign.tab.json deleted file mode 100644 index 093e85f6..00000000 --- a/tests/tables/assign.tab.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "指派商务负责人", - "version": "1.0.0", - "decription": "指派商务负责人表单(?Next:表格和表单解耦)", - "bind": { "model": "service" }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [ - { - "columns": [{ "name": "name", "width": 24 }] - } - ] - } - } -} diff --git a/tests/tables/demo.tab.json b/tests/tables/demo.tab.json deleted file mode 100644 index 11d38e06..00000000 --- a/tests/tables/demo.tab.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "name": "::Demo", - "version": "1.0.0", - "decription": "::Demo", - "bind": { "model": "demo" }, - "apis": {}, - "columns": { - "::ID": { - "label": "::ID", - "view": { "type": "label", "props": { "value": ":id" } } - }, - "::SN": { - "label": "::SN", - "view": { "type": "label", "props": { "value": ":sn" } }, - "edit": { - "type": "input", - "props": { "value": ":sn", "placeholder": "::Please input the SN" } - } - }, - "::Name": { - "label": "::Name", - "view": { "type": "label", "props": { "value": ":name" } }, - "edit": { "type": "input", "props": { "value": ":name" } } - }, - "::Kind": { - "label": "::Kind", - "view": { "type": "label", "props": { "value": ":kind" } }, - "edit": { - "type": "select", - "props": { - "value": ":kind", - "options": [ - { "label": "::Cat", "value": "cat" }, - { "label": "::Dog", "value": "dog" } - ] - } - } - }, - "::Description": { - "label": "::Description", - "view": { "type": "label", "props": { "value": ":desc" } }, - "edit": { "type": "textArea", "props": { "value": ":desc", "rows": 4 } } - } - }, - "filters": { - "::Keywords": { - "label": "::Keywords", - "bind": "where.name.match", - "input": { - "type": "input", - "props": { "placeholder": "::type the keywords..." } - } - } - }, - "list": { - "primary": "id", - "layout": { - "columns": [ - { "name": "::ID", "width": 80 }, - { "name": "::SN", "width": 100 }, - { "name": "::Name", "width": 200 }, - { "name": "::Kind" } - ], - "filters": [{ "name": "::Keywords" }] - }, - "actions": { "pagination": { "props": { "showTotal": true } } }, - "option": { "operation": { "unfold": true } } - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [ - { - "columns": [ - { "name": "::SN", "width": 8 }, - { "name": "::Name", "width": 8 }, - { "name": "::Kind", "width": 8 }, - { "name": "::Description", "width": 24 } - ] - } - ] - }, - "actions": { "cancel": {}, "save": {}, "delete": {} } - } -} diff --git a/tests/tables/foo/bar.tab.json b/tests/tables/foo/bar.tab.json deleted file mode 100644 index fb76ec06..00000000 --- a/tests/tables/foo/bar.tab.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "菜单", - "version": "1.0.0", - "decription": "菜单设置", - "bind": { - "model": "service" - }, - "apis": {}, - "columns": {}, - "filters": {}, - "list": { - "primary": "id", - "layout": { - "columns": [{ "name": "name", "width": "240px" }], - "filters": [{ "name": "name", "width": 6 }] - }, - "actions": { - "create": { - "type": "button", - "props": { "label": "新建菜单", "icon": "fas fa-plus" } - }, - "view": {}, - "edit": {}, - "import": {}, - "update": {}, - "delete": {}, - "insert": {}, - "updateWhere": {}, - "deleteWhere": {}, - "updateSelect": {}, - "deleteSelect": {}, - "pagination": {}, - "setting": {} - } - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [ - { - "title": "基础信息", - "description": "", - "columns": [ - { "name": "name", "width": 6 }, - { "name": "id", "width": 6 } - ] - } - ] - }, - "actions": { - "cancel": {}, - "save": { - "type": "button", - "props": { - "label": "保存" - } - }, - "delete": {} - } - }, - "insert": {}, - "view": {} -} diff --git a/tests/tables/hooks/find.tab.json b/tests/tables/hooks/find.tab.json deleted file mode 100644 index eb5c1233..00000000 --- a/tests/tables/hooks/find.tab.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "云服务库", - "version": "1.0.0", - "decription": "云服务库", - "bind": { "model": "service" }, - "hooks": { - "before:find": "flows.hooks.before_find", - "after:find": "flows.hooks.after_find" - }, - "apis": {}, - "columns": {}, - "filters": {}, - "list": { - "primary": "id", - "layout": { - "columns": [{ "name": "id", "width": 6 }], - "filters": [] - }, - "actions": {} - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [{ "columns": [{ "name": "id", "width": 6 }] }] - }, - "actions": { "cancel": {} } - }, - "insert": {}, - "view": {} -} diff --git a/tests/tables/hooks/save.tab.json b/tests/tables/hooks/save.tab.json deleted file mode 100644 index 455093aa..00000000 --- a/tests/tables/hooks/save.tab.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "云服务库", - "version": "1.0.0", - "decription": "云服务库", - "bind": { "model": "service" }, - "hooks": { - "before:save": "flows.hooks.before_save", - "after:save": "flows.hooks.after_save" - }, - "apis": {}, - "columns": {}, - "filters": {}, - "list": { - "primary": "id", - "layout": { - "columns": [{ "name": "id", "width": 6 }], - "filters": [] - }, - "actions": {} - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [{ "columns": [{ "name": "id", "width": 6 }] }] - }, - "actions": { "cancel": {} } - }, - "insert": {}, - "view": {} -} diff --git a/tests/tables/hooks/search.tab.json b/tests/tables/hooks/search.tab.json deleted file mode 100644 index 926d544f..00000000 --- a/tests/tables/hooks/search.tab.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "云服务库", - "version": "1.0.0", - "decription": "云服务库", - "bind": { - "model": "service", - "withs": {} - }, - "hooks": { - "before:search": "flows.hooks.before_search", - "after:search": "flows.hooks.after_search" - }, - "apis": { - "search": { - "process": "models.service.Paginate", - "guard": "-", - "default": [null, null, 15] - } - }, - "columns": {}, - "filters": {}, - "list": { - "primary": "id", - "layout": { - "columns": [{ "name": "id", "width": 6 }, { "name": "城市" }], - "filters": [] - }, - "actions": {} - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [{ "columns": [{ "name": "id", "width": 6 }] }] - }, - "actions": { "cancel": {} } - }, - "insert": {}, - "view": {} -} diff --git a/tests/tables/hooks/search_script.json b/tests/tables/hooks/search_script.json deleted file mode 100644 index 33b7bd4f..00000000 --- a/tests/tables/hooks/search_script.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "云服务库", - "version": "1.0.0", - "decription": "云服务库", - "bind": { - "model": "service", - "withs": {} - }, - "hooks": { - "before:search": "scripts.service.BeforeSearch", - "after:search": "scripts.service.AfterSearch" - }, - "apis": { - "search": { - "process": "models.service.Paginate", - "guard": "-", - "default": [{ "orders": [{ "column": "id" }] }, null, 15] - } - }, - "columns": {}, - "filters": {}, - "list": { - "primary": "id", - "layout": { - "columns": [ - { "name": "id", "width": 6 }, - { "name": "服务名称" }, - { "name": "城市" } - ], - "filters": [] - }, - "actions": {} - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [{ "columns": [{ "name": "id", "width": 6 }] }] - }, - "actions": { "cancel": {} } - }, - "insert": {}, - "view": {} -} diff --git a/tests/tables/hooks/select.tab.json b/tests/tables/hooks/select.tab.json deleted file mode 100644 index 1c9f6dc1..00000000 --- a/tests/tables/hooks/select.tab.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "云服务库", - "version": "1.0.0", - "decription": "云服务库", - "bind": { "model": "service" }, - "hooks": { - "before:select": "flows.hooks.before_select", - "after:select": "flows.hooks.after_select" - }, - "apis": {}, - "columns": {}, - "filters": {}, - "list": { - "primary": "id", - "layout": { - "columns": [{ "name": "id", "width": 6 }], - "filters": [] - }, - "actions": {} - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [{ "columns": [{ "name": "id", "width": 6 }] }] - }, - "actions": { "cancel": {} } - }, - "insert": {}, - "view": {} -} diff --git a/tests/tables/importable/table.tab.json b/tests/tables/importable/table.tab.json deleted file mode 100644 index d5294dd4..00000000 --- a/tests/tables/importable/table.tab.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "客户", - "version": "1.0.0", - "decription": "客户管理数据表格", - "bind": { - "model": "user" - }, - "apis": { - "search": {} - }, - "columns": { - "LOGO": { "@": "column.Image", "in": ["LOGO", ":logo", 40] }, - "ID": { "@": "column.Label", "in": ["ID", ":id"] } - }, - "filters": { - "关键词": { "@": "filter.关键词", "in": ["where.name.match"] }, - "排序": { "@": "filter.排序" }, - "状态": { "@": "filter.客户状态" } - }, - "list": { - "primary": "id", - "layout": { - "columns": [ - { "name": "ID", "width": 80 }, - { "name": "LOGO", "width": 300 } - ], - "filters": [ - { "name": "关键词" }, - { "name": "状态", "width": 3 }, - { "name": "排序", "width": 3 } - ] - }, - "actions": { - "create": { - "type": "button", - "props": { - "label": "添加厂商", - "icon": "fas fa-plus" - } - }, - "pagination": { - "props": { "showTotal": true } - } - }, - "option": { - "batch": { - "delete": {}, - "columns": [ - { "name": "状态", "width": 12 }, - { "name": "类型", "width": 12 }, - { "name": "排序", "width": 12 } - ] - } - } - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [ - { - "title": "客户资料", - "description": "", - "columns": [{ "name": "LOGO", "width": 12 }] - } - ] - }, - "actions": { - "cancel": {}, - "save": { "@": "action.保存" }, - "delete": { "@": "action.删除" } - } - }, - "insert": {}, - "view": {} -} diff --git a/tests/tables/service.json b/tests/tables/service.json deleted file mode 100644 index 641d9a6b..00000000 --- a/tests/tables/service.json +++ /dev/null @@ -1,393 +0,0 @@ -{ - "name": "云服务库", - "version": "1.0.0", - "decription": "可信云云服务库", - "bind": { - "model": "service", - "withs": { - "manu": { - "query": { - "select": ["id", "name", "short_name", "status"] - } - }, - "kind": { - "query": { - "select": ["id", "name"] - } - } - } - }, - "apis": { - "save": { "guard": "-" }, - "select": { "guard": "-" }, - "search": { - "process": "models.service.Paginate", - "default": [null, null, 15] - }, - "find": { - "process": "models.service.Find", - "default": [ - null, - { - "withs": { - "manu": { - "query": { - "select": ["id", "name", "short_name", "status"] - } - }, - "kind": { - "query": { - "select": ["id", "name"] - } - } - } - } - ] - } - }, - "columns": { - "服务名称": { - "label": "服务名称", - "view": { - "type": "label", - "props": { - "value": ":name" - } - }, - "edit": { - "type": "input", - "props": { - "value": ":name" - } - } - }, - "所属厂商": { - "label": "所属厂商", - "view": { - "name": "label", - "props": { - "value": ":manu.short_name" - } - }, - "edit": { - "type": "select", - "props": { - "value": ":manu.id", - "searchable": true, - "remote": { - "api": "/api/manu/get", - "query": { - "select": ["id", "name"], - "keyword": "where_name_like" - } - } - } - } - }, - "服务类型": { - "label": "服务类型", - "view": { - "name": "label", - "props": { - "value": ":kind.name" - } - }, - "edit": { - "type": "select", - "props": { - "value": ":kind.id", - "tree": true, - "searchable": true, - "remote": { - "api": "/api/kind/tree", - "query": { - "select": ["id", "name"], - "keyword": "where_name_like" - } - } - } - } - }, - "服务领域": { - "label": "服务领域", - "view": { - "name": "tags", - "props": { - "value": ":fields" - } - }, - "edit": { - "type": "select", - "props": { - "value": ":fields", - "searchable": true, - "inputable": true, - "multiple": true, - "options": [ - { "label": "视频会议", "value": "视频会议" }, - { "label": "即时通信", "value": "即时通信" }, - { "label": "客服管理", "value": "客服管理" }, - { "label": "财务管理", "value": "财务管理" }, - { "label": "客户关系管理", "value": "客户关系管理" }, - { "label": "营销管理", "value": "营销管理" }, - { "label": "办公自动化", "value": "办公自动化" }, - { "label": "ERP", "value": "ERP" }, - { "label": "人力", "value": "人力" }, - { "label": "采购", "value": "采购" }, - { "label": "供应链", "value": "供应链" }, - { "label": "企业网盘", "value": "企业网盘" }, - { "label": "邮件服务", "value": "邮件服务" }, - { "label": "电子合同", "value": "电子合同" }, - { "label": "安全工具", "value": "安全工具" }, - { "label": "舆情分析", "value": "舆情分析" }, - { "label": "行业应用", "value": "行业应用" }, - { "label": "其他", "value": "其他" } - ] - } - } - }, - "计费方式C": { - "label": "计费方式", - "view": { - "name": "xxxxx", - "components": { "label": {}, "image": {} } - } - }, - "计费方式": { - "label": "计费方式", - "view": { - "name": "tags", - "props": { - "value": ":fields", - "format": [".jpg", "..."] - } - }, - "edit": { - "type": "select", - "props": { - "value": ":price_options", - "multiple": true, - "options": [ - { "label": "按年订阅", "value": "按年订阅" }, - { "label": "按月订阅", "value": "按月订阅" }, - { "label": "按量计费", "value": "按量计费" }, - { "label": "私有化部署", "value": "私有化部署" }, - { "label": "其他", "value": "其他" } - ] - } - } - }, - "行业覆盖": { - "label": "行业覆盖", - "view": { - "name": "tags", - "props": { - "value": ":industries" - } - }, - "edit": { - "type": "select", - "props": { - "value": ":industries", - "inputable": true, - "multiple": true, - "options": [ - { "label": "房地产", "value": "房地产" }, - { "label": "旅游", "value": "旅游" }, - { "label": "教育", "value": "教育" }, - { "label": "互联网", "value": "互联网" }, - { "label": "其他", "value": "其他" } - ] - } - } - }, - "状态": { - "label": "状态", - "view": { - "name": "tags", - "props": { - "value": ":status", - "options": [ - { "label": "开启", "value": "enabled", "color": "primary" }, - { "label": "关闭", "value": "disabled", "color": "danger" } - ] - } - }, - "edit": { - "type": "select", - "props": { - "value": ":status", - "options": [ - { "label": "开启", "value": "enabled" }, - { "label": "关闭", "value": "disabled" } - ] - } - } - }, - "创建时间": { - "label": "创建时间", - "view": { - "name": "label", - "props": { - "value": ":created_at", - "datetime-format": "YYYY年MM月DD日 @hh:mm:ss" - } - } - }, - "更新时间": { - "label": "更新时间", - "view": { - "name": "label", - "props": { - "value": ":update_at", - "datetime-format": "YYYY年MM月DD日 @hh:mm:ss" - } - } - }, - "查询排序": { - "label": "查询排序", - "view": { - "type": "label", - "props": { - "value": ":rank" - } - }, - "edit": { - "type": "input", - "props": { - "value": ":rank", - "type": "number" - } - } - } - }, - "filters": { - "关键词": { - "label": "关键词", - "bind": "where.name.like", - "input": { - "type": "input", - "props": { - "placeholder": "请输入关键词" - } - } - }, - "所属厂商": { - "label": "厂商", - "bind": "where.manu_id.in", - "input": { - "type": "select", - "props": { - "searchable": true, - "multiple": true, - "placeholder": "请选择所属厂商", - "remote": { - "api": "/api/manu/get", - "query": { - "select": ["id", "name"], - "keyword": "where_name_like" - } - } - } - } - }, - "服务类型": { - "label": "类型", - "bind": "where.kind_id.in", - "input": { - "type": "select", - "props": { - "tree": true, - "searchable": true, - "multiple": true, - "placeholder": "请选择所属类型", - "remote": { - "api": "/api/kind/tree", - "query": { - "select": ["id", "name"], - "keyword": "where_name_like" - } - } - } - } - } - }, - "list": { - "primary": "id", - "layout": { - "columns": [ - { "name": "id", "width": 6 }, - { "name": "服务名称", "width": 6 }, - { "name": "所属厂商", "width": 6 }, - { "name": "服务类型", "width": 4 }, - { "name": "状态", "width": 4 }, - { "name": "服务领域", "width": 4 }, - { "name": "行业覆盖", "width": 4 }, - { "name": "计费方式", "width": 4 }, - { "name": "创建时间", "width": 6 }, - { "name": "更新时间", "width": 6 } - ], - "filters": [ - { "name": "关键词", "width": 6 }, - { "name": "所属厂商", "width": 6 }, - { "name": "服务类型", "width": 4 } - ] - }, - "actions": { - "create": { - "type": "button", - "props": { - "label": "新建服务", - "icon": "fas fa-plus" - } - }, - "view": {}, - "edit": {}, - "import": {}, - "update": {}, - "delete": {}, - "insert": {}, - "updateWhere": {}, - "deleteWhere": {}, - "updateSelect": {}, - "deleteSelect": {}, - "pagination": {}, - "setting": {} - } - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [ - { - "title": "基础信息", - "description": "", - "columns": [ - { "name": "服务名称", "width": 6 }, - { "name": "所属厂商", "width": 6 }, - { "name": "服务类型", "width": 6 }, - { "name": "状态", "width": 6 }, - { "name": "rank", "width": 6 } - ] - }, - { - "title": "数据标签", - "description": "", - "columns": ["服务领域", "计费方式", "行业覆盖"] - } - ] - }, - "actions": { - "cancel": {}, - "save": { - "type": "button", - "props": { - "label": "保存" - } - }, - "delete": {} - } - }, - "insert": {}, - "view": {} -} diff --git a/tests/tables/session.json b/tests/tables/session.json deleted file mode 100644 index 77cbfb91..00000000 --- a/tests/tables/session.json +++ /dev/null @@ -1,305 +0,0 @@ -{ - "name": "会话调试", - "version": "1.0.0", - "decription": "会话调试", - "bind": { - "model": "service", - "withs": { - "manu": { - "query": { - "select": ["id", "name", "short_name", "status"] - } - }, - "kind": { - "query": { - "select": ["id", "name"] - } - } - } - }, - "hooks": { - "before:search": "flows.hooks.session", - "after:search": "flows.hooks.session" - }, - "apis": { - "search": { - "default": [ - { - "wheres": [{ "column": "status", "value": "{{user_id}}" }], - "withs": { "manu": {}, "kind": {} } - }, - null, - 10 - ] - } - }, - "columns": { - "服务名称": { - "label": "服务名称", - "view": { - "type": "label", - "props": { - "value": ":name" - } - }, - "edit": { - "type": "input", - "props": { - "value": ":name" - } - } - }, - "所属厂商": { - "label": "所属厂商", - "view": { - "name": "label", - "props": { - "value": ":manu.short_name" - } - }, - "edit": { - "type": "select", - "props": { - "value": ":manu.id", - "searchable": true, - "remote": { - "api": "/api/manu/get", - "query": { - "select": ["id", "name"], - "keyword": "where_name_like" - } - } - } - } - }, - "服务类型": { - "label": "服务类型", - "view": { - "name": "label", - "props": { - "value": ":kind.name" - } - }, - "edit": { - "type": "select", - "props": { - "value": ":kind.id", - "tree": true, - "searchable": true, - "remote": { - "api": "/api/kind/tree", - "query": { - "select": ["id", "name"], - "keyword": "where_name_like" - } - } - } - } - }, - "服务领域": { - "label": "服务领域", - "view": { - "name": "tags", - "props": { - "value": ":fields" - } - }, - "edit": { - "type": "select", - "props": { - "value": ":fields", - "searchable": true, - "inputable": true, - "multiple": true, - "options": [ - { "label": "视频会议", "value": "视频会议" }, - { "label": "即时通信", "value": "即时通信" }, - { "label": "客服管理", "value": "客服管理" }, - { "label": "财务管理", "value": "财务管理" }, - { "label": "客户关系管理", "value": "客户关系管理" }, - { "label": "营销管理", "value": "营销管理" }, - { "label": "办公自动化", "value": "办公自动化" }, - { "label": "ERP", "value": "ERP" }, - { "label": "人力", "value": "人力" }, - { "label": "采购", "value": "采购" }, - { "label": "供应链", "value": "供应链" }, - { "label": "企业网盘", "value": "企业网盘" }, - { "label": "邮件服务", "value": "邮件服务" }, - { "label": "电子合同", "value": "电子合同" }, - { "label": "安全工具", "value": "安全工具" }, - { "label": "舆情分析", "value": "舆情分析" }, - { "label": "行业应用", "value": "行业应用" }, - { "label": "其他", "value": "其他" } - ] - } - } - }, - "计费方式C": { - "label": "计费方式", - "view": { - "name": "xxxxx", - "components": { "label": {}, "image": {} } - } - }, - "计费方式": { - "label": "计费方式", - "view": { - "name": "tags", - "props": { - "value": ":fields", - "format": [".jpg", "..."] - } - }, - "edit": { - "type": "select", - "props": { - "value": ":price_options", - "multiple": true, - "options": [ - { "label": "按年订阅", "value": "按年订阅" }, - { "label": "按月订阅", "value": "按月订阅" }, - { "label": "按量计费", "value": "按量计费" }, - { "label": "私有化部署", "value": "私有化部署" }, - { "label": "其他", "value": "其他" } - ] - } - } - }, - "行业覆盖": { - "label": "行业覆盖", - "view": { - "name": "tags", - "props": { - "value": ":industries" - } - }, - "edit": { - "type": "select", - "props": { - "value": ":industries", - "inputable": true, - "multiple": true, - "options": [ - { "label": "房地产", "value": "房地产" }, - { "label": "旅游", "value": "旅游" }, - { "label": "教育", "value": "教育" }, - { "label": "互联网", "value": "互联网" }, - { "label": "其他", "value": "其他" } - ] - } - } - }, - "状态": { - "label": "状态", - "view": { - "name": "tags", - "props": { - "value": ":status", - "options": [ - { "label": "开启", "value": "enabled", "color": "primary" }, - { "label": "关闭", "value": "disabled", "color": "danger" } - ] - } - }, - "edit": { - "type": "select", - "props": { - "value": ":status", - "options": [ - { "label": "开启", "value": "enabled" }, - { "label": "关闭", "value": "disabled" } - ] - } - } - }, - "创建时间": { - "label": "创建时间", - "view": { - "name": "label", - "props": { - "value": ":created_at", - "datetime-format": "YYYY年MM月DD日 @hh:mm:ss" - } - } - }, - "更新时间": { - "label": "更新时间", - "view": { - "name": "label", - "props": { - "value": ":update_at", - "datetime-format": "YYYY年MM月DD日 @hh:mm:ss" - } - } - }, - "查询排序": { - "label": "查询排序", - "view": { - "type": "label", - "props": { - "value": ":rank" - } - }, - "edit": { - "type": "input", - "props": { - "value": ":rank", - "type": "number" - } - } - } - }, - "filters": {}, - "list": { - "primary": "id", - "layout": { - "columns": [ - { "name": "服务名称", "width": 6 }, - { "name": "所属厂商", "width": 6 }, - { "name": "服务类型", "width": 4 }, - { "name": "状态", "width": 4 }, - { "name": "服务领域", "width": 4 }, - { "name": "计费方式", "width": 4 }, - { "name": "创建时间", "width": 6 }, - { "name": "更新时间", "width": 6 } - ], - "filters": [] - }, - "actions": {} - }, - "edit": { - "primary": "id", - "layout": { - "fieldset": [ - { - "title": "基础信息", - "description": "", - "columns": [ - { "name": "服务名称", "width": 6 }, - { "name": "所属厂商", "width": 6 }, - { "name": "服务类型", "width": 6 }, - { "name": "状态", "width": 6 }, - { "name": "rank", "width": 6 } - ] - }, - { - "title": "数据标签", - "description": "", - "columns": ["服务领域", "计费方式", "行业覆盖"] - } - ] - }, - "actions": { - "cancel": {}, - "save": { - "type": "button", - "props": { - "label": "保存" - } - }, - "delete": {} - } - }, - "insert": {}, - "view": {} -} diff --git a/tests/tasks/mail.task.json b/tests/tasks/mail.task.json deleted file mode 100644 index 5bf7bafd..00000000 --- a/tests/tasks/mail.task.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "发送邮件", - "worker_nums": "$ENV.SEND_MAIL_WORKER_NUMS", - "attempts": 3, - "attempt_after": 200, - "timeout": 2, - "size": 1000, - "process": "scripts.mail.Send", - "event": { - "next": "scripts.mail.NextID", - "add": "scripts.mail.OnAdd", - "success": "scripts.mail.OnSuccess", - "error": "scripts.mail.OnError", - "progress": "scripts.mail.OnProgress" - } -} diff --git a/tests/websockets/message.ws.json b/tests/websockets/message.ws.json deleted file mode 100644 index 0ef04577..00000000 --- a/tests/websockets/message.ws.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "A WebSocket client", - "description": "A Chat WebSocket client", - "version": "0.10.0", - "url": "ws://127.0.0.1:5011/websocket/message", - "protocols": ["yao-message-01"], - "buffer": { "read": 1024, "write": 1024 }, - "limit": { "read-wait": 10, "pong-wait": 20, "max-message": 1024 }, - "timeout": 5, - "attempt_after": 200, - "attempts": 9, - "event": { - "data": "scripts.websocket.onData", - "closed": "scripts.websocket.onClosed", - "connected": "scripts.websocket.onConnected", - "error": "scripts.websocket.onError" - } -} diff --git a/tests/widgets/README.md b/tests/widgets/README.md deleted file mode 100644 index 18be262f..00000000 --- a/tests/widgets/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Widgets - -## a customize low-code modules diff --git a/tests/widgets/dyform/README.md b/tests/widgets/dyform/README.md deleted file mode 100644 index 0c3894f7..00000000 --- a/tests/widgets/dyform/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# DyForm - -## A form widget. users can design forms online diff --git a/tests/widgets/dyform/assets/README.md b/tests/widgets/dyform/assets/README.md deleted file mode 100644 index 226d49ab..00000000 --- a/tests/widgets/dyform/assets/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Assets - -1. The assets folder's file will be copied to the `ui/.widgets//` folder. -2. If the folder does not exist will be created. To upgrade the assets, remove the `ui/.widgets//` folder. -3. The asset file PUBLIC URL is `http(s):///.widgets//`, ANYONE CAN VISIT DIRECTLY. diff --git a/tests/widgets/dyform/compile.js b/tests/widgets/dyform/compile.js deleted file mode 100644 index 308b0c31..00000000 --- a/tests/widgets/dyform/compile.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * The DSL compiler. - * Translate the customize DSL to Models, Processes, Flows, Tables, etc. - */ - -/** - * Source - * Where to get the source of DSL - */ -function Source() { - return dyforms(); -} - -/** - * Compile - * Translate or extend the customize DSL - * @param {*} dsl - */ -function Compile(name, dsl) { - let newdsl = { name: name }; - return newdsl; -} - -/** - * OnLoad - * When the widget instance are loaded, the function will be called. - * For preparing the sources the widget need. - * @param {DSL} dsl - */ -function OnLoad(name, dsl) { - log.Info("[Widget] dyform.OnLoad %s %v", name, dsl); -} - -/** - * Migrate - * When the migrate command executes, the function will be called - * @param {DSL} dsl - * @param {bool} force - */ -function Migrate(dsl, force) {} - -/** - * Customize DSL - * @returns - */ -function dyforms() { - return { - pad: { - guard: "bearer-jwt", - title: "应用表单", - sn: "PAD1024", - orign: "手机平板", - columns: [ - { - name: "门店", - field: "store_id", - searchable: true, - props: { - value: ":store_id", - type: "SelectStore", - title: "门店", - placeholder: "请选择门店", - validation: [ - { type: "Required" }, - { type: "StoreStatus", args: [":store_id"] }, - ], - }, - }, - { - name: "订单数量", - field: "orders_amount", - searchable: true, - props: { - value: ":orders_amount", - type: "Number", - title: "订单数量", - placeholder: "今日订单数量", - computed: "OrderAmount", - validation: [ - { type: "Required" }, - { type: "IsNumber", args: [":orders_amount"] }, - ], - }, - }, - { - name: "账单地址", - field: "address", - props: { - value: ":address", - type: "Text", - title: "账单地址", - placeholder: "填写账单地址", - validation: [ - { type: "Required" }, - { type: "MaxLength", args: [":address", 200] }, - ], - }, - }, - ], - filters: [], - "list-layout": { - columns: [{ name: "store_id" }, { name: "orders_amount" }], - filters: [{ name: "订单数量" }], - }, - }, - }; -} diff --git a/tests/widgets/dyform/export.js b/tests/widgets/dyform/export.js deleted file mode 100644 index 40f4b658..00000000 --- a/tests/widgets/dyform/export.js +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Export Models, APIs, Flows, Tables, Tasks, Schedules, etc. - */ - -/** - * Export Apis - * @param {*} dsl - * @returns - */ -function Apis() { - let apis = {}; - apis[`dyform`] = dyformAPI(); - return apis; -} - -/** - * Export Models - * @param {*} dsl - * @returns - */ -function Models(name, dsl) { - let models = {}; - models[`dyform.${name}`] = dyformModel(name); - return models; -} - -/** - * Export Flows - * @param {*} dsl - * @returns - */ -function Flows(name, dsl) { - return [{}]; -} - -/** - * Export Tables - * @param {*} dsl - * @returns - */ -function Tables(name, dsl) { - let tables = {}; - tables[`dyform.${name}`] = dyformTable(name); - return tables; -} - -/** - * Export Tasks - * @param {*} dsl - * @returns - */ -function Tasks(name, dsl) { - let tasks = {}; - tasks[`dyform.${name}`] = dyformTask(name); - return tasks; -} - -/** - * Export Schedules - * @param {*} dsl - * @returns - */ -function Schedules(name, dsl) { - let schedules = {}; - schedules[`dyform.${name}`] = dyformSchedule(name); - return schedules; -} - -function dyformSchedule(name) { - return { - name: `Schedule ${name}`, - schedule: "*/1 * * * *", - process: `widgets.dyform.Save`, - args: [name], - }; -} - -function dyformTask(name) { - return { - name: `Task ${name}`, - worker_nums: 2, - attempts: 3, - attempt_after: 200, - timeout: 2, - size: 1000, - process: `widgets.dyform.Save`, - args: [name], - }; -} - -function dyformAPI() { - return { - name: `dyform API`, - version: "1.0.0", - description: "dyform APIs", - group: "dyform", - guard: "-", - paths: [ - { - path: "/:name/search", - method: "GET", - process: `widgets.dyform.Save`, - in: ["$param.name", ":query"], - out: { status: 200, type: "application/json" }, - }, - ], - }; -} - -function dyformTable(name) { - return { - name: `dyform_${name}`, - version: "1.0.0", - decription: "云服务库", - bind: { model: `dyform.${name}` }, - apis: {}, - columns: {}, - filters: {}, - list: { - primary: "id", - layout: { columns: [{ name: "ID", width: 6 }], filters: [] }, - actions: {}, - }, - edit: { - primary: "id", - layout: { fieldset: [{ columns: [{ name: "ID", width: 6 }] }] }, - actions: { cancel: {} }, - }, - insert: {}, - view: {}, - }; -} - -function dyformModel(name) { - return { - table: { name: `dyform_${name}` }, - columns: [ - { label: "DYFORM ID", name: "id", type: "ID" }, - { label: "SN", name: "sn", type: "string", length: 20, unique: true }, - { label: "NAME", name: "name", type: "string", length: 200, index: true }, - { label: "SOURCE", name: "source", type: "JSON", nullable: true }, - { - label: "TITLE", - name: "title", - type: "string", - length: 200, - index: true, - }, - ], - indexes: [], - }; -} diff --git a/tests/widgets/dyform/helper.js b/tests/widgets/dyform/helper.js deleted file mode 100644 index 7c474b22..00000000 --- a/tests/widgets/dyform/helper.js +++ /dev/null @@ -1,3 +0,0 @@ -function Foo(param) { - return `${param} World`; -} diff --git a/tests/widgets/dyform/process.js b/tests/widgets/dyform/process.js deleted file mode 100644 index 4f41fb2f..00000000 --- a/tests/widgets/dyform/process.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Export the widget processes - * Export() defined witch function in this file will be registered as YAO PROCESS - * The process name is .. - * The processes can be used in compile.js and export.js DIRECTLY - */ - -/** - * SchemaSave - * Save the schema of dyform - * @name: dyform..Save - * @param {String} instance the instance name - * @param {*} payload - */ -function SchemaSave(instance, payload) { - return { instance: instance, payload: payload }; -} - -/** - * Export processes - */ -function Export() { - return { Save: "SchemaSave" }; -} diff --git a/tests/widgets/dyform/widget.json b/tests/widgets/dyform/widget.json deleted file mode 100644 index e19b1256..00000000 --- a/tests/widgets/dyform/widget.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "Dynamic Form", - "description": "A form widget. users can design forms online", - "version": "0.1.0", - "root": "dyforms", - "extension": ".form.json", - "modules": ["Apis", "Models", "Tables", "Tasks", "Schedules"] -} diff --git a/tests/workflows/assign.wflow.json b/tests/workflows/assign.wflow.json deleted file mode 100644 index f688372a..00000000 --- a/tests/workflows/assign.wflow.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "label": "指派商务负责人", - "version": "1.0.0", - "description": "指派商务负责人", - "apis": { - "/confirm": { - "label": "确认", - "process": "xiang.assign.confirm" - }, - "/save": { - "label": "保存", - "process": "xiang.assign.save" - }, - "/terminal": { - "label": "关闭", - "process": "xiang.assign.terminal" - }, - "/submit": { - "label": "提交审批", - "process": "flows.assign.submit" - }, - "/reject/reset": { - "label": "审批驳回并重填", - "process": "flows.assign.reject.rest" - }, - "/reject/terminal": { - "label": "审批驳回并关闭", - "process": "flows.assign.reject.terminal" - }, - "/resolve": { - "label": "审批同意", - "process": "flows.assign.resolve" - } - }, - "actions": { - "关闭": { "@": "assign.关闭" }, - "保存": { "@": "assign.保存" }, - "提交": { "@": "assign.提交" }, - "同意": { "@": "assign.同意" }, - "驳回并重填": { "@": "assign.驳回并重填" }, - "驳回并关闭": { "@": "assign.驳回并关闭" } - }, - "nodes": [ - { - "name": "选择商务负责人", - "body": { - "type": "form", - "props": { "name": "assign" } - }, - "actions": ["关闭", "保存", "提交"], - "user": { "process": "flows.current.user", "args": ["id"] } - }, - { - "name": "项目负责人审批", - "body": { - "type": "iframe", - "props": { "src": "/workflows/assign/approve.html" } - }, - "actions": ["驳回并重填", "驳回并关闭", "同意"], - "user": { "process": "flows.select.manager", "args": [2] }, - "next": [ - { - "when": [{ "审批通过": "{{$data.审批结果}}", "=": "通过" }], - "goto": "审批通过" - }, - { - "when": [{ "审批通过": "{{$data.审批结果}}", "=": "驳回" }], - "goto": "审批驳回" - } - ] - }, - { - "name": "审批通过", - "body": { - "type": "markdown", - "props": { - "content": "审批通过, {{$data.项目名称}}商务负责人为: **{{$data.商务负责人名称}}**" - } - }, - "actions": ["关闭"], - "user": { "process": "flows.current.user", "args": ["id"] } - }, - { - "name": "审批驳回", - "body": { - "type": "markdown", - "props": { - "content": "您的请求被驳回, 驳回原因: **{{$data.驳回原因}}**" - } - }, - "actions": ["关闭"], - "user": { "process": "flows.current.user", "args": ["id"] } - } - ] -} diff --git a/tests/xiang/icons/icon.icns b/tests/xiang/icons/icon.icns deleted file mode 100644 index b4298ead..00000000 Binary files a/tests/xiang/icons/icon.icns and /dev/null differ diff --git a/tests/xiang/icons/icon.ico b/tests/xiang/icons/icon.ico deleted file mode 100644 index 8c749ab8..00000000 Binary files a/tests/xiang/icons/icon.ico and /dev/null differ diff --git a/tests/xiang/icons/icon.png b/tests/xiang/icons/icon.png deleted file mode 100644 index 6cae97c4..00000000 Binary files a/tests/xiang/icons/icon.png and /dev/null differ diff --git a/user/captcha.go b/user/captcha.go deleted file mode 100644 index ceaa5842..00000000 --- a/user/captcha.go +++ /dev/null @@ -1,134 +0,0 @@ -package user - -import ( - "errors" - "image/color" - - "github.com/mojocn/base64Captcha" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/kun/log" -) - -var captchaStore = base64Captcha.DefaultMemStore - -// CaptchaOption 验证码配置 -type CaptchaOption struct { - Type string - Height int - Width int - Length int - Lang string - Background string -} - -// NewCaptchaOption 创建验证码配置 -func NewCaptchaOption() CaptchaOption { - return CaptchaOption{ - Width: 240, - Height: 80, - Length: 4, - Lang: "zh", - Background: "#FFFFFF", - } -} - -// MakeCaptcha 制作验证码 -func MakeCaptcha(option CaptchaOption) (string, string) { - - if option.Width == 0 { - option.Width = 240 - } - - if option.Height == 0 { - option.Width = 80 - } - - if option.Length == 0 { - option.Length = 4 - } - - if option.Lang == "" { - option.Lang = "zh" - } - - var driver base64Captcha.Driver - switch option.Type { - case "audio": - driver = base64Captcha.NewDriverAudio(option.Length, option.Lang) - break - case "math": - background := background(option.Background) - driver = base64Captcha.NewDriverMath( - option.Height, option.Width, 3, - base64Captcha.OptionShowHollowLine, background, - base64Captcha.DefaultEmbeddedFonts, []string{}, - ) - break - default: - driver = base64Captcha.NewDriverDigit( - option.Height, option.Width, 5, - 0.7, 80, - ) - break - } - - c := base64Captcha.NewCaptcha(driver, captchaStore) - id, content, err := c.Generate() - if err != nil { - exception.New("生成验证码出错 %s", 500, err).Throw() - } - - log.Debug("图形/音频 ID:%s 验证码:%s", id, captchaStore.Get(id, false)) - return id, content -} - -// ValidateCaptcha 校验验证码 -func ValidateCaptcha(id string, value string) bool { - return captchaStore.Verify(id, value, true) -} - -func background(s string) *color.RGBA { - if s == "" { - s = "#555555" - } - bg, err := parseHexColorFast(s) - if err != nil { - exception.New("背景色格式错误 %s", 400, s).Throw() - } - return &bg -} - -func parseHexColorFast(s string) (c color.RGBA, err error) { - c.A = 0xff - - if s[0] != '#' { - return c, errors.New("invalid format") - } - - hexToByte := func(b byte) byte { - switch { - case b >= '0' && b <= '9': - return b - '0' - case b >= 'a' && b <= 'f': - return b - 'a' + 10 - case b >= 'A' && b <= 'F': - return b - 'A' + 10 - } - err = errors.New("invalid format") - return 0 - } - - switch len(s) { - case 7: - c.R = hexToByte(s[1])<<4 + hexToByte(s[2]) - c.G = hexToByte(s[3])<<4 + hexToByte(s[4]) - c.B = hexToByte(s[5])<<4 + hexToByte(s[6]) - case 4: - c.R = hexToByte(s[1]) * 17 - c.G = hexToByte(s[2]) * 17 - c.B = hexToByte(s[3]) * 17 - default: - err = errors.New("invalid format") - } - return -} diff --git a/user/captcha_test.go b/user/captcha_test.go deleted file mode 100644 index 7c5f604c..00000000 --- a/user/captcha_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package user - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCaptcha(t *testing.T) { - id, content := MakeCaptcha(CaptchaOption{ - Type: "audio", - Width: 240, - Height: 80, - Length: 4, - Lang: "zh", - }) - assert.IsType(t, "string", id) - assert.IsType(t, "string", content) - captchaStore.Get(id, false) - assert.True(t, ValidateCaptcha(id, captchaStore.Get(id, false))) - - id, content = MakeCaptcha(CaptchaOption{ - Type: "math", - Width: 240, - Height: 80, - Length: 4, - Lang: "zh", - }) - assert.IsType(t, "string", id) - assert.IsType(t, "string", content) - captchaStore.Get(id, false) - assert.True(t, ValidateCaptcha(id, captchaStore.Get(id, false))) - - id, content = MakeCaptcha(CaptchaOption{ - Type: "digit", - Width: 240, - Height: 80, - Length: 4, - Lang: "zh", - }) - assert.IsType(t, "string", id) - assert.IsType(t, "string", content) - captchaStore.Get(id, false) - assert.True(t, ValidateCaptcha(id, captchaStore.Get(id, false))) -} diff --git a/user/process.go b/user/process.go deleted file mode 100644 index ba89385f..00000000 --- a/user/process.go +++ /dev/null @@ -1,80 +0,0 @@ -package user - -import ( - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/any" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/kun/log" - "github.com/yaoapp/kun/maps" -) - -func init() { - gou.RegisterProcessHandler("xiang.user.Captcha", ProcessCaptcha) - gou.RegisterProcessHandler("xiang.user.Login", ProcessLogin) -} - -// ProcessLogin xiang.user.Login 用户登录 -func ProcessLogin(process *gou.Process) interface{} { - process.ValidateArgNums(1) - payload := process.ArgsMap(0).Dot() - log.With(log.F{"payload": payload}).Debug("ProcessLogin") - - id := any.Of(payload.Get("captcha.id")).CString() - value := any.Of(payload.Get("captcha.code")).CString() - if id == "" { - exception.New("请输入验证码ID", 400).Ctx(maps.Map{"id": id, "code": value}).Throw() - } - if value == "" { - exception.New("请输入验证码", 400).Ctx(maps.Map{"id": id, "code": value}).Throw() - } - if !ValidateCaptcha(id, value) { - log.With(log.F{"id": id, "code": value}).Debug("ProcessLogin") - exception.New("验证码不正确", 403).Ctx(maps.Map{"id": id, "code": value}).Throw() - return nil - } - - email := any.Of(payload.Get("email")).CString() - mobile := any.Of(payload.Get("mobile")).CString() - password := any.Of(payload.Get("password")).CString() - if email != "" { - return Auth("email", email, password) - } else if mobile != "" { - return Auth("mobile", mobile, password) - } - - exception.New("参数错误", 400).Ctx(payload).Throw() - return nil -} - -// ProcessCaptcha xiang.user.Captcha 验证码 -func ProcessCaptcha(process *gou.Process) interface{} { - process.ValidateArgNums(1) - option := CaptchaOption{ - Width: any.Of(process.ArgsURLValue(0, "width", "240")).CInt(), - Height: any.Of(process.ArgsURLValue(0, "height", "80")).CInt(), - Length: any.Of(process.ArgsURLValue(0, "height", "4")).CInt(), - Type: process.ArgsURLValue(0, "type", "math"), - Background: process.ArgsURLValue(0, "background", "#FFFFFF"), - Lang: process.ArgsURLValue(0, "lang", "zh"), - } - id, content := MakeCaptcha(option) - return maps.Map{ - "id": id, - "content": content, - } -} - -// ProcessToken xiang.user.Token 使用 Key & Secret 换取 Token -func ProcessToken(process *gou.Process) interface{} { - return nil -} - -// ProcessTokenRefresh xiang.user.TokenRefresh 刷新Token -func ProcessTokenRefresh(process *gou.Process) interface{} { - return nil -} - -// ProcessInfo xiang.user.Info 读取当前用户资料 -func ProcessInfo(process *gou.Process) interface{} { - return nil -} diff --git a/user/user.go b/user/user.go deleted file mode 100644 index df29f11f..00000000 --- a/user/user.go +++ /dev/null @@ -1,85 +0,0 @@ -package user - -import ( - "time" - - "github.com/golang-jwt/jwt" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/session" - "github.com/yaoapp/kun/any" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/kun/maps" - "github.com/yaoapp/yao/helper" - "golang.org/x/crypto/bcrypt" -) - -// JwtClaims JWT claims -type JwtClaims struct { - ID int - Type string - Name string - jwt.StandardClaims -} - -var loginTypes = map[string]string{ - "email": "email", - "mobile": "mobile", -} - -// Auth 用户身份鉴权 -func Auth(field string, value string, password string) maps.Map { - column, has := loginTypes[field] - if !has { - exception.New("登录方式(%s)尚未支持", 400, field).Throw() - } - - user := gou.Select("xiang.user") - rows, err := user.Get(gou.QueryParam{ - Select: []interface{}{"id", "password", "name", "type", "email", "mobile", "extra"}, - Limit: 1, - Wheres: []gou.QueryWhere{ - {Column: column, Value: value}, - {Column: "status", Value: "enabled"}, - }, - }) - - if err != nil { - exception.New("数据库查询错误", 500, field).Throw() - } - - if len(rows) == 0 { - exception.New("用户不存在(%s)", 404, value).Throw() - } - - row := rows[0] - passwordHash := row.Get("password").(string) - row.Del("password") - - err = bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(password)) - if err != nil { - exception.New("登录密码错误", 403, value).Throw() - } - - expiresAt := time.Now().Unix() + 3600 - - // token := MakeToken(row, expiresAt) - sid := session.ID() - id := any.Of(row.Get("id")).CInt() - token := helper.JwtMake(id, map[string]interface{}{}, map[string]interface{}{ - "expires_at": expiresAt, - "sid": sid, - "issuer": "xiang", - }) - session.Global().Expire(time.Duration(token.ExpiresAt)*time.Second).ID(sid).Set("user_id", id) - session.Global().ID(sid).Set("user", row) - session.Global().ID(sid).Set("issuer", "xiang") - - // 读取菜单 - menus := gou.NewProcess("flows.xiang.menu").Run() - return maps.Map{ - "expires_at": token.ExpiresAt, - "token": token.Token, - "user": row, - "menus": menus, - } -} diff --git a/utils/datetime/now.go b/utils/datetime/now.go index 14943978..148f225e 100644 --- a/utils/datetime/now.go +++ b/utils/datetime/now.go @@ -3,30 +3,30 @@ package datetime import ( "time" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) // ProcessTimestamp utils.now.Timestamp -func ProcessTimestamp(process *gou.Process) interface{} { +func ProcessTimestamp(process *process.Process) interface{} { return time.Now().Unix() } // ProcessTimestampms utils.now.Timestampms -func ProcessTimestampms(process *gou.Process) interface{} { +func ProcessTimestampms(process *process.Process) interface{} { return time.Now().UnixMilli() } // ProcessDate utils.now.Date -func ProcessDate(process *gou.Process) interface{} { +func ProcessDate(process *process.Process) interface{} { return time.Now().Local().Format("2006-01-02") } // ProcessTime utils.now.Time -func ProcessTime(process *gou.Process) interface{} { +func ProcessTime(process *process.Process) interface{} { return time.Now().Local().Format("15:04:05") } // ProcessDateTime utils.now.DateTime -func ProcessDateTime(process *gou.Process) interface{} { +func ProcessDateTime(process *process.Process) interface{} { return time.Now().Local().Format("2006-01-02 15:04:05") } diff --git a/utils/datetime_test.go b/utils/datetime_test.go index 52d9bbf8..331061f9 100644 --- a/utils/datetime_test.go +++ b/utils/datetime_test.go @@ -5,13 +5,13 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestProcessNow(t *testing.T) { - assert.LessOrEqual(t, time.Now().Unix(), gou.NewProcess("utils.now.Timestamp").Run().(int64)) - assert.LessOrEqual(t, time.Now().UnixMilli(), gou.NewProcess("utils.now.Timestampms").Run().(int64)) - assert.NotNil(t, gou.NewProcess("utils.now.Date").Run()) - assert.NotNil(t, gou.NewProcess("utils.now.Time").Run()) - assert.NotNil(t, gou.NewProcess("utils.now.DateTime").Run()) + assert.LessOrEqual(t, time.Now().Unix(), process.New("utils.now.Timestamp").Run().(int64)) + assert.LessOrEqual(t, time.Now().UnixMilli(), process.New("utils.now.Timestampms").Run().(int64)) + assert.NotNil(t, process.New("utils.now.Date").Run()) + assert.NotNil(t, process.New("utils.now.Time").Run()) + assert.NotNil(t, process.New("utils.now.DateTime").Run()) } diff --git a/utils/process.go b/utils/process.go index db9cd750..e0f05b12 100644 --- a/utils/process.go +++ b/utils/process.go @@ -1,87 +1,88 @@ package utils import ( - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/yao/utils/datetime" "github.com/yaoapp/yao/utils/str" "github.com/yaoapp/yao/utils/tree" ) func init() { - gou.AliasProcess("xiang.helper.Captcha", "yao.utils.Captcha") // deprecated - gou.AliasProcess("xiang.helper.CaptchaValidate", "yao.utils.CaptchaValidate") // deprecated + process.Alias("xiang.helper.Captcha", "yao.utils.Captcha") // deprecated + process.Alias("xiang.helper.CaptchaValidate", "yao.utils.CaptchaValidate") // deprecated // **************************************** // * Migrate Processes Version 0.10.2+ // **************************************** // Application - gou.AliasProcess("xiang.main.Ping", "utils.app.Ping") - gou.AliasProcess("xiang.main.Inspect", "utils.app.Inspect") + process.Alias("xiang.main.Ping", "utils.app.Ping") + process.Alias("xiang.main.Inspect", "utils.app.Inspect") // FMT - gou.AliasProcess("xiang.helper.Print", "utils.fmt.Print") + process.Alias("xiang.helper.Print", "utils.fmt.Print") // ENV - gou.AliasProcess("xiang.helper.EnvSet", "utils.env.Get") - gou.AliasProcess("xiang.helper.EnvGet", "utils.env.Get") - gou.AliasProcess("xiang.helper.EnvMultiSet", "utils.env.SetMany") - gou.AliasProcess("xiang.helper.EnvMultiGet", "utils.env.GetMany") + process.Alias("xiang.helper.EnvSet", "utils.env.Get") + process.Alias("xiang.helper.EnvGet", "utils.env.Get") + process.Alias("xiang.helper.EnvMultiSet", "utils.env.SetMany") + process.Alias("xiang.helper.EnvMultiGet", "utils.env.GetMany") // Flow - gou.AliasProcess("xiang.helper.For", "utils.flow.For") - gou.AliasProcess("xiang.helper.Each", "utils.flow.Each") - gou.AliasProcess("xiang.helper.Case", "utils.flow.Case") - gou.AliasProcess("xiang.helper.IF", "utils.flow.IF") - gou.AliasProcess("xiang.helper.Throw", "utils.flow.Throw") - gou.AliasProcess("xiang.helper.Return", "utils.flow.Return") + process.Alias("xiang.helper.For", "utils.flow.For") + process.Alias("xiang.helper.Each", "utils.flow.Each") + process.Alias("xiang.helper.Case", "utils.flow.Case") + process.Alias("xiang.helper.IF", "utils.flow.IF") + process.Alias("xiang.helper.Throw", "utils.flow.Throw") + process.Alias("xiang.helper.Return", "utils.flow.Return") // JWT - gou.AliasProcess("xiang.helper.JwtMake", "utils.jwt.Make") - gou.AliasProcess("xiang.helper.JwtValidate", "utils.jwt.Verify") + process.Alias("xiang.helper.JwtMake", "utils.jwt.Make") + process.Alias("xiang.helper.JwtValidate", "utils.jwt.Verify") // Password // utils.pwd.Hash - gou.AliasProcess("xiang.helper.PasswordValidate", "utils.pwd.Verify") + process.Alias("xiang.helper.PasswordValidate", "utils.pwd.Verify") // Captcha - gou.AliasProcess("xiang.helper.Captcha", "utils.captcha.Make") - gou.AliasProcess("xiang.helper.CaptchaValidate", "utils.captcha.Verify") + process.Alias("xiang.helper.Captcha", "utils.captcha.Make") + process.Alias("xiang.helper.CaptchaValidate", "utils.captcha.Verify") // String - gou.AliasProcess("xiang.helper.StrConcat", "utils.str.Concat") - gou.RegisterProcessHandler("utils.str.Join", str.ProcessJoin) - gou.RegisterProcessHandler("utils.str.JoinPath", str.ProcessJoinPath) + process.Alias("xiang.helper.StrConcat", "utils.str.Concat") + process.Alias("xiang.helper.HexToString", "utils.str.Hex") + process.Register("utils.str.Join", str.ProcessJoin) + process.Register("utils.str.JoinPath", str.ProcessJoinPath) // Array - gou.AliasProcess("xiang.helper.ArrayPluck", "utils.arr.Pluck") - gou.AliasProcess("xiang.helper.ArraySplit", "utils.arr.Split") - gou.AliasProcess("xiang.helper.ArrayTree", "utils.arr.Tree") - gou.AliasProcess("xiang.helper.ArrayUnique", "utils.arr.Unique") - gou.AliasProcess("xiang.helper.ArrayIndexes", "utils.arr.Indexes") - gou.AliasProcess("xiang.helper.ArrayGet", "utils.arr.Get") - gou.AliasProcess("xiang.helper.ArrayColumn", "utils.arr.Column") // doc - gou.AliasProcess("xiang.helper.ArrayKeep", "utils.arr.Keep") - gou.AliasProcess("xiang.helper.ArrayMapSet", "utils.arr.MapSet") + process.Alias("xiang.helper.ArrayPluck", "utils.arr.Pluck") + process.Alias("xiang.helper.ArraySplit", "utils.arr.Split") + process.Alias("xiang.helper.ArrayTree", "utils.arr.Tree") + process.Alias("xiang.helper.ArrayUnique", "utils.arr.Unique") + process.Alias("xiang.helper.ArrayIndexes", "utils.arr.Indexes") + process.Alias("xiang.helper.ArrayGet", "utils.arr.Get") + process.Alias("xiang.helper.ArrayColumn", "utils.arr.Column") // doc + process.Alias("xiang.helper.ArrayKeep", "utils.arr.Keep") + process.Alias("xiang.helper.ArrayMapSet", "utils.arr.MapSet") // Tree - gou.RegisterProcessHandler("utils.tree.Flatten", tree.ProcessFlatten) + process.Register("utils.tree.Flatten", tree.ProcessFlatten) // Map - gou.AliasProcess("xiang.helper.MapGet", "utils.map.Get") - gou.AliasProcess("xiang.helper.MapSet", "utils.map.Set") - gou.AliasProcess("xiang.helper.MapDel", "utils.map.Del") - gou.AliasProcess("xiang.helper.MapDel", "utils.map.DelMany") - gou.AliasProcess("xiang.helper.MapKeys", "utils.map.Keys") - gou.AliasProcess("xiang.helper.MapValues", "utils.map.Values") - gou.AliasProcess("xiang.helper.MapToArray", "utils.map.Array") // doc + process.Alias("xiang.helper.MapGet", "utils.map.Get") + process.Alias("xiang.helper.MapSet", "utils.map.Set") + process.Alias("xiang.helper.MapDel", "utils.map.Del") + process.Alias("xiang.helper.MapDel", "utils.map.DelMany") + process.Alias("xiang.helper.MapKeys", "utils.map.Keys") + process.Alias("xiang.helper.MapValues", "utils.map.Values") + process.Alias("xiang.helper.MapToArray", "utils.map.Array") // doc // utils.map.Merge // Time - gou.AliasProcess("xiang.flow.Sleep", "utils.time.Sleep") - gou.RegisterProcessHandler("utils.now.Time", datetime.ProcessTime) - gou.RegisterProcessHandler("utils.now.Date", datetime.ProcessDate) - gou.RegisterProcessHandler("utils.now.DateTime", datetime.ProcessDateTime) - gou.RegisterProcessHandler("utils.now.Timestamp", datetime.ProcessTimestamp) - gou.RegisterProcessHandler("utils.now.Timestampms", datetime.ProcessTimestampms) + process.Alias("xiang.flow.Sleep", "utils.time.Sleep") + process.Register("utils.now.Time", datetime.ProcessTime) + process.Register("utils.now.Date", datetime.ProcessDate) + process.Register("utils.now.DateTime", datetime.ProcessDateTime) + process.Register("utils.now.Timestamp", datetime.ProcessTimestamp) + process.Register("utils.now.Timestampms", datetime.ProcessTimestampms) } diff --git a/utils/str/str.go b/utils/str/str.go index 04459d84..80515c71 100644 --- a/utils/str/str.go +++ b/utils/str/str.go @@ -5,11 +5,11 @@ import ( "path/filepath" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) // ProcessJoin utils.str.Join -func ProcessJoin(process *gou.Process) interface{} { +func ProcessJoin(process *process.Process) interface{} { process.ValidateArgNums(2) args := process.ArgsArray(0) sep := process.ArgsString(1) @@ -21,7 +21,7 @@ func ProcessJoin(process *gou.Process) interface{} { } // ProcessJoinPath utils.str.JoinPath -func ProcessJoinPath(process *gou.Process) interface{} { +func ProcessJoinPath(process *process.Process) interface{} { process.ValidateArgNums(2) paths := []string{} for _, arg := range process.Args { diff --git a/utils/str_test.go b/utils/str_test.go index 8c069289..124cbfe8 100644 --- a/utils/str_test.go +++ b/utils/str_test.go @@ -6,16 +6,31 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" + _ "github.com/yaoapp/yao/helper" ) func TestProcessStrJoin(t *testing.T) { - res := gou.NewProcess("utils.str.Join", []interface{}{"FOO", 20, "BAR"}, ",").Run().(string) + res := process.New("utils.str.Join", []interface{}{"FOO", 20, "BAR"}, ",").Run().(string) assert.Equal(t, "FOO,20,BAR", res) } func TestProcessStrJoinPath(t *testing.T) { - res := gou.NewProcess("utils.str.JoinPath", "data", 20, "app").Run().(string) + res := process.New("utils.str.JoinPath", "data", 20, "app").Run().(string) shouldBe := fmt.Sprintf("data%s20%sapp", string(os.PathSeparator), string(os.PathSeparator)) assert.Equal(t, shouldBe, res) } + +func TestProcessStrHex(t *testing.T) { + res, err := process.New("utils.str.Hex", []byte{0x0, 0x1}).Exec() + assert.Nil(t, err) + assert.Equal(t, "0001", res) + + res, err = process.New("utils.str.Hex", string([]byte{0x0, 0x1})).Exec() + assert.Nil(t, err) + assert.Equal(t, "0001", res) + + res, err = process.New("utils.str.Hex", 1024).Exec() + assert.Nil(t, err) + assert.Nil(t, res) +} diff --git a/utils/tree/tree.go b/utils/tree/tree.go index 5906f638..5088085c 100644 --- a/utils/tree/tree.go +++ b/utils/tree/tree.go @@ -3,11 +3,11 @@ package tree import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) // ProcessFlatten utils.tree.Flatten cast to array -func ProcessFlatten(process *gou.Process) interface{} { +func ProcessFlatten(process *process.Process) interface{} { process.ValidateArgNums(1) array := process.ArgsArray(0) option := process.ArgsMap(1, map[string]interface{}{"primary": "id", "children": "children", "parent": "parent"}) diff --git a/utils/tree_test.go b/utils/tree_test.go index ccdf712a..4b1a6a45 100644 --- a/utils/tree_test.go +++ b/utils/tree_test.go @@ -5,7 +5,7 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestProcessTreeFlatten(t *testing.T) { @@ -26,7 +26,7 @@ func TestProcessTreeFlatten(t *testing.T) { t.Fatal(err) } - rows := gou.NewProcess("utils.tree.Flatten", data, map[string]interface{}{"primary": "id", "children": "children", "parent": "parent"}).Run().([]interface{}) + rows := process.New("utils.tree.Flatten", data, map[string]interface{}{"primary": "id", "children": "children", "parent": "parent"}).Run().([]interface{}) assert.Equal(t, 4, len(rows)) assert.Equal(t, float64(1), rows[1].(map[string]interface{})["parent"]) } diff --git a/websocket/websocket.go b/websocket/websocket.go index 860a0c6d..991cf40f 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -1,36 +1,38 @@ package websocket import ( - "fmt" - "path/filepath" - - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/share" ) // Load 加载API func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "websockets") - return LoadFrom(root, "") + // exts := []string{"*.http.yao", "*.http.json", "*.http.jsonc"} + // return application.App.Walk("websockets", func(root, file string, isdir bool) error { + // _, err := websocket.Load(file, share.ID(root, file)) + // return err + // }, exts...) + + // var root = filepath.Join(cfg.Root, "websockets") + // return LoadFrom(root, "") + + return nil } -// LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) error { +// // LoadFrom 从特定目录加载 +// func LoadFrom(dir string, prefix string) error { - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } +// if share.DirNotExists(dir) { +// return fmt.Errorf("%s does not exists", dir) +// } - err := share.Walk(dir, ".ws.json", func(root, filename string) { - name := prefix + share.SpecName(root, filename) - content := share.ReadFile(filename) - _, err := gou.LoadWebSocket(string(content), name) - if err != nil { - log.With(log.F{"root": root, "file": filename}).Error(err.Error()) - } - }) +// err := share.Walk(dir, ".ws.json", func(root, filename string) { +// name := prefix + share.SpecName(root, filename) +// content := share.ReadFile(filename) +// _, err := gou.LoadWebSocket(string(content), name) +// if err != nil { +// log.With(log.F{"root": root, "file": filename}).Error(err.Error()) +// } +// }) - return err -} +// return err +// } diff --git a/websocket/websocket_test.go b/websocket/websocket_test.go index a2b4fd4c..047674cc 100644 --- a/websocket/websocket_test.go +++ b/websocket/websocket_test.go @@ -8,33 +8,26 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" "github.com/yaoapp/gou/websocket" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/script" ) func TestLoad(t *testing.T) { - runtime.Load(config.Conf) Load(config.Conf) - LoadFrom("not a path", "404.") check(t) } func TestWebSocketOpen(t *testing.T) { - runtime.Load(config.Conf) - Load(config.Conf) - script.Load(config.Conf) - srv, url := serve(t) - defer srv.Stop() + // Load(config.Conf) + // script.Load(config.Conf) + // srv, url := serve(t) + // defer srv.Stop() - ws := gou.SelectWebSocket("message") - err := ws.Open(url, "messageV2", "chatV3") - if err != nil { - t.Fatal(err) - } + // ws := websocket.Se("message") + // err := ws.Open(url, "messageV2", "chatV3") + // if err != nil { + // t.Fatal(err) + // } } func serve(t *testing.T) (*websocket.Upgrader, string) { @@ -64,9 +57,9 @@ func serve(t *testing.T) (*websocket.Upgrader, string) { } func check(t *testing.T) { - keys := []string{} - for key := range gou.WebSockets { - keys = append(keys, key) - } - assert.Equal(t, 1, len(keys)) + // keys := []string{} + // for key := range gou.WebSockets { + // keys = append(keys, key) + // } + // assert.Equal(t, 1, len(keys)) } diff --git a/widget/widget.go b/widget/widget.go index 8bd89b0f..7f780c11 100644 --- a/widget/widget.go +++ b/widget/widget.go @@ -1,107 +1,112 @@ package widget import ( - "errors" - "fmt" - "io/ioutil" - "os" "path/filepath" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/application" "github.com/yaoapp/gou/widget" - "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/share" - "github.com/yaoapp/yao/table" ) // Load Widgets func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "widgets") - return LoadFrom(root) -} - -// LoadFrom widget -func LoadFrom(dir string) error { register := moduleRegister() - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - - paths, err := ioutil.ReadDir(dir) - if err != nil { + return application.App.Walk("widgets", func(root, file string, isdir bool) error { + if isdir { + return nil + } + path := filepath.Dir(file) + _, err := widget.Load(path, nil, register) return err - } - for _, path := range paths { + }, "widget.yao", "widget.json", "widget.jsonc") - if !path.IsDir() { - continue - } - - name := path.Name() - if _, err := os.Stat(filepath.Join(dir, name, "widget.json")); errors.Is(err, os.ErrNotExist) { - // path/to/whatever does not exist - continue - } - w, err := gou.LoadWidget(filepath.Join(dir, name), name, register) - if err != nil { - return err - } - - // Load instances - err = w.Load() - if err != nil { - return err - } - } - - return err + // var root = filepath.Join(cfg.Root, "widgets") + // return LoadFrom(root) } +// // LoadFrom widget +// func LoadFrom(dir string) error { + +// register := moduleRegister() + +// if share.DirNotExists(dir) { +// return fmt.Errorf("%s does not exists", dir) +// } + +// paths, err := ioutil.ReadDir(dir) +// if err != nil { +// return err +// } + +// for _, path := range paths { + +// if !path.IsDir() { +// continue +// } + +// name := path.Name() +// if _, err := os.Stat(filepath.Join(dir, name, "widget.json")); errors.Is(err, os.ErrNotExist) { +// // path/to/whatever does not exist +// continue +// } +// w, err := gou.LoadWidget(filepath.Join(dir, name), name, register) +// if err != nil { +// return err +// } + +// // Load instances +// err = w.Load() +// if err != nil { +// return err +// } +// } + +// return err +// } + func moduleRegister() widget.ModuleRegister { return widget.ModuleRegister{ - "Apis": func(name string, source []byte) error { - _, err := gou.LoadAPIReturn(string(source), name) - log.Trace("[Widget] Register api %s", name) - if err != nil { - log.Error("[Widget] Register api %s %v", name, err) - } - return err - }, - "Models": func(name string, source []byte) error { - _, err := gou.LoadModelReturn(string(source), name) - log.Trace("[Widget] Register model %s", name) - if err != nil { - log.Error("[Widget] Register model %s %v", name, err) - } - return err - }, - "Tables": func(name string, source []byte) error { - log.Trace("[Widget] Register table %s", name) - _, err := table.LoadTable(string(source), name) - if err != nil { - log.Error("[Widget] Register table %s %v", name, err) - } - return nil - }, - "Tasks": func(name string, source []byte) error { - log.Trace("[Widget] Register task %s", name) - _, err := gou.LoadTask(string(source), name) - if err != nil { - log.Error("[Widget] Register task %s %v", name, err) - } - return nil - }, - "Schedules": func(name string, source []byte) error { - log.Trace("[Widget] Register schedule %s", name) - _, err := gou.LoadSchedule(string(source), name) - if err != nil { - log.Error("[Widget] Register schedule %s %v", name, err) - } - return nil - }, + // "Apis": func(name string, source []byte) error { + // _, err := api.Load(string(source), name) + // log.Trace("[Widget] Register api %s", name) + // if err != nil { + // log.Error("[Widget] Register api %s %v", name, err) + // } + // return err + // }, + // "Models": func(name string, source []byte) error { + // _, err := model.Load(string(source), name) + // log.Trace("[Widget] Register model %s", name) + // if err != nil { + // log.Error("[Widget] Register model %s %v", name, err) + // } + // return err + // }, + // "Tables": func(name string, source []byte) error { + // log.Trace("[Widget] Register table %s", name) + // _, err := table.LoadTable(string(source), name) + // if err != nil { + // log.Error("[Widget] Register table %s %v", name, err) + // } + // return nil + // }, + // "Tasks": func(name string, source []byte) error { + // log.Trace("[Widget] Register task %s", name) + // _, err := gou.LoadTask(string(source), name) + // if err != nil { + // log.Error("[Widget] Register task %s %v", name, err) + // } + // return nil + // }, + // "Schedules": func(name string, source []byte) error { + // log.Trace("[Widget] Register schedule %s", name) + // _, err := gou.LoadSchedule(string(source), name) + // if err != nil { + // log.Error("[Widget] Register schedule %s %v", name, err) + // } + // return nil + // }, } } diff --git a/widget/widget_test.go b/widget/widget_test.go index 1c45425a..81218369 100644 --- a/widget/widget_test.go +++ b/widget/widget_test.go @@ -4,24 +4,23 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/any" + "github.com/yaoapp/gou/widget" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/share" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - runtime.Load(config.Conf) - share.DBConnect(config.Conf.DB) // 创建数据库连接 - Load(config.Conf) - LoadFrom("not a path") - v, err := gou.NewProcess("widgets.dyform.Save", "pad", "pay").Exec() - if err != nil { - t.Fatal(err) - } + test.Prepare(t, config.Conf) + defer test.Clean() - res := any.Of(v).Map() - assert.Equal(t, "pad", res.Get("instance")) - assert.Equal(t, "pay", res.Get("payload")) + Load(config.Conf) + check(t) +} + +func check(t *testing.T) { + ids := map[string]bool{} + for id := range widget.Widgets { + ids[id] = true + } + assert.True(t, ids["dyform"]) } diff --git a/widgets/action/action_test.go b/widgets/action/action_test.go index 21dcfccd..c836fa00 100644 --- a/widgets/action/action_test.go +++ b/widgets/action/action_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) func TestNewProcess(t *testing.T) { @@ -46,7 +46,7 @@ func testData() map[string]*Process { } } -func testHandler(p *Process, process *gou.Process) (interface{}, error) { +func testHandler(p *Process, process *process.Process) (interface{}, error) { args := p.Args(process) return args, nil } diff --git a/widgets/action/guard.go b/widgets/action/guard.go index 965ce060..3346cde5 100644 --- a/widgets/action/guard.go +++ b/widgets/action/guard.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/gin-gonic/gin" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" "github.com/yaoapp/kun/log" ) @@ -19,11 +19,11 @@ func (p *Process) UseGuard(c *gin.Context, id string) error { } if guard != "" { - if middleware, has := gou.HTTPGuards[guard]; has { + if middleware, has := api.HTTPGuards[guard]; has { middleware(c) continue } - gou.ProcessGuard(guard)(c) + api.ProcessGuard(guard)(c) } } return nil diff --git a/widgets/action/process.go b/widgets/action/process.go index 52bada6e..ecca6798 100644 --- a/widgets/action/process.go +++ b/widgets/action/process.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" @@ -103,7 +103,7 @@ func (p *Process) WithAfter(after *hook.After) *Process { } // Args get the process args -func (p *Process) Args(process *gou.Process) []interface{} { +func (p *Process) Args(process *process.Process) []interface{} { process.ValidateArgNums(1) args := []interface{}{} args = append(args, p.Default...) @@ -122,7 +122,7 @@ func (p *Process) Args(process *gou.Process) []interface{} { } // Exec exec the process -func (p *Process) Exec(process *gou.Process) (interface{}, error) { +func (p *Process) Exec(process *process.Process) (interface{}, error) { if p.Handler == nil { return nil, fmt.Errorf("%s handler does not set", p.Name) } @@ -130,7 +130,7 @@ func (p *Process) Exec(process *gou.Process) (interface{}, error) { } // MustExec exec the process -func (p *Process) MustExec(process *gou.Process) interface{} { +func (p *Process) MustExec(process *process.Process) interface{} { res, err := p.Exec(process) if err != nil { exception.New(err.Error(), 500).Throw() diff --git a/widgets/action/types.go b/widgets/action/types.go index 6050e182..33ceb82f 100644 --- a/widgets/action/types.go +++ b/widgets/action/types.go @@ -1,7 +1,7 @@ package action import ( - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/yao/widgets/hook" ) @@ -19,4 +19,4 @@ type Process struct { } // Handler action handler -type Handler func(p *Process, process *gou.Process) (interface{}, error) +type Handler func(p *Process, process *process.Process) (interface{}, error) diff --git a/widgets/api.go b/widgets/api.go index 1562c4cf..5ada74bf 100644 --- a/widgets/api.go +++ b/widgets/api.go @@ -5,7 +5,7 @@ import ( "os" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" "github.com/yaoapp/yao/widgets/chart" "github.com/yaoapp/yao/widgets/form" "github.com/yaoapp/yao/widgets/list" @@ -35,7 +35,7 @@ func userApis(apis map[string]interface{}) { // Group string `json:"group,omitempty"` // Guard string `json:"guard,omitempty"` // Paths []Path `json:"paths,omitempty"` - for group, api := range gou.APIs { + for group, api := range api.APIs { if strings.HasPrefix(group, "widgets") { continue } @@ -85,7 +85,7 @@ func userApis(apis map[string]interface{}) { func tableApis(apis map[string]interface{}) { - api, has := gou.APIs["widgets.table"] + api, has := api.APIs["widgets.table"] if !has { return } @@ -115,7 +115,7 @@ func tableApis(apis map[string]interface{}) { func formApis(apis map[string]interface{}) { - api, has := gou.APIs["widgets.form"] + api, has := api.APIs["widgets.form"] if !has { return } @@ -138,7 +138,7 @@ func formApis(apis map[string]interface{}) { func listApis(apis map[string]interface{}) { - api, has := gou.APIs["widgets.list"] + api, has := api.APIs["widgets.list"] if !has { return } @@ -158,7 +158,7 @@ func listApis(apis map[string]interface{}) { func chartApis(apis map[string]interface{}) { - api, has := gou.APIs["widgets.chart"] + api, has := api.APIs["widgets.chart"] if !has { return } @@ -173,10 +173,10 @@ func chartApis(apis map[string]interface{}) { } } -func widgetApis(apis map[string]interface{}, api *gou.API, widgetID string, dsl string, groupGuard string, pathGuards []map[string]string) { +func widgetApis(apis map[string]interface{}, apiInst *api.API, widgetID string, dsl string, groupGuard string, pathGuards []map[string]string) { - pathMapping := map[string]gou.Path{} - for _, path := range api.HTTP.Paths { + pathMapping := map[string]api.Path{} + for _, path := range apiInst.HTTP.Paths { pathMapping[path.Path] = path } @@ -203,7 +203,7 @@ func widgetApis(apis map[string]interface{}, api *gou.API, widgetID string, dsl guard = groupGuard } - fullpath := fmt.Sprintf("/apis/%s%s", api.HTTP.Group, path.Path) + fullpath := fmt.Sprintf("/apis/%s%s", apiInst.HTTP.Group, path.Path) paths = append(paths, map[string]interface{}{ "name": path.Label, "description": path.Description, @@ -221,11 +221,11 @@ func widgetApis(apis map[string]interface{}, api *gou.API, widgetID string, dsl apis[dsl] = map[string]interface{}{ "DSL": dsl, - "name": api.HTTP.Name, - "version": api.HTTP.Version, - "group": fmt.Sprintf("/%s", api.HTTP.Group), + "name": apiInst.HTTP.Name, + "version": apiInst.HTTP.Version, + "group": fmt.Sprintf("/%s", apiInst.HTTP.Group), "guard": groupGuard, - "description": api.HTTP.Description, + "description": apiInst.HTTP.Description, "paths": paths, } } diff --git a/widgets/app/app.go b/widgets/app/app.go index 7b86efc4..19956cf4 100644 --- a/widgets/app/app.go +++ b/widgets/app/app.go @@ -2,16 +2,17 @@ package app import ( "fmt" - "io/ioutil" "os" "path/filepath" "regexp" - "strconv" "strings" "time" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/process" + v8 "github.com/yaoapp/gou/runtime/v8" "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" @@ -53,19 +54,40 @@ func LoadAndExport(cfg config.Config) error { // Load the app DSL func Load(cfg config.Config) error { - file := filepath.Join(cfg.Root, "app.json") - file, err := filepath.Abs(file) + // file := filepath.Join(cfg.Root, "app.json") + // file, err := filepath.Abs(file) + // if err != nil { + // return err + // } + + var data []byte + var err error + var file string + application.App.Walk("", func(root, filename string, isdir bool) error { + if isdir { + return nil + } + + data, err = application.App.Read(filename) + if err != nil { + return err + } + + file = filename + return nil + + }, "app.yao", "app.json", "app.jsonc") + if err != nil { return err } - data, err := ioutil.ReadFile(file) - if err != nil { - return err + if data == nil { + return fmt.Errorf("app.yao not found") } dsl := &DSL{Optional: OptionalDSL{}, Lang: cfg.Lang} - err = jsoniter.Unmarshal(data, dsl) + err = application.Parse(file, data, dsl) if err != nil { return err } @@ -90,13 +112,13 @@ func exportAPI() error { return fmt.Errorf("the app does not init") } - http := gou.HTTP{ + http := api.HTTP{ Name: "Widget App API", Description: "Widget App API", Version: share.VERSION, Guard: "bearer-jwt", Group: "__yao/app", - Paths: []gou.Path{}, + Paths: []api.Path{}, } process := "yao.app.Xgen" @@ -104,96 +126,96 @@ func exportAPI() error { process = Setting.Setting } - path := gou.Path{ + path := api.Path{ Label: "App Setting", Description: "App Setting", Guard: "-", Path: "/setting", Method: "GET", Process: process, - In: []string{}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST - path = gou.Path{ + path = api.Path{ Label: "App Setting", Description: "App Setting", Guard: "-", Path: "/setting", Method: "POST", Process: process, - In: []string{":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) process = "yao.app.Menu" - args := []string{} + args := []interface{}{} if Setting.Menu.Process != "" { if Setting.Menu.Args != nil { args = Setting.Menu.Args } } - path = gou.Path{ + path = api.Path{ Label: "App Menu", Description: "App Menu", Path: "/menu", Method: "GET", Process: process, In: args, - Out: gou.Out{Status: 200, Type: "application/json"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) process = "yao.app.Icons" - path = gou.Path{ + path = api.Path{ Label: "App Icons", Description: "App Icons", Path: "/icons/:name", Guard: "-", Method: "GET", Process: process, - In: []string{"$param.name"}, - Out: gou.Out{Status: 200}, + In: []interface{}{"$param.name"}, + Out: api.Out{Status: 200}, } http.Paths = append(http.Paths, path) - path = gou.Path{ + path = api.Path{ Label: "Setup", Description: "Setup", Path: "/setup", Guard: "-", Method: "POST", Process: "yao.app.Setup", - In: []string{":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) - path = gou.Path{ + path = api.Path{ Label: "Check", Description: "Check", Path: "/check", Guard: "-", Method: "POST", Process: "yao.app.Check", - In: []string{":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) - path = gou.Path{ + path = api.Path{ Label: "Serivce", Description: "Serivce", Path: "/service/:name", Guard: "bearer-jwt", Method: "POST", Process: "yao.app.Service", - In: []string{"$param.name", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.name", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) @@ -204,7 +226,7 @@ func exportAPI() error { } // load apis - _, err = gou.LoadAPIReturn(string(source), "widgets.app") + _, err = api.LoadSource(".yao", source, "widgets.app") return err } @@ -215,16 +237,16 @@ func Export() error { } func exportProcess() { - gou.RegisterProcessHandler("yao.app.setting", processSetting) - gou.RegisterProcessHandler("yao.app.xgen", processXgen) - gou.RegisterProcessHandler("yao.app.menu", processMenu) - gou.RegisterProcessHandler("yao.app.icons", processIcons) - gou.RegisterProcessHandler("yao.app.setup", processSetup) - gou.RegisterProcessHandler("yao.app.check", processCheck) - gou.RegisterProcessHandler("yao.app.service", processService) + process.Register("yao.app.setting", processSetting) + process.Register("yao.app.xgen", processXgen) + process.Register("yao.app.menu", processMenu) + process.Register("yao.app.icons", processIcons) + process.Register("yao.app.setup", processSetup) + process.Register("yao.app.check", processCheck) + process.Register("yao.app.service", processService) } -func processService(process *gou.Process) interface{} { +func processService(process *process.Process) interface{} { process.ValidateArgNums(2) service := fmt.Sprintf("__yao_service.%s", process.ArgsString(0)) payload := process.ArgsMap(1) @@ -242,30 +264,30 @@ func processService(process *gou.Process) interface{} { args = v } - req := gou.Yao.New(service, method) - if process.Sid != "" { - req.WithSid(process.Sid) + script, err := v8.Select(service) + if err != nil { + exception.New("services.%s not loaded", 404, process.ArgsString(0)).Throw() + return nil } - res, err := req.Call(args...) + ctx, err := script.NewContext(process.Sid, process.Global) if err != nil { - // parse Exception - code := 500 - message := err.Error() - match := regExcp.FindStringSubmatch(message) - if len(match) > 0 { - code, err = strconv.Atoi(match[1]) - if err == nil { - message = strings.TrimSpace(match[2]) - } - } - exception.New(message, code).Throw() + message := fmt.Sprintf("services.%s failed to create context. %s", process.ArgsString(0), err.Error()) + log.Error("[V8] process error. %s", message) + exception.New(message, 500).Throw() + return nil + } + defer ctx.Close() + + res, err := ctx.Call(method, args...) + if err != nil { + exception.New(err.Error(), 500).Throw() } return res } -func processCheck(process *gou.Process) interface{} { +func processCheck(process *process.Process) interface{} { process.ValidateArgNums(1) payload := process.ArgsMap(0) time.Sleep(3 * time.Second) @@ -276,7 +298,7 @@ func processCheck(process *gou.Process) interface{} { return nil } -func processSetup(process *gou.Process) interface{} { +func processSetup(process *process.Process) interface{} { process.ValidateArgNums(1) payload := process.ArgsMap(0) time.Sleep(3 * time.Second) @@ -285,7 +307,7 @@ func processSetup(process *gou.Process) interface{} { exception.New("Something error", 500).Throw() } - lang := process.Lang() + lang := session.Lang(process) if sid, has := payload["sid"].(string); has { lang, err := session.Global().ID(sid).Get("__yao_lang") if err != nil { @@ -310,27 +332,25 @@ func processSetup(process *gou.Process) interface{} { } } -func processIcons(process *gou.Process) interface{} { +func processIcons(process *process.Process) interface{} { process.ValidateArgNums(1) name := process.ArgsString(0) - file, err := filepath.Abs(filepath.Join(config.Conf.Root, "icons", name)) - if err != nil { - exception.New(err.Error(), 400).Throw() - } - content, err := ioutil.ReadFile(file) + file := filepath.Join("icons", name) + content, err := application.App.Read(file) if err != nil { exception.New(err.Error(), 400).Throw() } return string(content) } -func processMenu(process *gou.Process) interface{} { +func processMenu(p *process.Process) interface{} { if Setting.Menu.Process != "" { - return gou. - NewProcess(Setting.Menu.Process, process.Args...). - WithGlobal(process.Global). - WithSID(process.Sid). + + return process. + New(Setting.Menu.Process, p.Args...). + WithGlobal(p.Global). + WithSID(p.Sid). Run() } @@ -350,14 +370,14 @@ func processMenu(process *gou.Process) interface{} { "limit": 200, "orders": []map[string]interface{}{{"column": "rank", "option": "asc"}}, } - return gou. - NewProcess("models.xiang.menu.get", args). - WithGlobal(process.Global). - WithSID(process.Sid). + return process. + New("models.xiang.menu.get", args). + WithGlobal(p.Global). + WithSID(p.Sid). Run() } -func processSetting(process *gou.Process) interface{} { +func processSetting(process *process.Process) interface{} { if Setting == nil { exception.New("the app does not init", 500).Throw() return nil @@ -384,7 +404,7 @@ func processSetting(process *gou.Process) interface{} { session.Global().ID(sid).Set("__yao_lang", lang) } - setting, err := i18n.Trans(process.Lang(config.Conf.Lang), []string{"app.app"}, Setting) + setting, err := i18n.Trans(session.Lang(process, config.Conf.Lang), []string{"app.app"}, Setting) if err != nil { exception.New(err.Error(), 500).Throw() } @@ -393,7 +413,7 @@ func processSetting(process *gou.Process) interface{} { return *setting.(*DSL) } -func processXgen(process *gou.Process) interface{} { +func processXgen(process *process.Process) interface{} { if Setting == nil { exception.New("the app does not init", 500).Throw() @@ -444,7 +464,7 @@ func processXgen(process *gou.Process) interface{} { } // Translate - newLayout, err := i18n.Trans(process.Lang(config.Conf.Lang), []string{"login.admin"}, layout) + newLayout, err := i18n.Trans(session.Lang(process, config.Conf.Lang), []string{"login.admin"}, layout) if err != nil { log.Error("[Login] Xgen i18n.Trans login.admin %s", err.Error()) } @@ -481,7 +501,7 @@ func processXgen(process *gou.Process) interface{} { } // Translate - newLayout, err := i18n.Trans(process.Lang(config.Conf.Lang), []string{"login.user"}, layout) + newLayout, err := i18n.Trans(session.Lang(process, config.Conf.Lang), []string{"login.user"}, layout) if err != nil { log.Error("[Login] Xgen %s", err.Error()) } @@ -521,7 +541,7 @@ func processXgen(process *gou.Process) interface{} { xgenSetting["favicon"] = Setting.Favicon } - setting, err := i18n.Trans(process.Lang(config.Conf.Lang), []string{"app.app"}, xgenSetting) + setting, err := i18n.Trans(session.Lang(process, config.Conf.Lang), []string{"app.app"}, xgenSetting) if err != nil { exception.New(err.Error(), 500).Throw() } @@ -550,20 +570,23 @@ func (dsl *DSL) replaceAdminRoot() error { // icons func (dsl *DSL) icons(cfg config.Config) { - favicon := filepath.Join(cfg.Root, "icons", "app.ico") - if _, err := os.Stat(favicon); err == nil { - dsl.Favicon = fmt.Sprintf("/api/__yao/app/icons/app.ico") - } + dsl.Favicon = fmt.Sprintf("/api/__yao/app/icons/app.ico") + dsl.Logo = fmt.Sprintf("/api/__yao/app/icons/app.png") - logo := filepath.Join(cfg.Root, "icons", "app.png") - if _, err := os.Stat(logo); err == nil { - dsl.Logo = fmt.Sprintf("/api/__yao/app/icons/app.png") - } + // favicon := filepath.Join(cfg.Root, "icons", "app.ico") + // if _, err := os.Stat(favicon); err == nil { + // dsl.Favicon = fmt.Sprintf("/api/__yao/app/icons/app.ico") + // } + + // logo := filepath.Join(cfg.Root, "icons", "app.png") + // if _, err := os.Stat(logo); err == nil { + // dsl.Logo = fmt.Sprintf("/api/__yao/app/icons/app.png") + // } } // Permissions get the permission blacklist // {".":[]} -func Permissions(process *gou.Process, widget string, id string) map[string]bool { +func Permissions(process *process.Process, widget string, id string) map[string]bool { permissions := map[string]bool{} sessionData, _ := session.Global().ID(process.Sid).Get("__permissions") data, ok := sessionData.(map[string]interface{}) diff --git a/widgets/app/app_test.go b/widgets/app/app_test.go index 0fe5825e..8cd34ae8 100644 --- a/widgets/app/app_test.go +++ b/widgets/app/app_test.go @@ -5,19 +5,23 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" "github.com/yaoapp/gou/lang" + "github.com/yaoapp/gou/process" + "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/flow" "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/runtime" "github.com/yaoapp/yao/script" + "github.com/yaoapp/yao/test" _ "github.com/yaoapp/yao/utils" "github.com/yaoapp/yao/widgets/login" ) func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + err := Load(config.Conf) if err != nil { t.Fatal(err) @@ -26,7 +30,7 @@ func TestLoad(t *testing.T) { assert.Equal(t, "::Demo Application", Setting.Name) assert.Equal(t, "::Demo", Setting.Short) assert.Equal(t, "::Another yao application", Setting.Description) - assert.Equal(t, []string{"demo"}, Setting.Menu.Args) + assert.Equal(t, []interface{}{"demo"}, Setting.Menu.Args) assert.Equal(t, "flows.app.menu", Setting.Menu.Process) assert.Equal(t, true, Setting.Optional["hideNotification"]) assert.Equal(t, false, Setting.Optional["hideSetting"]) @@ -34,6 +38,9 @@ func TestLoad(t *testing.T) { func TestLoadHK(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + err := i18n.Load(config.Conf) if err != nil { t.Fatal(err) @@ -53,7 +60,7 @@ func TestLoadHK(t *testing.T) { assert.Equal(t, "示例應用", setting.Name) assert.Equal(t, "演示", setting.Short) assert.Equal(t, "又一個YAO應用", setting.Description) - assert.Equal(t, []string{"demo"}, setting.Menu.Args) + assert.Equal(t, []interface{}{"demo"}, setting.Menu.Args) assert.Equal(t, "flows.app.menu", setting.Menu.Process) assert.Equal(t, true, Setting.Optional["hideNotification"]) assert.Equal(t, false, Setting.Optional["hideSetting"]) @@ -61,7 +68,7 @@ func TestLoadHK(t *testing.T) { assert.Equal(t, "::Demo Application", Setting.Name) assert.Equal(t, "::Demo", Setting.Short) assert.Equal(t, "::Another yao application", Setting.Description) - assert.Equal(t, []string{"demo"}, Setting.Menu.Args) + assert.Equal(t, []interface{}{"demo"}, Setting.Menu.Args) assert.Equal(t, "flows.app.menu", Setting.Menu.Process) assert.Equal(t, true, Setting.Optional["hideNotification"]) assert.Equal(t, false, Setting.Optional["hideSetting"]) @@ -69,6 +76,9 @@ func TestLoadHK(t *testing.T) { func TestLoadCN(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + err := i18n.Load(config.Conf) if err != nil { t.Fatal(err) @@ -88,7 +98,7 @@ func TestLoadCN(t *testing.T) { assert.Equal(t, "示例应用", setting.Name) assert.Equal(t, "演示", setting.Short) assert.Equal(t, "又一个 YAO 应用", setting.Description) - assert.Equal(t, []string{"demo"}, setting.Menu.Args) + assert.Equal(t, []interface{}{"demo"}, setting.Menu.Args) assert.Equal(t, "flows.app.menu", setting.Menu.Process) assert.Equal(t, true, Setting.Optional["hideNotification"]) assert.Equal(t, false, Setting.Optional["hideSetting"]) @@ -96,7 +106,7 @@ func TestLoadCN(t *testing.T) { assert.Equal(t, "::Demo Application", Setting.Name) assert.Equal(t, "::Demo", Setting.Short) assert.Equal(t, "::Another yao application", Setting.Description) - assert.Equal(t, []string{"demo"}, Setting.Menu.Args) + assert.Equal(t, []interface{}{"demo"}, Setting.Menu.Args) assert.Equal(t, "flows.app.menu", Setting.Menu.Process) assert.Equal(t, true, Setting.Optional["hideNotification"]) assert.Equal(t, false, Setting.Optional["hideSetting"]) @@ -104,6 +114,9 @@ func TestLoadCN(t *testing.T) { func TestExport(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + err := login.Load(config.Conf) if err != nil { t.Fatal(err) @@ -119,34 +132,38 @@ func TestExport(t *testing.T) { t.Fatal(err) } - api, has := gou.APIs["widgets.app"] - assert.True(t, has) - assert.Equal(t, 7, len(api.HTTP.Paths)) + // api, has := gou.APIs["widgets.app"] + // assert.True(t, has) + // assert.Equal(t, 7, len(api.HTTP.Paths)) - _, has = gou.ThirdHandlers["yao.app.setting"] - assert.True(t, has) + // _, has = gou.ThirdHandlers["yao.app.setting"] + // assert.True(t, has) - _, has = gou.ThirdHandlers["yao.app.xgen"] - assert.True(t, has) + // _, has = gou.ThirdHandlers["yao.app.xgen"] + // assert.True(t, has) - _, has = gou.ThirdHandlers["yao.app.menu"] - assert.True(t, has) + // _, has = gou.ThirdHandlers["yao.app.menu"] + // assert.True(t, has) - _, has = gou.ThirdHandlers["yao.app.check"] - assert.True(t, has) + // _, has = gou.ThirdHandlers["yao.app.check"] + // assert.True(t, has) - _, has = gou.ThirdHandlers["yao.app.setup"] - assert.True(t, has) + // _, has = gou.ThirdHandlers["yao.app.setup"] + // assert.True(t, has) - _, has = gou.ThirdHandlers["yao.app.service"] - assert.True(t, has) + // _, has = gou.ThirdHandlers["yao.app.service"] + // assert.True(t, has) } func TestProcessSetting(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + loadApp(t) backup := config.Conf.Lang config.Conf.Lang = "en-us" - res, err := gou.NewProcess("yao.app.Setting").Exec() + res, err := process.New("yao.app.Setting").Exec() if err != nil { t.Fatal(err) } @@ -156,14 +173,14 @@ func TestProcessSetting(t *testing.T) { assert.Equal(t, "Demo Application", setting.Name) assert.Equal(t, "Demo", setting.Short) assert.Equal(t, "Another yao application", setting.Description) - assert.Equal(t, []string{"demo"}, setting.Menu.Args) + assert.Equal(t, []interface{}{"demo"}, setting.Menu.Args) assert.Equal(t, "flows.app.menu", setting.Menu.Process) assert.Equal(t, true, Setting.Optional["hideNotification"]) assert.Equal(t, false, Setting.Optional["hideSetting"]) assert.Equal(t, true, setting.Sid != "") // Set - res, err = gou.NewProcess("yao.app.Setting", map[string]interface{}{"lang": "zh-hk", "sid": setting.Sid}).Exec() + res, err = process.New("yao.app.Setting", map[string]interface{}{"lang": "zh-hk", "sid": setting.Sid}).Exec() if err != nil { t.Fatal(err) } @@ -171,17 +188,22 @@ func TestProcessSetting(t *testing.T) { setting2, ok := res.(DSL) assert.Equal(t, setting.Sid, setting2.Sid) - lang := gou.NewProcess("yao.app.Setting").WithSID(setting.Sid).Lang() + p := process.New("yao.app.Setting").WithSID(setting.Sid) + lang := session.Lang(p) assert.Equal(t, "zh-hk", lang) config.Conf.Lang = backup } func TestProcessXgen(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + loadApp(t) backup := config.Conf.Lang config.Conf.Lang = "en-us" - res, err := gou.NewProcess("yao.app.Xgen").Exec() + res, err := process.New("yao.app.Xgen").Exec() if err != nil { t.Fatal(err) } @@ -206,7 +228,7 @@ func TestProcessXgen(t *testing.T) { assert.Equal(t, true, xgen.Get("sid").(string) != "") // Set - res, err = gou.NewProcess("yao.app.Xgen", map[string]interface{}{"lang": "zh-hk", "sid": xgen.Get("sid")}).Exec() + res, err = process.New("yao.app.Xgen", map[string]interface{}{"lang": "zh-hk", "sid": xgen.Get("sid")}).Exec() if err != nil { t.Fatal(err) } @@ -214,14 +236,19 @@ func TestProcessXgen(t *testing.T) { xgen2 := any.Of(res).MapStr().Dot() assert.Equal(t, xgen.Get("sid"), xgen2.Get("sid")) - lang := gou.NewProcess("yao.app.Setting").WithSID(xgen2.Get("sid").(string)).Lang() + p := process.New("yao.app.Setting").WithSID(xgen2.Get("sid").(string)) + lang := session.Lang(p) assert.Equal(t, "zh-hk", lang) config.Conf.Lang = backup } func TestProcessMenu(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + loadApp(t) - res, err := gou.NewProcess("yao.app.Menu").Exec() + res, err := process.New("yao.app.Menu").Exec() if err != nil { t.Fatal(err) } @@ -232,8 +259,12 @@ func TestProcessMenu(t *testing.T) { } func TestProcessIcons(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + loadApp(t) - res, err := gou.NewProcess("yao.app.Icons", "app.png").Exec() + res, err := process.New("yao.app.Icons", "app.png").Exec() if err != nil { t.Fatal(err) } @@ -241,32 +272,44 @@ func TestProcessIcons(t *testing.T) { } func TestProcessCheck(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + loadApp(t) - res, err := gou.NewProcess("yao.app.Check", map[string]interface{}{}).Exec() + res, err := process.New("yao.app.Check", map[string]interface{}{}).Exec() if err != nil { t.Fatal(err) } assert.Nil(t, res) - _, err = gou.NewProcess("yao.app.Check", map[string]interface{}{"error": "1"}).Exec() + _, err = process.New("yao.app.Check", map[string]interface{}{"error": "1"}).Exec() assert.NotNil(t, err) } func TestProcessSetup(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + loadApp(t) - res, err := gou.NewProcess("yao.app.Setup", map[string]interface{}{"sid": "hello"}).Exec() + res, err := process.New("yao.app.Setup", map[string]interface{}{"sid": "hello"}).Exec() if err != nil { t.Fatal(err) } assert.Equal(t, "http://127.0.0.1:5099/admin/", res.(map[string]interface{})["admin"]) - _, err = gou.NewProcess("yao.app.Setup", map[string]interface{}{"error": "1"}).Exec() + _, err = process.New("yao.app.Setup", map[string]interface{}{"error": "1"}).Exec() assert.NotNil(t, err) } func TestProcessService(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + loadApp(t) - res, err := gou.NewProcess( + res, err := process.New( "yao.app.Service", "foo", map[string]interface{}{"method": "Bar", "args": []interface{}{"hello", "world"}}, @@ -279,7 +322,6 @@ func TestProcessService(t *testing.T) { } func loadApp(t *testing.T) { - runtime.Load(config.Conf) err := script.Load(config.Conf) if err != nil { diff --git a/widgets/app/types.go b/widgets/app/types.go index 4c6ab30f..dcd81978 100644 --- a/widgets/app/types.go +++ b/widgets/app/types.go @@ -20,8 +20,8 @@ type DSL struct { // MenuDSL the menu DSL type MenuDSL struct { - Process string `json:"process,omitempty"` - Args []string `json:"args,omitempty"` + Process string `json:"process,omitempty"` + Args []interface{} `json:"args,omitempty"` } // OptionalDSL the Optional DSL diff --git a/widgets/chart/api.go b/widgets/chart/api.go index 8ff0d61f..d6aaa808 100644 --- a/widgets/chart/api.go +++ b/widgets/chart/api.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" "github.com/yaoapp/yao/share" "github.com/yaoapp/yao/widgets/action" ) @@ -61,48 +61,48 @@ func (chart *DSL) getAction(path string) (*action.Process, error) { // export API func exportAPI() error { - http := gou.HTTP{ + http := api.HTTP{ Name: "Widget Chart API", Description: "Widget Chart API", Version: share.VERSION, Guard: "widget-chart", Group: "__yao/chart", - Paths: []gou.Path{}, + Paths: []api.Path{}, } // GET /api/__yao/chart/:id/setting -> Default process: yao.chart.Xgen - path := gou.Path{ + path := api.Path{ Label: "Setting", Description: "Setting", Path: "/:id/setting", Method: "GET", Process: "yao.chart.Setting", - In: []string{"$param.id"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/chart/:id/data -> Default process: yao.chart.Data $param.id :query - path = gou.Path{ + path = api.Path{ Label: "Data", Description: "Data", Path: "/:id/data", Method: "GET", Process: "yao.chart.Data", - In: []string{"$param.id", ":query"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":query"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/chart/:id/component/:xpath/:method -> Default process: yao.chart.Component $param.id $param.xpath $param.method :query - path = gou.Path{ + path = api.Path{ Label: "Component", Description: "Component", Path: "/:id/component/:xpath/:method", Method: "GET", Process: "yao.chart.Component", - In: []string{"$param.id", "$param.xpath", "$param.method", ":query"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", ":query"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) @@ -113,6 +113,6 @@ func exportAPI() error { } // load apis - _, err = gou.LoadAPIReturn(string(source), "widgets.chart") + _, err = api.LoadSource(".yao", source, "widgets.chart") return err } diff --git a/widgets/chart/chart.go b/widgets/chart/chart.go index 31776db7..a047e9c0 100644 --- a/widgets/chart/chart.go +++ b/widgets/chart/chart.go @@ -2,11 +2,11 @@ package chart import ( "fmt" - "path/filepath" "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" @@ -55,69 +55,85 @@ func LoadAndExport(cfg config.Config) error { // Load load task func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "charts") - return LoadFrom(root, "") -} - -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - messages := []string{} - err := share.Walk(dir, ".json", func(root, filename string) { - id := prefix + share.ID(root, filename) - data := share.ReadFile(filename) - dsl := New(id) - err := jsoniter.Unmarshal(data, dsl) - if err != nil { - messages = append(messages, fmt.Sprintf("[%s] %s", id, err.Error())) - return + exts := []string{"*.yao", "*.json", "*.jsonc"} + err := application.App.Walk("charts", func(root, file string, isdir bool) error { + if isdir { + return nil + } + if err := LoadFile(root, file); err != nil { + messages = append(messages, err.Error()) } - if dsl.Action == nil { - dsl.Action = &ActionDSL{} - } - dsl.Action.SetDefaultProcess() - - if dsl.Layout == nil { - dsl.Layout = &LayoutDSL{} - } - - // mapping - err = dsl.mapping() - if err != nil { - messages = append(messages, fmt.Sprintf("[%s] Mapping %s", id, err.Error())) - return - } - - // Validate - err = dsl.Validate() - if err != nil { - messages = append(messages, fmt.Sprintf("[%s] %s", id, err.Error())) - return - } - - Charts[id] = dsl - }) + return nil + }, exts...) if len(messages) > 0 { - return fmt.Errorf(strings.Join(messages, ";")) + return fmt.Errorf(strings.Join(messages, ";\n")) } return err } +// LoadFile load table dsl by file +func LoadFile(root string, file string) error { + + id := share.ID(root, file) + data, err := application.App.Read(file) + if err != nil { + return err + } + + dsl := New(id) + err = application.Parse(file, data, dsl) + if err != nil { + return fmt.Errorf("[%s] %s", id, err.Error()) + } + + err = dsl.parse(id, root) + if err != nil { + return err + } + + Charts[id] = dsl + return nil +} + +// LoadData load via data +func (dsl *DSL) parse(id string, root string) error { + + if dsl.Action == nil { + dsl.Action = &ActionDSL{} + } + dsl.Action.SetDefaultProcess() + + if dsl.Layout == nil { + dsl.Layout = &LayoutDSL{} + } + + // mapping + err := dsl.mapping() + if err != nil { + return err + } + + // Validate + err = dsl.Validate() + if err != nil { + return err + } + + return nil +} + // Get chart via process or id func Get(chart interface{}) (*DSL, error) { id := "" switch chart.(type) { case string: id = chart.(string) - case *gou.Process: - id = chart.(*gou.Process).ArgsString(0) + case *process.Process: + id = chart.(*process.Process).ArgsString(0) default: return nil, fmt.Errorf("%v type does not support", chart) } diff --git a/widgets/chart/chart_test.go b/widgets/chart/chart_test.go index ba0d6035..afb058eb 100644 --- a/widgets/chart/chart_test.go +++ b/widgets/chart/chart_test.go @@ -6,14 +6,15 @@ import ( "github.com/stretchr/testify/assert" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/script" - "github.com/yaoapp/yao/share" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + err := Load(config.Conf) if err != nil { t.Fatal(err) @@ -22,18 +23,10 @@ func TestLoad(t *testing.T) { } func prepare(t *testing.T, language ...string) { - runtime.Load(config.Conf) + i18n.Load(config.Conf) - share.DBConnect(config.Conf.DB) // removed later - // load scripts - err := script.Load(config.Conf) - if err != nil { - t.Fatal(err) - } - - // load models - err = model.Load(config.Conf) + err := Load(config.Conf) if err != nil { t.Fatal(err) } diff --git a/widgets/chart/handler.go b/widgets/chart/handler.go index 5aa6f6bf..44169d85 100644 --- a/widgets/chart/handler.go +++ b/widgets/chart/handler.go @@ -3,7 +3,7 @@ package chart import ( "fmt" - "github.com/yaoapp/gou" + gouProcess "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/widgets/action" ) @@ -13,7 +13,7 @@ import ( // ******************************** // Life-Circle: Before Hook → Run Process → Compute View → After Hook // Execute Compute View On: Data -func processHandler(p *action.Process, process *gou.Process) (interface{}, error) { +func processHandler(p *action.Process, process *gouProcess.Process) (interface{}, error) { chart, err := Get(process) if err != nil { @@ -51,7 +51,7 @@ func processHandler(p *action.Process, process *gou.Process) (interface{}, error } // Execute Process - act, err := gou.ProcessOf(name, args...) + act, err := gouProcess.Of(name, args...) if err != nil { log.Error("[chart] %s %s -> %s %s", chart.ID, p.Name, name, err.Error()) return nil, fmt.Errorf("[chart] %s %s -> %s %s", chart.ID, p.Name, name, err.Error()) diff --git a/widgets/chart/process.go b/widgets/chart/process.go index 6179599d..aaec9dc6 100644 --- a/widgets/chart/process.go +++ b/widgets/chart/process.go @@ -3,20 +3,20 @@ package chart import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" "github.com/yaoapp/yao/widgets/app" ) // Export process func exportProcess() { - gou.RegisterProcessHandler("yao.chart.setting", processSetting) - gou.RegisterProcessHandler("yao.chart.xgen", processXgen) - gou.RegisterProcessHandler("yao.chart.component", processComponent) - gou.RegisterProcessHandler("yao.chart.data", processData) + process.Register("yao.chart.setting", processSetting) + process.Register("yao.chart.xgen", processXgen) + process.Register("yao.chart.component", processComponent) + process.Register("yao.chart.data", processData) } -func processXgen(process *gou.Process) interface{} { +func processXgen(process *process.Process) interface{} { chart := MustGet(process) data := process.ArgsMap(1, map[string]interface{}{}) @@ -29,7 +29,7 @@ func processXgen(process *gou.Process) interface{} { return setting } -func processComponent(process *gou.Process) interface{} { +func processComponent(process *process.Process) interface{} { process.ValidateArgNums(3) chart := MustGet(process) @@ -58,13 +58,13 @@ func processComponent(process *gou.Process) interface{} { return res } -func processSetting(process *gou.Process) interface{} { +func processSetting(process *process.Process) interface{} { chart := MustGet(process) process.Args = append(process.Args, process.Args[0]) // chart name return chart.Action.Setting.MustExec(process) } -func processData(process *gou.Process) interface{} { +func processData(process *process.Process) interface{} { chart := MustGet(process) return chart.Action.Data.MustExec(process) } diff --git a/widgets/chart/process_test.go b/widgets/chart/process_test.go index b9f85449..4bfb39e5 100644 --- a/widgets/chart/process_test.go +++ b/widgets/chart/process_test.go @@ -5,18 +5,25 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/maps" "github.com/yaoapp/yao/config" - q "github.com/yaoapp/yao/query" + "github.com/yaoapp/yao/test" ) func TestProcessData(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) + clear(t) + testData(t) + args := []interface{}{"dashboard", map[string]interface{}{"range": "2022-01-02", "status": "checked"}} - res, err := gou.NewProcess("yao.chart.Data", args...).Exec() + res, err := process.New("yao.chart.Data", args...).Exec() if err != nil { t.Fatal(err) } @@ -25,7 +32,13 @@ func TestProcessData(t *testing.T) { } func TestProcessComponent(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) + clear(t) + testData(t) + args := []interface{}{ "dashboard", "fields.filter.状态.edit.props.xProps", @@ -33,7 +46,7 @@ func TestProcessComponent(t *testing.T) { map[string]interface{}{"select": []string{"name", "status"}, "limit": 2}, } - res, err := gou.NewProcess("yao.chart.Component", args...).Exec() + res, err := process.New("yao.chart.Component", args...).Exec() if err != nil { t.Fatal(err) } @@ -48,21 +61,33 @@ func TestProcessComponent(t *testing.T) { } func TestProcessComponentError(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) + clear(t) + testData(t) + args := []interface{}{ "dashboard", "fields.filter.edit.props.状态.::not-exist", "remote", map[string]interface{}{"select": []string{"name", "status"}, "limit": 2}, } - _, err := gou.NewProcess("yao.chart.Component", args...).Exec() + _, err := process.New("yao.chart.Component", args...).Exec() assert.Contains(t, err.Error(), "fields.filter.edit.props.状态.::not-exist") } func TestProcessSetting(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) + clear(t) + testData(t) + args := []interface{}{"dashboard"} - res, err := gou.NewProcess("yao.chart.Setting", args...).Exec() + res, err := process.New("yao.chart.Setting", args...).Exec() if err != nil { t.Fatal(err) } @@ -72,9 +97,15 @@ func TestProcessSetting(t *testing.T) { } func TestProcessXgen(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) + clear(t) + testData(t) + args := []interface{}{"dashboard"} - res, err := gou.NewProcess("yao.chart.Xgen", args...).Exec() + res, err := process.New("yao.chart.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -84,7 +115,13 @@ func TestProcessXgen(t *testing.T) { } func TestProcessXgenWithPermissions(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) + clear(t) + testData(t) + session.Global().Set("__permissions", map[string]interface{}{ "charts.dashboard": []string{ "7f46a38d7ff3f1832375ff63cd412f41", // operation.actions[0] 跳转至大屏 @@ -94,7 +131,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { }) args := []interface{}{"dashboard"} - res, err := gou.NewProcess("yao.chart.Xgen", args...).Exec() + res, err := process.New("yao.chart.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -106,7 +143,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { assert.Equal(t, nil, data.Get("fields.chart.综合评分")) session.Global().Set("__permissions", nil) - res, err = gou.NewProcess("yao.chart.Xgen", args...).Exec() + res, err = process.New("yao.chart.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -118,19 +155,8 @@ func TestProcessXgenWithPermissions(t *testing.T) { assert.NotEqual(t, nil, data.Get("fields.chart.综合评分")) } -func load(t *testing.T) { - prepare(t) - err := Load(config.Conf) - if err != nil { - t.Fatal(err) - } - q.Load(config.Conf) - clear(t) - testData(t) -} - func testData(t *testing.T) { - pet := gou.Select("pet") + pet := model.Select("pet") err := pet.Insert( []string{"name", "type", "status", "mode", "stay", "cost", "doctor_id"}, [][]interface{}{ @@ -145,7 +171,7 @@ func testData(t *testing.T) { } func clear(t *testing.T) { - for _, m := range gou.Models { + for _, m := range model.Models { err := m.DropTable() if err != nil { t.Fatal(err) diff --git a/widgets/component/compute.go b/widgets/component/compute.go index bf6636c2..05173a88 100644 --- a/widgets/component/compute.go +++ b/widgets/component/compute.go @@ -6,7 +6,7 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/maps" ) @@ -42,7 +42,7 @@ func (compute *Compute) Value(data maps.MapStr, sid string, global map[string]in } // Run process - process, err := gou.ProcessOf(compute.Process, args...) + process, err := process.Of(compute.Process, args...) if err != nil { return nil, err } diff --git a/widgets/component/process.go b/widgets/component/process.go index 940208da..812387d9 100644 --- a/widgets/component/process.go +++ b/widgets/component/process.go @@ -5,29 +5,30 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" ) // Export process func exportProcess() { - gou.RegisterProcessHandler("yao.component.selectoptions", processSelectOptions) + process.Register("yao.component.selectoptions", processSelectOptions) } -func processSelectOptions(process *gou.Process) interface{} { +func processSelectOptions(process *process.Process) interface{} { process.ValidateArgNums(1) query := process.ArgsMap(0, map[string]interface{}{}) if !query.Has("model") { exception.New("query.model required", 400).Throw() } - model, ok := query.Get("model").(string) + modelName, ok := query.Get("model").(string) if !ok { exception.New("query.model must be a string", 400).Throw() } - m := gou.Select(model) + m := model.Select(modelName) valueField := query.Get("value") if valueField == nil { @@ -55,10 +56,10 @@ func processSelectOptions(process *gou.Process) interface{} { } } - wheres := []gou.QueryWhere{} + wheres := []model.QueryWhere{} switch input := query.Get("wheres").(type) { case string: - where := gou.QueryWhere{} + where := model.QueryWhere{} err := jsoniter.Unmarshal([]byte(input), &where) if err != nil { exception.New("query.wheres error %s", 400, err.Error()).Throw() @@ -68,7 +69,7 @@ func processSelectOptions(process *gou.Process) interface{} { case []string: for _, data := range input { - where := gou.QueryWhere{} + where := model.QueryWhere{} err := jsoniter.Unmarshal([]byte(data), &where) if err != nil { exception.New("query.wheres error %s", 400, err.Error()).Throw() @@ -89,7 +90,7 @@ func processSelectOptions(process *gou.Process) interface{} { } } - rows, err := m.Get(gou.QueryParam{ + rows, err := m.Get(model.QueryParam{ Select: []interface{}{valueField, labelField}, Wheres: wheres, Limit: limit, diff --git a/widgets/component/props.go b/widgets/component/props.go index 65fd0fe9..1e633be4 100644 --- a/widgets/component/props.go +++ b/widgets/component/props.go @@ -6,7 +6,8 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + gouProcess "github.com/yaoapp/gou/process" + "github.com/yaoapp/gou/types" "github.com/yaoapp/kun/log" ) @@ -26,7 +27,7 @@ func (cProp CloudPropsDSL) UploadPath() string { } // ExecUpload execute upload -func (cProp CloudPropsDSL) ExecUpload(process *gou.Process, upload gou.UploadFile) (interface{}, error) { +func (cProp CloudPropsDSL) ExecUpload(process *gouProcess.Process, upload types.UploadFile) (interface{}, error) { if upload.TempFile == "" { log.Error("[component] %s.$%s upload file is required", cProp.Xpath, cProp.Name) @@ -41,7 +42,7 @@ func (cProp CloudPropsDSL) ExecUpload(process *gou.Process, upload gou.UploadFil } // Create process - p, err := gou.ProcessOf(name, upload) + p, err := gouProcess.Of(name, upload) if err != nil { log.Error("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) return nil, fmt.Errorf("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) @@ -58,7 +59,7 @@ func (cProp CloudPropsDSL) ExecUpload(process *gou.Process, upload gou.UploadFil } // ExecQuery execute query -func (cProp CloudPropsDSL) ExecQuery(process *gou.Process, query map[string]interface{}) (interface{}, error) { +func (cProp CloudPropsDSL) ExecQuery(process *gouProcess.Process, query map[string]interface{}) (interface{}, error) { if query == nil { query = map[string]interface{}{} @@ -80,7 +81,7 @@ func (cProp CloudPropsDSL) ExecQuery(process *gou.Process, query map[string]inte } // Create process - p, err := gou.ProcessOf(name, query) + p, err := gouProcess.Of(name, query) if err != nil { log.Error("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) return nil, fmt.Errorf("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) diff --git a/widgets/compute/compute.go b/widgets/compute/compute.go index 901d7a60..e4d8597e 100644 --- a/widgets/compute/compute.go +++ b/widgets/compute/compute.go @@ -5,7 +5,7 @@ import ( "reflect" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/maps" "github.com/yaoapp/yao/widgets/field" @@ -14,7 +14,7 @@ import ( var views = map[string]bool{"find": true, "get": true, "search": true, "data": true} // ComputeEdit edit compute edit -func (c *Computable) ComputeEdit(name string, process *gou.Process, args []interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { +func (c *Computable) ComputeEdit(name string, process *process.Process, args []interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { namer := strings.Split(strings.ToLower(name), ".") name = namer[len(namer)-1] @@ -86,7 +86,7 @@ func (c *Computable) ComputeEdit(name string, process *gou.Process, args []inter } // EditRow edit row -func (c *Computable) editRow(process *gou.Process, res map[string]interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { +func (c *Computable) editRow(process *process.Process, res map[string]interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { messages := []string{} row := maps.MapOf(res).Dot() @@ -121,7 +121,7 @@ func (c *Computable) editRow(process *gou.Process, res map[string]interface{}, g } // EditRows edit row -func (c *Computable) editRows(process *gou.Process, columns []string, res [][]interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { +func (c *Computable) editRows(process *process.Process, columns []string, res [][]interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { messages := []string{} keys := map[string]int{} @@ -157,7 +157,7 @@ func (c *Computable) editRows(process *gou.Process, columns []string, res [][]in } // ComputeView view view -func (c *Computable) ComputeView(name string, process *gou.Process, res interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { +func (c *Computable) ComputeView(name string, process *process.Process, res interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { namer := strings.Split(strings.ToLower(name), ".") name = namer[len(namer)-1] @@ -180,7 +180,7 @@ func (c *Computable) ComputeView(name string, process *gou.Process, res interfac } // ViewRows viewrows -func (c *Computable) viewRows(name string, process *gou.Process, res interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { +func (c *Computable) viewRows(name string, process *process.Process, res interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { switch res.(type) { case []interface{}: @@ -214,7 +214,11 @@ func (c *Computable) viewRows(name string, process *gou.Process, res interface{} } // ViewRow row -func (c *Computable) viewRow(name string, process *gou.Process, res map[string]interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { +func (c *Computable) viewRow(name string, process *process.Process, res map[string]interface{}, getField func(string) (*field.ColumnDSL, string, string, error)) error { + + if c.Computes == nil { + return nil + } messages := []string{} row := maps.MapOf(res).Dot() @@ -263,6 +267,6 @@ func (c *Computable) viewRow(name string, process *gou.Process, res map[string]i } // ComputeFilter filter -func (c *Computable) ComputeFilter(name string, process *gou.Process, args []interface{}, getFilter func(string) (*field.FilterDSL, string, string, error)) error { +func (c *Computable) ComputeFilter(name string, process *process.Process, args []interface{}, getFilter func(string) (*field.FilterDSL, string, string, error)) error { return nil } diff --git a/widgets/dashboard/api.go b/widgets/dashboard/api.go index f3e380fc..ffc32898 100644 --- a/widgets/dashboard/api.go +++ b/widgets/dashboard/api.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" "github.com/yaoapp/yao/share" "github.com/yaoapp/yao/widgets/action" ) @@ -61,48 +61,48 @@ func (dashboard *DSL) getAction(path string) (*action.Process, error) { // export API func exportAPI() error { - http := gou.HTTP{ + http := api.HTTP{ Name: "Widget Dashboard API", Description: "Widget Dashboard API", Version: share.VERSION, Guard: "widget-dashboard", Group: "__yao/dashboard", - Paths: []gou.Path{}, + Paths: []api.Path{}, } // GET /api/__yao/dashboard/:id/setting -> Default process: yao.dashboard.Xgen - path := gou.Path{ + path := api.Path{ Label: "Setting", Description: "Setting", Path: "/:id/setting", Method: "GET", Process: "yao.dashboard.Setting", - In: []string{"$param.id"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/dashboard/:id/data -> Default process: yao.dashboard.Data $param.id :query - path = gou.Path{ + path = api.Path{ Label: "Data", Description: "Data", Path: "/:id/data", Method: "GET", Process: "yao.dashboard.Data", - In: []string{"$param.id", ":query"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":query"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/dashboard/:id/component/:xpath/:method -> Default process: yao.dashboard.Component $param.id $param.xpath $param.method :query - path = gou.Path{ + path = api.Path{ Label: "Component", Description: "Component", Path: "/:id/component/:xpath/:method", Method: "GET", Process: "yao.dashboard.Component", - In: []string{"$param.id", "$param.xpath", "$param.method", ":query"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", ":query"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) @@ -113,6 +113,6 @@ func exportAPI() error { } // load apis - _, err = gou.LoadAPIReturn(string(source), "widgets.dashboard") + _, err = api.LoadSource(".yao", source, "widgets.dashboard") return err } diff --git a/widgets/dashboard/dashboard.go b/widgets/dashboard/dashboard.go index 44227f81..3c0b8689 100644 --- a/widgets/dashboard/dashboard.go +++ b/widgets/dashboard/dashboard.go @@ -2,11 +2,11 @@ package dashboard import ( "fmt" - "path/filepath" "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" @@ -55,69 +55,85 @@ func LoadAndExport(cfg config.Config) error { // Load load task func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "dashboards") - return LoadFrom(root, "") -} - -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - messages := []string{} - err := share.Walk(dir, ".json", func(root, filename string) { - id := prefix + share.ID(root, filename) - data := share.ReadFile(filename) - dsl := New(id) - err := jsoniter.Unmarshal(data, dsl) - if err != nil { - messages = append(messages, fmt.Sprintf("[%s] %s", id, err.Error())) - return + exts := []string{"*.yao", "*.json", "*.jsonc"} + err := application.App.Walk("dashboards", func(root, file string, isdir bool) error { + if isdir { + return nil + } + if err := LoadFile(root, file); err != nil { + messages = append(messages, err.Error()) } - if dsl.Action == nil { - dsl.Action = &ActionDSL{} - } - dsl.Action.SetDefaultProcess() - - if dsl.Layout == nil { - dsl.Layout = &LayoutDSL{} - } - - // mapping - err = dsl.mapping() - if err != nil { - messages = append(messages, fmt.Sprintf("[%s] Mapping %s", id, err.Error())) - return - } - - // Validate - err = dsl.Validate() - if err != nil { - messages = append(messages, fmt.Sprintf("[%s] %s", id, err.Error())) - return - } - - Dashboards[id] = dsl - }) + return nil + }, exts...) if len(messages) > 0 { - return fmt.Errorf(strings.Join(messages, ";")) + return fmt.Errorf(strings.Join(messages, ";\n")) } return err } +// LoadFile load table dsl by file +func LoadFile(root string, file string) error { + + id := share.ID(root, file) + data, err := application.App.Read(file) + if err != nil { + return err + } + + dsl := New(id) + err = application.Parse(file, data, dsl) + if err != nil { + return fmt.Errorf("[%s] %s", id, err.Error()) + } + + err = dsl.parse(id, root) + if err != nil { + return err + } + + Dashboards[id] = dsl + return nil +} + +// LoadData load via data +func (dsl *DSL) parse(id string, root string) error { + + if dsl.Action == nil { + dsl.Action = &ActionDSL{} + } + dsl.Action.SetDefaultProcess() + + if dsl.Layout == nil { + dsl.Layout = &LayoutDSL{} + } + + // mapping + err := dsl.mapping() + if err != nil { + return err + } + + // Validate + err = dsl.Validate() + if err != nil { + return err + } + + return nil +} + // Get dashboard via process or id func Get(dashboard interface{}) (*DSL, error) { id := "" switch dashboard.(type) { case string: id = dashboard.(string) - case *gou.Process: - id = dashboard.(*gou.Process).ArgsString(0) + case *process.Process: + id = dashboard.(*process.Process).ArgsString(0) default: return nil, fmt.Errorf("%v type does not support", dashboard) } diff --git a/widgets/dashboard/dashboard_test.go b/widgets/dashboard/dashboard_test.go index 33e7c08a..d9d7b947 100644 --- a/widgets/dashboard/dashboard_test.go +++ b/widgets/dashboard/dashboard_test.go @@ -7,14 +7,14 @@ import ( "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/flow" "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/script" - "github.com/yaoapp/yao/share" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() prepare(t) + err := Load(config.Conf) if err != nil { t.Fatal(err) @@ -23,23 +23,13 @@ func TestLoad(t *testing.T) { } func prepare(t *testing.T, language ...string) { - runtime.Load(config.Conf) i18n.Load(config.Conf) - share.DBConnect(config.Conf.DB) // removed later - // load scripts - err := script.Load(config.Conf) + err := Load(config.Conf) if err != nil { t.Fatal(err) } - // load models - err = model.Load(config.Conf) - if err != nil { - t.Fatal(err) - } - - // load flows err = flow.Load(config.Conf) if err != nil { t.Fatal(err) diff --git a/widgets/dashboard/handler.go b/widgets/dashboard/handler.go index 780e3722..8cc99df4 100644 --- a/widgets/dashboard/handler.go +++ b/widgets/dashboard/handler.go @@ -3,7 +3,7 @@ package dashboard import ( "fmt" - "github.com/yaoapp/gou" + gouProcess "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/widgets/action" ) @@ -13,7 +13,7 @@ import ( // ******************************** // Life-Circle: Before Hook → Run Process → Compute View → After Hook // Execute Compute View On: Data -func processHandler(p *action.Process, process *gou.Process) (interface{}, error) { +func processHandler(p *action.Process, process *gouProcess.Process) (interface{}, error) { dashboard, err := Get(process) if err != nil { @@ -51,7 +51,7 @@ func processHandler(p *action.Process, process *gou.Process) (interface{}, error } // Execute Process - act, err := gou.ProcessOf(name, args...) + act, err := gouProcess.Of(name, args...) if err != nil { log.Error("[dashboard] %s %s -> %s %s", dashboard.ID, p.Name, name, err.Error()) return nil, fmt.Errorf("[dashboard] %s %s -> %s %s", dashboard.ID, p.Name, name, err.Error()) diff --git a/widgets/dashboard/process.go b/widgets/dashboard/process.go index 9a8d5c11..a85fc521 100644 --- a/widgets/dashboard/process.go +++ b/widgets/dashboard/process.go @@ -3,20 +3,20 @@ package dashboard import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" "github.com/yaoapp/yao/widgets/app" ) // Export process func exportProcess() { - gou.RegisterProcessHandler("yao.dashboard.setting", processSetting) - gou.RegisterProcessHandler("yao.dashboard.xgen", processXgen) - gou.RegisterProcessHandler("yao.dashboard.component", processComponent) - gou.RegisterProcessHandler("yao.dashboard.data", processData) + process.Register("yao.dashboard.setting", processSetting) + process.Register("yao.dashboard.xgen", processXgen) + process.Register("yao.dashboard.component", processComponent) + process.Register("yao.dashboard.data", processData) } -func processXgen(process *gou.Process) interface{} { +func processXgen(process *process.Process) interface{} { dashboard := MustGet(process) data := process.ArgsMap(1, map[string]interface{}{}) @@ -29,7 +29,7 @@ func processXgen(process *gou.Process) interface{} { return setting } -func processComponent(process *gou.Process) interface{} { +func processComponent(process *process.Process) interface{} { process.ValidateArgNums(3) dashboard := MustGet(process) @@ -58,13 +58,13 @@ func processComponent(process *gou.Process) interface{} { return res } -func processSetting(process *gou.Process) interface{} { +func processSetting(process *process.Process) interface{} { dashboard := MustGet(process) process.Args = append(process.Args, process.Args[0]) // dashboard name return dashboard.Action.Setting.MustExec(process) } -func processData(process *gou.Process) interface{} { +func processData(process *process.Process) interface{} { dashboard := MustGet(process) return dashboard.Action.Data.MustExec(process) } diff --git a/widgets/dashboard/process_test.go b/widgets/dashboard/process_test.go index ff46e261..64818f00 100644 --- a/widgets/dashboard/process_test.go +++ b/widgets/dashboard/process_test.go @@ -5,18 +5,22 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/maps" "github.com/yaoapp/yao/config" - q "github.com/yaoapp/yao/query" + "github.com/yaoapp/yao/test" ) func TestProcessData(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + args := []interface{}{"workspace", map[string]interface{}{"range": "2022-01-02", "status": "checked"}} - res, err := gou.NewProcess("yao.dashboard.Data", args...).Exec() + res, err := process.New("yao.dashboard.Data", args...).Exec() if err != nil { t.Fatal(err) } @@ -25,7 +29,10 @@ func TestProcessData(t *testing.T) { } func TestProcessComponent(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + args := []interface{}{ "workspace", "fields.filter.状态.edit.props.xProps", @@ -33,7 +40,7 @@ func TestProcessComponent(t *testing.T) { map[string]interface{}{"select": []string{"name", "status"}, "limit": 2}, } - res, err := gou.NewProcess("yao.dashboard.Component", args...).Exec() + res, err := process.New("yao.dashboard.Component", args...).Exec() if err != nil { t.Fatal(err) } @@ -53,7 +60,7 @@ func TestProcessComponent(t *testing.T) { map[string]interface{}{"foo": "bar"}, } - res2, err := gou.NewProcess("yao.dashboard.Component", args...).Exec() + res2, err := process.New("yao.dashboard.Component", args...).Exec() if err != nil { t.Fatal(err) } @@ -69,7 +76,7 @@ func TestProcessComponent(t *testing.T) { map[string]interface{}{"foo": "bar"}, } - res2, err = gou.NewProcess("yao.dashboard.Component", args...).Exec() + res2, err = process.New("yao.dashboard.Component", args...).Exec() if err != nil { t.Fatal(err) } @@ -81,21 +88,27 @@ func TestProcessComponent(t *testing.T) { } func TestProcessComponentError(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + args := []interface{}{ "workspace", "fields.filter.edit.props.状态.::not-exist", "remote", map[string]interface{}{"select": []string{"name", "status"}, "limit": 2}, } - _, err := gou.NewProcess("yao.dashboard.Component", args...).Exec() + _, err := process.New("yao.dashboard.Component", args...).Exec() assert.Contains(t, err.Error(), "fields.filter.edit.props.状态.::not-exist") } func TestProcessSetting(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + args := []interface{}{"workspace"} - res, err := gou.NewProcess("yao.dashboard.Setting", args...).Exec() + res, err := process.New("yao.dashboard.Setting", args...).Exec() if err != nil { t.Fatal(err) } @@ -108,9 +121,12 @@ func TestProcessSetting(t *testing.T) { } func TestProcessXgen(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + args := []interface{}{"workspace"} - res, err := gou.NewProcess("yao.dashboard.Xgen", args...).Exec() + res, err := process.New("yao.dashboard.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -123,7 +139,10 @@ func TestProcessXgen(t *testing.T) { } func TestProcessXgenWithPermissions(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + session.Global().Set("__permissions", map[string]interface{}{ "dashboards.workspace": []string{ "7f46a38d7ff3f1832375ff63cd412f41", // operation.actions[0] 跳转至大屏 @@ -134,7 +153,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { }) args := []interface{}{"workspace"} - res, err := gou.NewProcess("yao.dashboard.Xgen", args...).Exec() + res, err := process.New("yao.dashboard.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -147,7 +166,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { assert.Equal(t, nil, data.Get("fields.dashboard.图表展示2")) session.Global().Set("__permissions", nil) - res, err = gou.NewProcess("yao.dashboard.Xgen", args...).Exec() + res, err = process.New("yao.dashboard.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -160,19 +179,8 @@ func TestProcessXgenWithPermissions(t *testing.T) { assert.NotEqual(t, nil, data.Get("fields.dashboard.图表展示2")) } -func load(t *testing.T) { - prepare(t) - err := Load(config.Conf) - if err != nil { - t.Fatal(err) - } - q.Load(config.Conf) - clear(t) - testData(t) -} - func testData(t *testing.T) { - pet := gou.Select("pet") + pet := model.Select("pet") err := pet.Insert( []string{"name", "type", "status", "mode", "stay", "cost", "doctor_id"}, [][]interface{}{ @@ -187,7 +195,7 @@ func testData(t *testing.T) { } func clear(t *testing.T) { - for _, m := range gou.Models { + for _, m := range model.Models { err := m.DropTable() if err != nil { t.Fatal(err) diff --git a/widgets/environment/environment.go b/widgets/environment/environment.go deleted file mode 100644 index 73d9a94d..00000000 --- a/widgets/environment/environment.go +++ /dev/null @@ -1,99 +0,0 @@ -package environment - -import ( - "fmt" - "io/ioutil" - "os" - "strings" - - jsoniter "github.com/json-iterator/go" -) - -// ReadFile open file and replace the content $ENV.XXX -func ReadFile(file string, defaults ...map[string]interface{}) ([]byte, error) { - content := map[string]interface{}{} - bytes, err := ioutil.ReadFile(file) - if err != nil { - return nil, err - } - - err = jsoniter.Unmarshal(bytes, &content) - if err != nil { - return nil, err - } - - defaultValues := map[string]interface{}{} - if len(defaults) > 0 { - defaultValues = defaults[0] - } - - Replace(content, defaultValues) - return jsoniter.Marshal(content) -} - -// Replace replace content -func Replace(content map[string]interface{}, defaults map[string]interface{}) { - replaceMap(content, defaults) -} - -func replaceAny(value interface{}, defaults map[string]interface{}) interface{} { - switch value.(type) { - case string: - return replaceStr(value.(string), defaults) - - case map[string]interface{}: - return replaceMap(value.(map[string]interface{}), defaults) - - case []interface{}: - return replaceArr(value.([]interface{}), defaults) - } - return value -} - -func replaceStr(value string, defaults map[string]interface{}) interface{} { - - v := strings.TrimSpace(value) - if strings.HasPrefix(v, "\\$$ENV.") { - return fmt.Sprintf("$ENV.%s", strings.TrimLeft(v, "\\$$ENV.")) - } - - if !strings.HasPrefix(v, "$ENV.") { - return value - } - - return getEnv(v, defaults) -} - -func getEnv(value string, defaults map[string]interface{}) interface{} { - - vars := strings.Split(value, "||") - name := strings.TrimLeft(strings.TrimSpace(vars[0]), "$ENV.") - v := os.Getenv(name) - if v != "" { - return v - } - - if len(vars) > 1 { - v = strings.TrimSpace(vars[1]) - } - - if v == "" { - return defaults[name] - } - - return v -} - -func replaceMap(values map[string]interface{}, defaults map[string]interface{}) map[string]interface{} { - for key, value := range values { - values[key] = replaceAny(value, defaults) - } - return values -} - -func replaceArr(values []interface{}, defaults map[string]interface{}) []interface{} { - for key, value := range values { - values[key] = replaceAny(value, defaults) - } - return values -} diff --git a/widgets/environment/environment_test.go b/widgets/environment/environment_test.go deleted file mode 100644 index 977af03c..00000000 --- a/widgets/environment/environment_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package environment - -import ( - "path/filepath" - "testing" - - jsoniter "github.com/json-iterator/go" - "github.com/stretchr/testify/assert" - "github.com/yaoapp/kun/maps" - "github.com/yaoapp/yao/config" -) - -func TestReadFile(t *testing.T) { - file := filepath.Join(config.Conf.Root, "tables", "env.tab.json") - defaults := map[string]interface{}{"PAGE_STATUS": "cured"} - content, err := ReadFile(file, defaults) - if err != nil { - t.Fatal(err) - } - - res := maps.MapStr{} - err = jsoniter.Unmarshal(content, &res) - if err != nil { - t.Fatal(err) - } - - res = res.Dot() - // utils.Dump(res) - - assert.Equal(t, "::PET ADMIN", res.Get("name")) - assert.Equal(t, "20", res.Get("action.search.default.2")) - assert.Equal(t, "https://yaoapps.com", res.Get("layout.header.preset.import.operation.0.link")) - assert.Equal(t, "cured", res.Get("layout.table.operation.actions[2].action[0].payload.status")) - assert.Equal(t, "提示", res["layout.table.operation.actions[5].confirm.title"]) - assert.Equal(t, "icon-trash", res["layout.table.operation.actions[5].icon"]) -} diff --git a/widgets/expression/expression.go b/widgets/expression/expression.go index 8f23a9e3..6d1476ca 100644 --- a/widgets/expression/expression.go +++ b/widgets/expression/expression.go @@ -6,7 +6,7 @@ import ( "regexp" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/maps" ) @@ -167,18 +167,18 @@ func replace(value string, data maps.MapStrAny) (interface{}, bool) { return value, replaced } -func computeOf(process string, argsvars []string, data maps.MapStrAny) (interface{}, bool) { +func computeOf(processName string, argsvars []string, data maps.MapStrAny) (interface{}, bool) { args := []interface{}{} for _, name := range argsvars { arg, _ := valueOf(strings.TrimSpace(name), data) args = append(args, arg) } - if !strings.Contains(process, ".") { - process = fmt.Sprintf("yao.expression.%s", process) + if !strings.Contains(processName, ".") { + processName = fmt.Sprintf("yao.expression.%s", processName) } - p, err := gou.ProcessOf(process, args...) + p, err := process.Of(processName, args...) if err != nil { return err.Error(), true } diff --git a/widgets/expression/process.go b/widgets/expression/process.go index db221ea4..df43b975 100644 --- a/widgets/expression/process.go +++ b/widgets/expression/process.go @@ -4,18 +4,18 @@ import ( "fmt" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/maps" ) // Export process func exportProcess() { - gou.RegisterProcessHandler("yao.expression.selectoption", processSelectOption) - gou.RegisterProcessHandler("yao.expression.trimspace", processTrimSpace) + process.Register("yao.expression.selectoption", processSelectOption) + process.Register("yao.expression.trimspace", processTrimSpace) } -func processSelectOption(process *gou.Process) interface{} { +func processSelectOption(process *process.Process) interface{} { process.ValidateArgNums(1) input := process.Args[0] switch input.(type) { @@ -69,7 +69,7 @@ func processSelectOption(process *gou.Process) interface{} { return []map[string]interface{}{} } -func processTrimSpace(process *gou.Process) interface{} { +func processTrimSpace(process *process.Process) interface{} { process.ValidateArgNums(1) input := process.ArgsString(0) return strings.TrimSpace(input) diff --git a/widgets/field/field.go b/widgets/field/field.go index 7400ca76..f90c2561 100644 --- a/widgets/field/field.go +++ b/widgets/field/field.go @@ -42,7 +42,7 @@ func LoadAndExport(cfg config.Config) error { func SelectTransform(name string) (*Transform, error) { trans, has := Transforms[name] if !has { - return nil, fmt.Errorf("SelectTransform %s does not found", name) + return nil, fmt.Errorf("Transform %s does not found", name) } return trans, nil } diff --git a/widgets/form/action.go b/widgets/form/action.go index e1abdcd0..edac2c84 100644 --- a/widgets/form/action.go +++ b/widgets/form/action.go @@ -3,7 +3,7 @@ package form import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/action" "github.com/yaoapp/yao/widgets/hook" "github.com/yaoapp/yao/widgets/table" @@ -120,7 +120,7 @@ func (act *ActionDSL) SetDefaultProcess() { } // BindModel bind model -func (act *ActionDSL) BindModel(m *gou.Model) { +func (act *ActionDSL) BindModel(m *model.Model) { name := m.ID act.Find.Bind(fmt.Sprintf("models.%s.Find", name)) diff --git a/widgets/form/api.go b/widgets/form/api.go index 088f98f3..cbe2de0b 100644 --- a/widgets/form/api.go +++ b/widgets/form/api.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" "github.com/yaoapp/yao/share" "github.com/yaoapp/yao/widgets/action" ) @@ -73,84 +73,84 @@ func (form *DSL) getAction(path string) (*action.Process, error) { // export API func exportAPI() error { - http := gou.HTTP{ + http := api.HTTP{ Name: "Widget Form API", Description: "Widget Form API", Version: share.VERSION, Guard: "widget-form", Group: "__yao/form", - Paths: []gou.Path{}, + Paths: []api.Path{}, } // GET /api/__yao/form/:id/setting -> Default process: yao.form.Xgen - path := gou.Path{ + path := api.Path{ Label: "Setting", Description: "Setting", Path: "/:id/setting", Method: "GET", Process: "yao.form.Setting", - In: []string{"$param.id"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/form/:id/find/:primary -> Default process: yao.form.Find $param.id $param.primary :query - path = gou.Path{ + path = api.Path{ Label: "Find", Description: "Find", Path: "/:id/find/:primary", Method: "GET", Process: "yao.form.Find", - In: []string{"$param.id", "$param.primary", ":query-param"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.primary", ":query-param"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/form/:id/component/:xpath/:method -> Default process: yao.form.Component $param.id $param.xpath $param.method :query - path = gou.Path{ + path = api.Path{ Label: "Component", Description: "Component", Path: "/:id/component/:xpath/:method", Method: "GET", Process: "yao.form.Component", - In: []string{"$param.id", "$param.xpath", "$param.method", ":query"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", ":query"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/form/:id/component/:xpath/:method -> Default process: yao.form.Component $param.id $param.xpath $param.method :payload - path = gou.Path{ + path = api.Path{ Label: "Component", Description: "Component", Path: "/:id/component/:xpath/:method", Method: "POST", Process: "yao.form.Component", - In: []string{"$param.id", "$param.xpath", "$param.method", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/upload/:xpath/:method -> Default process: yao.form.Upload $param.id $param.xpath $param.method $file.file - path = gou.Path{ + path = api.Path{ Label: "Upload", Description: "Upload", Path: "/:id/upload/:xpath/:method", Method: "POST", Process: "yao.form.Upload", - In: []string{"$param.id", "$param.xpath", "$param.method", "$file.file"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", "$file.file"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/form/:id/download/:field -> Default process: yao.form.Download $param.id $param.xpath $param.field $query.name $query.token - path = gou.Path{ + path = api.Path{ Label: "Download", Description: "Download", Path: "/:id/download/:field", Method: "GET", Process: "yao.form.Download", - In: []string{"$param.id", "$param.field", "$query.name", "$query.token"}, - Out: gou.Out{ + In: []interface{}{"$param.id", "$param.field", "$query.name", "$query.token"}, + Out: api.Out{ Status: 200, Body: "{{content}}", Headers: map[string]string{"Content-Type": "{{type}}"}, @@ -159,50 +159,50 @@ func exportAPI() error { http.Paths = append(http.Paths, path) // POST /api/__yao/form/:id/save -> Default process: yao.form.Save $param.id :payload - path = gou.Path{ + path = api.Path{ Label: "Save", Description: "Save", Path: "/:id/save", Method: "POST", Process: "yao.form.Save", - In: []string{"$param.id", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/form/:id/create -> Default process: yao.form.Create $param.id :payload - path = gou.Path{ + path = api.Path{ Label: "Create", Description: "Create", Path: "/:id/create", Method: "POST", Process: "yao.form.Create", - In: []string{"$param.id", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/form/:id/update/:primary -> Default process: yao.form.Update $param.id $param.primary :payload - path = gou.Path{ + path = api.Path{ Label: "Update", Description: "Update", Path: "/:id/update/:primary", Method: "POST", Process: "yao.form.Update", - In: []string{"$param.id", "$param.primary", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.primary", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/form/:id/delete/:primary -> Default process: yao.form.Delete $param.id $param.primary - path = gou.Path{ + path = api.Path{ Label: "Delete", Description: "Delete", Path: "/:id/delete/:primary", Method: "POST", Process: "yao.form.Delete", - In: []string{"$param.id", "$param.primary"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.primary"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) @@ -213,6 +213,6 @@ func exportAPI() error { } // load apis - _, err = gou.LoadAPIReturn(string(source), "widgets.form") + _, err = api.LoadSource(".yao", source, "widgets.form") return err } diff --git a/widgets/form/bind.go b/widgets/form/bind.go index 58aec8dc..1930567f 100644 --- a/widgets/form/bind.go +++ b/widgets/form/bind.go @@ -3,7 +3,7 @@ package form import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/table" ) @@ -36,7 +36,7 @@ func (dsl *DSL) Bind() error { func (dsl *DSL) bindModel() error { id := dsl.Action.Bind.Model - m, has := gou.Models[id] + m, has := model.Models[id] if !has { return fmt.Errorf("%s does not exist", id) } diff --git a/widgets/form/fields.go b/widgets/form/fields.go index 08b19bea..f0b3b620 100644 --- a/widgets/form/fields.go +++ b/widgets/form/fields.go @@ -4,14 +4,14 @@ import ( "fmt" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/component" "github.com/yaoapp/yao/widgets/field" "github.com/yaoapp/yao/widgets/table" ) // BindModel bind model -func (fields *FieldsDSL) BindModel(m *gou.Model) error { +func (fields *FieldsDSL) BindModel(m *model.Model) error { fields.formMap = map[string]field.ColumnDSL{} @@ -27,6 +27,10 @@ func (fields *FieldsDSL) BindModel(m *gou.Model) error { return err } + if fields.Form == nil { + fields.Form = field.Columns{} + } + // append columns if _, has := fields.Form[formField.Key]; !has { fields.Form[formField.Key] = *formField diff --git a/widgets/form/form.go b/widgets/form/form.go index 34e24cbe..5ec4fdfa 100644 --- a/widgets/form/form.go +++ b/widgets/form/form.go @@ -6,13 +6,13 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" "github.com/yaoapp/yao/widgets/component" - "github.com/yaoapp/yao/widgets/environment" "github.com/yaoapp/yao/widgets/field" ) @@ -74,31 +74,18 @@ func LoadAndExport(cfg config.Config) error { // Load load task func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "forms") - return LoadFrom(root, "") -} - -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - messages := []string{} - err := share.Walk(dir, ".json", func(root, filename string) { - id := prefix + share.ID(root, filename) - data, err := environment.ReadFile(filename) - if err != nil { + exts := []string{"*.form.yao", "*.form.json", "*.form.jsonc"} + err := application.App.Walk("forms", func(root, file string, isdir bool) error { + if isdir { + return nil + } + if err := LoadFile(root, file); err != nil { messages = append(messages, err.Error()) - return } - err = LoadData(data, id, filepath.Dir(dir)) - if err != nil { - messages = append(messages, err.Error()) - } - }) + return nil + }, exts...) if len(messages) > 0 { return fmt.Errorf(strings.Join(messages, ";\n")) @@ -107,31 +94,55 @@ func LoadFrom(dir string, prefix string) error { return err } +// LoadFile load table dsl by file +func LoadFile(root string, file string) error { + + id := share.ID(root, file) + data, err := application.App.Read(file) + if err != nil { + return err + } + + dsl := New(id) + err = application.Parse(file, data, dsl) + if err != nil { + return fmt.Errorf("[%s] %s", id, err.Error()) + } + + err = dsl.parse(id, root) + if err != nil { + return err + } + + Forms[id] = dsl + return nil +} + // LoadID load via id func LoadID(id string, root string) error { - dirs := strings.Split(id, ".") - name := fmt.Sprintf("%s.form.json", dirs[len(dirs)-1]) - elems := []string{root} - elems = append(elems, dirs[0:len(dirs)-1]...) - elems = append(elems, "forms", name) - filename := filepath.Join(elems...) - data, err := environment.ReadFile(filename) - if err != nil { - return fmt.Errorf("[Form] LoadID %s root=%s %s", id, root, err.Error()) + + file := filepath.Join("forms", share.File(id, ".form.yao")) + if exists, _ := application.App.Exists(file); exists { + return LoadFile("forms", file) } - return LoadData(data, id, root) + + file = filepath.Join("forms", share.File(id, ".form.jsonc")) + if exists, _ := application.App.Exists(file); exists { + return LoadFile("forms", file) + } + + file = filepath.Join("forms", share.File(id, ".form.json")) + if exists, _ := application.App.Exists(file); exists { + return LoadFile("forms", file) + } + + return fmt.Errorf("form %s not found", id) } // LoadData load via data -func LoadData(data []byte, id string, root string) error { - dsl := New(id) +func (dsl *DSL) parse(id string, root string) error { + dsl.Root = root - - err := jsoniter.Unmarshal(data, dsl) - if err != nil { - return fmt.Errorf("[Form] LoadData %s %s", id, err.Error()) - } - if dsl.Action == nil { dsl.Action = &ActionDSL{} } @@ -146,7 +157,7 @@ func LoadData(data []byte, id string, root string) error { } // Bind model / store / table / ... - err = dsl.Bind() + err := dsl.Bind() if err != nil { return fmt.Errorf("[Form] LoadData Bind %s %s", id, err.Error()) } @@ -173,8 +184,8 @@ func Get(form interface{}) (*DSL, error) { switch form.(type) { case string: id = form.(string) - case *gou.Process: - id = form.(*gou.Process).ArgsString(0) + case *process.Process: + id = form.(*process.Process).ArgsString(0) default: return nil, fmt.Errorf("%v type does not support", form) } @@ -198,6 +209,10 @@ func MustGet(form interface{}) *DSL { // Xgen trans to xgen setting func (dsl *DSL) Xgen(data map[string]interface{}, excludes map[string]bool) (map[string]interface{}, error) { + if dsl.Config == nil { + dsl.Config = map[string]interface{}{} + } + if dsl.Layout == nil { dsl.Layout = &LayoutDSL{Form: &ViewLayoutDSL{}} } diff --git a/widgets/form/form_test.go b/widgets/form/form_test.go index fa65d06f..cb2db57f 100644 --- a/widgets/form/form_test.go +++ b/widgets/form/form_test.go @@ -1,24 +1,24 @@ package form import ( + "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/fs" - "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/script" - "github.com/yaoapp/yao/share" - "github.com/yaoapp/yao/table" + "github.com/yaoapp/yao/flow" + "github.com/yaoapp/yao/test" + "github.com/yaoapp/yao/widgets/app" "github.com/yaoapp/yao/widgets/expression" "github.com/yaoapp/yao/widgets/field" - "github.com/yaoapp/yao/widgets/test" + "github.com/yaoapp/yao/widgets/table" ) func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() prepare(t) + err := Load(config.Conf) if err != nil { t.Fatal(err) @@ -26,31 +26,28 @@ func TestLoad(t *testing.T) { assert.Equal(t, 10, len(Forms)) } +func TestLoadID(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + + err := LoadID("pet", filepath.Join(config.Conf.Root)) + if err != nil { + t.Fatal(err) + } +} + func prepare(t *testing.T, language ...string) { - runtime.Load(config.Conf) - err := test.LoadEngine(language...) + // load flows + err := flow.Load(config.Conf) if err != nil { t.Fatal(err) } - i18n.Load(config.Conf) - share.DBConnect(config.Conf.DB) // removed later - - // load fs - err = fs.Load(config.Conf) - if err != nil { - t.Fatal(err) - } - - // load scripts - err = script.Load(config.Conf) - if err != nil { - t.Fatal(err) - } - - // load models - err = model.Load(config.Conf) + // load app + err = app.LoadAndExport(config.Conf) if err != nil { t.Fatal(err) } @@ -73,6 +70,11 @@ func prepare(t *testing.T, language ...string) { t.Fatal(err) } + err = Load(config.Conf) + if err != nil { + t.Fatal(err) + } + // export err = Export() if err != nil { diff --git a/widgets/form/handler.go b/widgets/form/handler.go index 03aa37ae..e130b7d8 100644 --- a/widgets/form/handler.go +++ b/widgets/form/handler.go @@ -4,7 +4,9 @@ import ( "fmt" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + gouProcess "github.com/yaoapp/gou/process" + "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/i18n" @@ -17,7 +19,7 @@ import ( // Life-Circle: Before Hook → Compute Edit → Run Process → Compute View → After Hook // Execute Compute Edit On: Save, Create, Update // Execute Compute View On: Find -func processHandler(p *action.Process, process *gou.Process) (interface{}, error) { +func processHandler(p *action.Process, process *gouProcess.Process) (interface{}, error) { form, err := Get(process) if err != nil { @@ -55,7 +57,7 @@ func processHandler(p *action.Process, process *gou.Process) (interface{}, error } // Execute Process - act, err := gou.ProcessOf(name, args...) + act, err := gouProcess.Of(name, args...) if err != nil { log.Error("[form] %s %s -> %s %s", form.ID, p.Name, name, err.Error()) return nil, fmt.Errorf("[form] %s %s -> %s %s", form.ID, p.Name, name, err.Error()) @@ -95,7 +97,7 @@ func processHandler(p *action.Process, process *gou.Process) (interface{}, error } // translateSetting -func (dsl *DSL) translate(name string, process *gou.Process, data interface{}) (interface{}, error) { +func (dsl *DSL) translate(name string, process *gouProcess.Process, data interface{}) (interface{}, error) { if strings.ToLower(name) != "yao.form.setting" { return data, nil @@ -103,7 +105,7 @@ func (dsl *DSL) translate(name string, process *gou.Process, data interface{}) ( widgets := []string{} if dsl.Action.Bind.Model != "" { - m := gou.Select(dsl.Action.Bind.Model) + m := model.Select(dsl.Action.Bind.Model) widgets = append(widgets, fmt.Sprintf("model.%s", m.ID)) } @@ -116,7 +118,7 @@ func (dsl *DSL) translate(name string, process *gou.Process, data interface{}) ( } widgets = append(widgets, fmt.Sprintf("form.%s", dsl.ID)) - res, err := i18n.Trans(process.Lang(config.Conf.Lang), widgets, data) + res, err := i18n.Trans(session.Lang(process, config.Conf.Lang), widgets, data) if err != nil { return nil, err } diff --git a/widgets/form/layout.go b/widgets/form/layout.go index 1cecc4c8..52bc4adb 100644 --- a/widgets/form/layout.go +++ b/widgets/form/layout.go @@ -4,14 +4,14 @@ import ( "fmt" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/component" "github.com/yaoapp/yao/widgets/mapping" "github.com/yaoapp/yao/widgets/table" ) // BindModel bind model -func (layout *LayoutDSL) BindModel(m *gou.Model, formID string, fields *FieldsDSL, option map[string]interface{}) { +func (layout *LayoutDSL) BindModel(m *model.Model, formID string, fields *FieldsDSL, option map[string]interface{}) { if layout.Primary == "" { layout.Primary = m.PrimaryKey } diff --git a/widgets/form/process.go b/widgets/form/process.go index d1f6419e..92cb8738 100644 --- a/widgets/form/process.go +++ b/widgets/form/process.go @@ -5,8 +5,9 @@ import ( "net/url" "strings" - "github.com/yaoapp/gou" "github.com/yaoapp/gou/fs" + gouProcess "github.com/yaoapp/gou/process" + "github.com/yaoapp/gou/types" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/helper" @@ -15,19 +16,19 @@ import ( // Export process func exportProcess() { - gou.RegisterProcessHandler("yao.form.setting", processSetting) - gou.RegisterProcessHandler("yao.form.xgen", processXgen) - gou.RegisterProcessHandler("yao.form.component", processComponent) - gou.RegisterProcessHandler("yao.form.upload", processUpload) - gou.RegisterProcessHandler("yao.form.download", processDownload) - gou.RegisterProcessHandler("yao.form.find", processFind) - gou.RegisterProcessHandler("yao.form.save", processSave) - gou.RegisterProcessHandler("yao.form.create", processCreate) - gou.RegisterProcessHandler("yao.form.update", processUpdate) - gou.RegisterProcessHandler("yao.form.delete", processDelete) + gouProcess.Register("yao.form.setting", processSetting) + gouProcess.Register("yao.form.xgen", processXgen) + gouProcess.Register("yao.form.component", processComponent) + gouProcess.Register("yao.form.upload", processUpload) + gouProcess.Register("yao.form.download", processDownload) + gouProcess.Register("yao.form.find", processFind) + gouProcess.Register("yao.form.save", processSave) + gouProcess.Register("yao.form.create", processCreate) + gouProcess.Register("yao.form.update", processUpdate) + gouProcess.Register("yao.form.delete", processDelete) } -func processXgen(process *gou.Process) interface{} { +func processXgen(process *gouProcess.Process) interface{} { form := MustGet(process) data := process.ArgsMap(1, map[string]interface{}{}) @@ -40,7 +41,7 @@ func processXgen(process *gou.Process) interface{} { return setting } -func processComponent(process *gou.Process) interface{} { +func processComponent(process *gouProcess.Process) interface{} { process.ValidateArgNums(3) form := MustGet(process) @@ -69,7 +70,7 @@ func processComponent(process *gou.Process) interface{} { return res } -func processDownload(process *gou.Process) interface{} { +func processDownload(process *gouProcess.Process) interface{} { process.ValidateArgNums(4) form := MustGet(process) @@ -97,7 +98,7 @@ func processDownload(process *gou.Process) interface{} { } // Create process - p, err := gou.ProcessOf(name, file) + p, err := gouProcess.Of(name, file) if err != nil { log.Error("[downalod] %s.%s %s", form.ID, field, err.Error()) exception.New("[downalod] %s.%s %s", 400, form.ID, field, err.Error()).Throw() @@ -113,7 +114,7 @@ func processDownload(process *gou.Process) interface{} { return res } -func processUpload(process *gou.Process) interface{} { +func processUpload(process *gouProcess.Process) interface{} { process.ValidateArgNums(4) form := MustGet(process) @@ -128,7 +129,7 @@ func processUpload(process *gou.Process) interface{} { } // $file.file - tmpfile, ok := process.Args[3].(gou.UploadFile) + tmpfile, ok := process.Args[3].(types.UploadFile) if !ok { exception.New("parameters error: %v", 400, process.Args[3]).Throw() } @@ -148,33 +149,33 @@ func processUpload(process *gou.Process) interface{} { return res } -func processSetting(process *gou.Process) interface{} { +func processSetting(process *gouProcess.Process) interface{} { form := MustGet(process) process.Args = append(process.Args, process.Args[0]) // form name return form.Action.Setting.MustExec(process) } -func processSave(process *gou.Process) interface{} { +func processSave(process *gouProcess.Process) interface{} { form := MustGet(process) return form.Action.Save.MustExec(process) } -func processCreate(process *gou.Process) interface{} { +func processCreate(process *gouProcess.Process) interface{} { form := MustGet(process) return form.Action.Create.MustExec(process) } -func processFind(process *gou.Process) interface{} { +func processFind(process *gouProcess.Process) interface{} { form := MustGet(process) return form.Action.Find.MustExec(process) } -func processUpdate(process *gou.Process) interface{} { +func processUpdate(process *gouProcess.Process) interface{} { form := MustGet(process) return form.Action.Update.MustExec(process) } -func processDelete(process *gou.Process) interface{} { +func processDelete(process *gouProcess.Process) interface{} { form := MustGet(process) return form.Action.Delete.MustExec(process) } diff --git a/widgets/form/process_test.go b/widgets/form/process_test.go index e10f19e9..7e7e3fe9 100644 --- a/widgets/form/process_test.go +++ b/widgets/form/process_test.go @@ -7,24 +7,29 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" "github.com/yaoapp/gou/fs" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" + "github.com/yaoapp/gou/types" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/maps" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/helper" - q "github.com/yaoapp/yao/query" + "github.com/yaoapp/yao/test" ) func TestProcessFind(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{"pet", 1} - res, err := gou.NewProcess("yao.form.find", args...).Exec() + res, err := process.New("yao.form.find", args...).Exec() if err != nil { t.Fatal(err) } @@ -34,7 +39,11 @@ func TestProcessFind(t *testing.T) { } func TestProcessSave(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{"pet", map[string]interface{}{ @@ -47,13 +56,13 @@ func TestProcessSave(t *testing.T) { "doctor_id": 1, }} - res, err := gou.NewProcess("yao.form.Save", args...).Exec() + res, err := process.New("yao.form.Save", args...).Exec() if err != nil { t.Fatal(err) } assert.Equal(t, "4", fmt.Sprintf("%v", res)) - res, err = gou.NewProcess("yao.form.find", "pet", res).Exec() + res, err = process.New("yao.form.find", "pet", res).Exec() if err != nil { t.Fatal(err) } @@ -63,7 +72,11 @@ func TestProcessSave(t *testing.T) { } func TestProcessCreate(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{"pet", map[string]interface{}{ @@ -77,13 +90,13 @@ func TestProcessCreate(t *testing.T) { "doctor_id": 1, }} - res, err := gou.NewProcess("yao.form.Create", args...).Exec() + res, err := process.New("yao.form.Create", args...).Exec() if err != nil { t.Fatal(err) } assert.Equal(t, "6", fmt.Sprintf("%v", res)) - res, err = gou.NewProcess("yao.form.find", "pet", res).Exec() + res, err = process.New("yao.form.find", "pet", res).Exec() if err != nil { t.Fatal(err) } @@ -93,7 +106,11 @@ func TestProcessCreate(t *testing.T) { } func TestProcessUpdate(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{"pet", 1, map[string]interface{}{ @@ -106,12 +123,12 @@ func TestProcessUpdate(t *testing.T) { "doctor_id": 1, }} - _, err := gou.NewProcess("yao.form.Update", args...).Exec() + _, err := process.New("yao.form.Update", args...).Exec() if err != nil { t.Fatal(err) } - res, err := gou.NewProcess("yao.form.find", "pet", 1).Exec() + res, err := process.New("yao.form.find", "pet", 1).Exec() if err != nil { t.Fatal(err) } @@ -121,22 +138,30 @@ func TestProcessUpdate(t *testing.T) { } func TestProcessDelete(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{"pet", 1} - _, err := gou.NewProcess("yao.form.Delete", args...).Exec() + _, err := process.New("yao.form.Delete", args...).Exec() if err != nil { t.Fatal(err) } - _, err = gou.NewProcess("yao.form.find", "pet", 1).Exec() + _, err = process.New("yao.form.find", "pet", 1).Exec() assert.Contains(t, err.Error(), "ID=1") } func TestProcessComponent(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{ @@ -146,7 +171,7 @@ func TestProcessComponent(t *testing.T) { map[string]interface{}{"select": []string{"name", "status"}, "limit": 2}, } - res, err := gou.NewProcess("yao.form.Component", args...).Exec() + res, err := process.New("yao.form.Component", args...).Exec() if err != nil { t.Fatal(err) } @@ -161,7 +186,11 @@ func TestProcessComponent(t *testing.T) { } func TestProcessComponentError(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{ @@ -170,22 +199,26 @@ func TestProcessComponentError(t *testing.T) { "remote", map[string]interface{}{"select": []string{"name", "status"}, "limit": 2}, } - _, err := gou.NewProcess("yao.form.Component", args...).Exec() + _, err := process.New("yao.form.Component", args...).Exec() assert.Contains(t, err.Error(), "fields.filter.edit.props.状态.::not-exist") } func TestProcessUpload(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{ "pet", "fields.form.相关图片.edit.props", "api", - gou.UploadFile{TempFile: tempFile(t)}, + types.UploadFile{TempFile: tempFile(t)}, } - res, err := gou.NewProcess("yao.form.Upload", args...).Exec() + res, err := process.New("yao.form.Upload", args...).Exec() if err != nil { t.Fatal(err) } @@ -196,7 +229,11 @@ func TestProcessUpload(t *testing.T) { } func TestProcessDownload(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) @@ -208,7 +245,7 @@ func TestProcessDownload(t *testing.T) { } args := []interface{}{"pet", "images", "/text.txt", jwt.Token} - res, err := gou.NewProcess("yao.form.Download", args...).Exec() + res, err := process.New("yao.form.Download", args...).Exec() if err != nil { t.Fatal(err) } @@ -220,11 +257,15 @@ func TestProcessDownload(t *testing.T) { } func TestProcessSetting(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{"pet"} - res, err := gou.NewProcess("yao.form.Setting", args...).Exec() + res, err := process.New("yao.form.Setting", args...).Exec() if err != nil { t.Fatal(err) } @@ -237,11 +278,15 @@ func TestProcessSetting(t *testing.T) { } func TestProcessXgen(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{"pet"} - res, err := gou.NewProcess("yao.form.Xgen", args...).Exec() + res, err := process.New("yao.form.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -254,7 +299,11 @@ func TestProcessXgen(t *testing.T) { } func TestProcessXgenWithPermissions(t *testing.T) { - load(t) + + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) @@ -266,7 +315,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { }) args := []interface{}{"pet"} - res, err := gou.NewProcess("yao.form.Xgen", args...).Exec() + res, err := process.New("yao.form.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -279,7 +328,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { assert.NotEqual(t, "删除", data.Get("actions[0].title")) session.Global().Set("__permissions", nil) - res, err = gou.NewProcess("yao.form.Xgen", args...).Exec() + res, err = process.New("yao.form.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -293,17 +342,8 @@ func TestProcessXgenWithPermissions(t *testing.T) { } -func load(t *testing.T) { - prepare(t) - err := Load(config.Conf) - if err != nil { - t.Fatal(err) - } - q.Load(config.Conf) -} - func testData(t *testing.T) { - pet := gou.Select("pet") + pet := model.Select("pet") err := pet.Insert( []string{"name", "type", "status", "mode", "stay", "cost", "doctor_id"}, [][]interface{}{ @@ -333,7 +373,7 @@ func tempFile(t *testing.T) string { } func clear(t *testing.T) { - for _, m := range gou.Models { + for _, m := range model.Models { err := m.DropTable() if err != nil { t.Fatal(err) diff --git a/widgets/hook/hook.go b/widgets/hook/hook.go index 97d6055d..08952eab 100644 --- a/widgets/hook/hook.go +++ b/widgets/hook/hook.go @@ -3,7 +3,7 @@ package hook import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) // CopyBefore copy a before hook @@ -23,7 +23,7 @@ func CopyAfter(hook *After, new *After) { // Exec execute the hook func (hook *Before) Exec(args []interface{}, sid string, global map[string]interface{}) ([]interface{}, error) { - p, err := gou.ProcessOf(hook.String(), args...) + p, err := process.Of(hook.String(), args...) if err != nil { return nil, fmt.Errorf("%s %s", hook.String(), err.Error()) } @@ -56,7 +56,7 @@ func (hook *After) Exec(value interface{}, sid string, global map[string]interfa args = append(args, value) } - p, err := gou.ProcessOf(hook.String(), args...) + p, err := process.Of(hook.String(), args...) if err != nil { return nil, fmt.Errorf("[%s] %s", hook.String(), err.Error()) } diff --git a/widgets/list/action.go b/widgets/list/action.go index a432ba2d..67123062 100644 --- a/widgets/list/action.go +++ b/widgets/list/action.go @@ -1,7 +1,7 @@ package list import ( - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/action" "github.com/yaoapp/yao/widgets/hook" "github.com/yaoapp/yao/widgets/table" @@ -74,7 +74,7 @@ func (act *ActionDSL) SetDefaultProcess() { } // BindModel bind model -func (act *ActionDSL) BindModel(m *gou.Model) error { +func (act *ActionDSL) BindModel(m *model.Model) error { return nil } diff --git a/widgets/list/api.go b/widgets/list/api.go index 1953dff3..d8de639f 100644 --- a/widgets/list/api.go +++ b/widgets/list/api.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" "github.com/yaoapp/yao/share" "github.com/yaoapp/yao/widgets/action" ) @@ -65,84 +65,84 @@ func (list *DSL) getAction(path string) (*action.Process, error) { // export API func exportAPI() error { - http := gou.HTTP{ + http := api.HTTP{ Name: "Widget List API", Description: "Widget List API", Version: share.VERSION, Guard: "widget-list", Group: "__yao/list", - Paths: []gou.Path{}, + Paths: []api.Path{}, } // GET /api/__yao/list/:id/setting -> Default process: yao.list.Xgen - path := gou.Path{ + path := api.Path{ Label: "Setting", Description: "Setting", Path: "/:id/setting", Method: "GET", Process: "yao.list.Setting", - In: []string{"$param.id"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/list/:id/find -> Default process: yao.list.Get $param.id :query - path = gou.Path{ + path = api.Path{ Label: "Get", Description: "Get", Path: "/:id/get", Method: "GET", Process: "yao.list.Find", - In: []string{"$param.id", ":query-param"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":query-param"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/list/:id/component/:xpath/:method -> Default process: yao.list.Component $param.id $param.xpath $param.method :query - path = gou.Path{ + path = api.Path{ Label: "Component", Description: "Component", Path: "/:id/component/:xpath/:method", Method: "GET", Process: "yao.list.Component", - In: []string{"$param.id", "$param.xpath", "$param.method", ":query"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", ":query"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/list/:id/component/:xpath/:method -> Default process: yao.list.Component $param.id $param.xpath $param.method :payload - path = gou.Path{ + path = api.Path{ Label: "Component", Description: "Component", Path: "/:id/component/:xpath/:method", Method: "POST", Process: "yao.list.Component", - In: []string{"$param.id", "$param.xpath", "$param.method", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/upload/:xpath/:method -> Default process: yao.list.Upload $param.id $param.xpath $param.method $file.file - path = gou.Path{ + path = api.Path{ Label: "Upload", Description: "Upload", Path: "/:id/upload/:xpath/:method", Method: "POST", Process: "yao.list.Upload", - In: []string{"$param.id", "$param.xpath", "$param.method", "$file.file"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", "$file.file"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/list/:id/download/:field -> Default process: yao.list.Download $param.id $param.xpath $param.field $query.name $query.token - path = gou.Path{ + path = api.Path{ Label: "Download", Description: "Download", Path: "/:id/download/:field", Method: "GET", Process: "yao.list.Download", - In: []string{"$param.id", "$param.field", "$query.name", "$query.token"}, - Out: gou.Out{ + In: []interface{}{"$param.id", "$param.field", "$query.name", "$query.token"}, + Out: api.Out{ Status: 200, Body: "{{content}}", Headers: map[string]string{"Content-Type": "{{type}}"}, @@ -151,14 +151,14 @@ func exportAPI() error { http.Paths = append(http.Paths, path) // POST /api/__yao/list/:id/save -> Default process: yao.list.Save $param.id :payload - path = gou.Path{ + path = api.Path{ Label: "Save", Description: "Save", Path: "/:id/save", Method: "POST", Process: "yao.list.Save", - In: []string{"$param.id", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) @@ -169,6 +169,6 @@ func exportAPI() error { } // load apis - _, err = gou.LoadAPIReturn(string(source), "widgets.list") + _, err = api.LoadSource(".yao", source, "widgets.list") return err } diff --git a/widgets/list/bind.go b/widgets/list/bind.go index 586b4ff2..09d02223 100644 --- a/widgets/list/bind.go +++ b/widgets/list/bind.go @@ -3,7 +3,7 @@ package list import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/table" ) @@ -32,7 +32,7 @@ func (dsl *DSL) Bind() error { func (dsl *DSL) bindModel() error { id := dsl.Action.Bind.Model - m, has := gou.Models[id] + m, has := model.Models[id] if !has { return fmt.Errorf("%s does not exist", id) } diff --git a/widgets/list/fields.go b/widgets/list/fields.go index c9a662e9..b500a2cb 100644 --- a/widgets/list/fields.go +++ b/widgets/list/fields.go @@ -4,12 +4,12 @@ import ( "fmt" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/table" ) // BindModel bind model -func (fields *FieldsDSL) BindModel(m *gou.Model) error { +func (fields *FieldsDSL) BindModel(m *model.Model) error { // fields.listMap = map[string]field.ColumnDSL{} diff --git a/widgets/list/handler.go b/widgets/list/handler.go index fd9f5091..608c5043 100644 --- a/widgets/list/handler.go +++ b/widgets/list/handler.go @@ -4,7 +4,9 @@ import ( "fmt" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + gouProcess "github.com/yaoapp/gou/process" + "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/i18n" @@ -17,7 +19,7 @@ import ( // Life-Circle: Before Hook → Compute Edit → Run Process → Compute View → After Hook // Execute Compute Edit On: Save // Execute Compute View On: Get -func processHandler(p *action.Process, process *gou.Process) (interface{}, error) { +func processHandler(p *action.Process, process *gouProcess.Process) (interface{}, error) { list, err := Get(process) if err != nil { @@ -55,7 +57,7 @@ func processHandler(p *action.Process, process *gou.Process) (interface{}, error } // Execute Process - act, err := gou.ProcessOf(name, args...) + act, err := gouProcess.Of(name, args...) if err != nil { log.Error("[list] %s %s -> %s %s", list.ID, p.Name, name, err.Error()) return nil, fmt.Errorf("[list] %s %s -> %s %s", list.ID, p.Name, name, err.Error()) @@ -95,7 +97,7 @@ func processHandler(p *action.Process, process *gou.Process) (interface{}, error } // translateSetting -func (dsl *DSL) translate(name string, process *gou.Process, data interface{}) (interface{}, error) { +func (dsl *DSL) translate(name string, process *gouProcess.Process, data interface{}) (interface{}, error) { if strings.ToLower(name) != "yao.list.setting" { return data, nil @@ -103,7 +105,7 @@ func (dsl *DSL) translate(name string, process *gou.Process, data interface{}) ( widgets := []string{} if dsl.Action.Bind.Model != "" { - m := gou.Select(dsl.Action.Bind.Model) + m := model.Select(dsl.Action.Bind.Model) widgets = append(widgets, fmt.Sprintf("model.%s", m.ID)) } @@ -112,7 +114,7 @@ func (dsl *DSL) translate(name string, process *gou.Process, data interface{}) ( } widgets = append(widgets, fmt.Sprintf("list.%s", dsl.ID)) - res, err := i18n.Trans(process.Lang(config.Conf.Lang), widgets, data) + res, err := i18n.Trans(session.Lang(process, config.Conf.Lang), widgets, data) if err != nil { return nil, err } diff --git a/widgets/list/layout.go b/widgets/list/layout.go index 012a9fee..fa94b965 100644 --- a/widgets/list/layout.go +++ b/widgets/list/layout.go @@ -2,14 +2,14 @@ package list import ( jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/component" "github.com/yaoapp/yao/widgets/mapping" "github.com/yaoapp/yao/widgets/table" ) // BindModel bind model -func (layout *LayoutDSL) BindModel(m *gou.Model, listID string, fields *FieldsDSL, option map[string]interface{}) { +func (layout *LayoutDSL) BindModel(m *model.Model, listID string, fields *FieldsDSL, option map[string]interface{}) { // if layout.Primary == "" { // layout.Primary = m.PrimaryKey // } diff --git a/widgets/list/list.go b/widgets/list/list.go index 5f082ecc..e1b93eb1 100644 --- a/widgets/list/list.go +++ b/widgets/list/list.go @@ -6,13 +6,13 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" "github.com/yaoapp/yao/widgets/component" - "github.com/yaoapp/yao/widgets/environment" "github.com/yaoapp/yao/widgets/field" ) @@ -66,31 +66,18 @@ func LoadAndExport(cfg config.Config) error { // Load load task func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "lists") - return LoadFrom(root, "") -} - -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - messages := []string{} - err := share.Walk(dir, ".json", func(root, filename string) { - id := prefix + share.ID(root, filename) - data, err := environment.ReadFile(filename) - if err != nil { + exts := []string{"*.yao", "*.json", "*.jsonc"} + err := application.App.Walk("lists", func(root, file string, isdir bool) error { + if isdir { + return nil + } + if err := LoadFile(root, file); err != nil { messages = append(messages, err.Error()) - return } - err = LoadData(data, id, filepath.Dir(dir)) - if err != nil { - messages = append(messages, err.Error()) - } - }) + return nil + }, exts...) if len(messages) > 0 { return fmt.Errorf(strings.Join(messages, ";\n")) @@ -99,30 +86,52 @@ func LoadFrom(dir string, prefix string) error { return err } +// LoadFile load table dsl by file +func LoadFile(root string, file string) error { + + id := share.ID(root, file) + data, err := application.App.Read(file) + if err != nil { + return err + } + + dsl := New(id) + err = application.Parse(file, data, dsl) + if err != nil { + return fmt.Errorf("[%s] %s", id, err.Error()) + } + + err = dsl.parse(id, root) + if err != nil { + return err + } + + Lists[id] = dsl + return nil +} + // LoadID load via id func LoadID(id string, root string) error { - dirs := strings.Split(id, ".") - name := fmt.Sprintf("%s.list.json", dirs[len(dirs)-1]) - elems := []string{root} - elems = append(elems, dirs[0:len(dirs)-1]...) - elems = append(elems, "lists", name) - filename := filepath.Join(elems...) - data, err := environment.ReadFile(filename) - if err != nil { - return fmt.Errorf("[List] LoadID %s root=%s %s", id, root, err.Error()) + file := filepath.Join("lists", share.File(id, ".yao")) + if exists, _ := application.App.Exists(file); exists { + return LoadFile("lists", file) } - return LoadData(data, id, root) + + file = filepath.Join("lists", share.File(id, ".jsonc")) + if exists, _ := application.App.Exists(file); exists { + return LoadFile("lists", file) + } + + file = filepath.Join("lists", share.File(id, ".json")) + if exists, _ := application.App.Exists(file); exists { + return LoadFile("lists", file) + } + + return fmt.Errorf("list %s not found", id) } // LoadData load via data -func LoadData(data []byte, id string, root string) error { - dsl := New(id) - dsl.Root = root - - err := jsoniter.Unmarshal(data, dsl) - if err != nil { - return fmt.Errorf("[List] LoadData %s %s", id, err.Error()) - } +func (dsl *DSL) parse(id string, root string) error { if dsl.Action == nil { dsl.Action = &ActionDSL{} @@ -138,7 +147,7 @@ func LoadData(data []byte, id string, root string) error { } // Bind model / store / list / ... - err = dsl.Bind() + err := dsl.Bind() if err != nil { return fmt.Errorf("[List] LoadData Bind %s %s", id, err.Error()) } @@ -165,8 +174,8 @@ func Get(list interface{}) (*DSL, error) { switch list.(type) { case string: id = list.(string) - case *gou.Process: - id = list.(*gou.Process).ArgsString(0) + case *process.Process: + id = list.(*process.Process).ArgsString(0) default: return nil, fmt.Errorf("%v type does not support", list) } diff --git a/widgets/list/list_test.go b/widgets/list/list_test.go index 5595ce43..b913011b 100644 --- a/widgets/list/list_test.go +++ b/widgets/list/list_test.go @@ -7,17 +7,15 @@ import ( "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/fs" "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/script" - "github.com/yaoapp/yao/share" - "github.com/yaoapp/yao/table" + "github.com/yaoapp/yao/test" "github.com/yaoapp/yao/widgets/expression" "github.com/yaoapp/yao/widgets/field" - "github.com/yaoapp/yao/widgets/test" + "github.com/yaoapp/yao/widgets/table" ) func TestLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() prepare(t) err := Load(config.Conf) if err != nil { @@ -28,29 +26,10 @@ func TestLoad(t *testing.T) { func prepare(t *testing.T, language ...string) { - runtime.Load(config.Conf) - err := test.LoadEngine(language...) - if err != nil { - t.Fatal(err) - } - i18n.Load(config.Conf) - share.DBConnect(config.Conf.DB) // removed later // load fs - err = fs.Load(config.Conf) - if err != nil { - t.Fatal(err) - } - - // load scripts - err = script.Load(config.Conf) - if err != nil { - t.Fatal(err) - } - - // load models - err = model.Load(config.Conf) + err := fs.Load(config.Conf) if err != nil { t.Fatal(err) } diff --git a/widgets/list/process.go b/widgets/list/process.go index b30ae9b2..705d02cb 100644 --- a/widgets/list/process.go +++ b/widgets/list/process.go @@ -5,8 +5,9 @@ import ( "net/url" "strings" - "github.com/yaoapp/gou" "github.com/yaoapp/gou/fs" + gouProcess "github.com/yaoapp/gou/process" + "github.com/yaoapp/gou/types" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/helper" @@ -15,15 +16,15 @@ import ( // Export process func exportProcess() { - gou.RegisterProcessHandler("yao.list.setting", processSetting) - gou.RegisterProcessHandler("yao.list.xgen", processXgen) - gou.RegisterProcessHandler("yao.list.component", processComponent) - gou.RegisterProcessHandler("yao.list.upload", processUpload) - gou.RegisterProcessHandler("yao.list.download", processDownload) - gou.RegisterProcessHandler("yao.list.save", processSave) + gouProcess.Register("yao.list.setting", processSetting) + gouProcess.Register("yao.list.xgen", processXgen) + gouProcess.Register("yao.list.component", processComponent) + gouProcess.Register("yao.list.upload", processUpload) + gouProcess.Register("yao.list.download", processDownload) + gouProcess.Register("yao.list.save", processSave) } -func processXgen(process *gou.Process) interface{} { +func processXgen(process *gouProcess.Process) interface{} { list := MustGet(process) data := process.ArgsMap(1, map[string]interface{}{}) @@ -36,7 +37,7 @@ func processXgen(process *gou.Process) interface{} { return setting } -func processComponent(process *gou.Process) interface{} { +func processComponent(process *gouProcess.Process) interface{} { process.ValidateArgNums(3) list := MustGet(process) @@ -65,7 +66,7 @@ func processComponent(process *gou.Process) interface{} { return res } -func processDownload(process *gou.Process) interface{} { +func processDownload(process *gouProcess.Process) interface{} { process.ValidateArgNums(4) list := MustGet(process) @@ -93,7 +94,7 @@ func processDownload(process *gou.Process) interface{} { } // Create process - p, err := gou.ProcessOf(name, file) + p, err := gouProcess.Of(name, file) if err != nil { log.Error("[downalod] %s.%s %s", list.ID, field, err.Error()) exception.New("[downalod] %s.%s %s", 400, list.ID, field, err.Error()).Throw() @@ -109,7 +110,7 @@ func processDownload(process *gou.Process) interface{} { return res } -func processUpload(process *gou.Process) interface{} { +func processUpload(process *gouProcess.Process) interface{} { process.ValidateArgNums(4) list := MustGet(process) @@ -124,7 +125,7 @@ func processUpload(process *gou.Process) interface{} { } // $file.file - tmpfile, ok := process.Args[3].(gou.UploadFile) + tmpfile, ok := process.Args[3].(types.UploadFile) if !ok { exception.New("parameters error: %v", 400, process.Args[3]).Throw() } @@ -144,18 +145,18 @@ func processUpload(process *gou.Process) interface{} { return res } -func processSetting(process *gou.Process) interface{} { +func processSetting(process *gouProcess.Process) interface{} { list := MustGet(process) process.Args = append(process.Args, process.Args[0]) // list name return list.Action.Setting.MustExec(process) } -func processGet(process *gou.Process) interface{} { +func processGet(process *gouProcess.Process) interface{} { list := MustGet(process) return list.Action.Get.MustExec(process) } -func processSave(process *gou.Process) interface{} { +func processSave(process *gouProcess.Process) interface{} { list := MustGet(process) return list.Action.Save.MustExec(process) } diff --git a/widgets/list/process_test.go b/widgets/list/process_test.go index 9cb08240..5605028f 100644 --- a/widgets/list/process_test.go +++ b/widgets/list/process_test.go @@ -6,19 +6,23 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" "github.com/yaoapp/yao/config" - q "github.com/yaoapp/yao/query" + "github.com/yaoapp/yao/test" ) func TestProcessSetting(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + load(t) clear(t) testData(t) args := []interface{}{"category"} - res, err := gou.NewProcess("yao.list.Setting", args...).Exec() + res, err := process.New("yao.list.Setting", args...).Exec() if err != nil { t.Fatal(err) } @@ -29,11 +33,14 @@ func TestProcessSetting(t *testing.T) { } func TestProcessXgen(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + load(t) clear(t) testData(t) args := []interface{}{"category"} - res, err := gou.NewProcess("yao.list.Xgen", args...).Exec() + res, err := process.New("yao.list.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -44,6 +51,9 @@ func TestProcessXgen(t *testing.T) { } func TestProcessXgenWithPermissions(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + load(t) clear(t) testData(t) @@ -55,7 +65,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { }) args := []interface{}{"category"} - res, err := gou.NewProcess("yao.list.Xgen", args...).Exec() + res, err := process.New("yao.list.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -66,7 +76,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { assert.False(t, data.Has("fields.list.库存预警")) session.Global().Set("__permissions", nil) - res, err = gou.NewProcess("yao.list.Xgen", args...).Exec() + res, err = process.New("yao.list.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -84,11 +94,10 @@ func load(t *testing.T) { if err != nil { t.Fatal(err) } - q.Load(config.Conf) } func testData(t *testing.T) { - category := gou.Select("category") + category := model.Select("category") err := category.Insert( []string{"name", "stock", "status", "rank"}, [][]interface{}{ @@ -118,7 +127,7 @@ func tempFile(t *testing.T) string { } func clear(t *testing.T) { - for _, m := range gou.Models { + for _, m := range model.Models { err := m.DropTable() if err != nil { t.Fatal(err) diff --git a/widgets/login/login.go b/widgets/login/login.go index 660d28ac..6e1879a3 100644 --- a/widgets/login/login.go +++ b/widgets/login/login.go @@ -2,11 +2,10 @@ package login import ( "fmt" - "path/filepath" - "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" + "github.com/yaoapp/gou/application" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" ) @@ -31,36 +30,32 @@ func LoadAndExport(cfg config.Config) error { // Load load login func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "logins") - return LoadFrom(root, "") + exts := []string{"*.login.yao", "*.login.json", "*.login.jsonc"} + return application.App.Walk("logins", func(root, file string, isdir bool) error { + if isdir { + return nil + } + return LoadFile(root, file) + }, exts...) } -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { +// LoadFile by dsl file +func LoadFile(root string, file string) error { - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) + id := share.ID(root, file) + data, err := application.App.Read(file) + if err != nil { + return err } - messages := []string{} - err := share.Walk(dir, ".json", func(root, filename string) { - id := prefix + share.ID(root, filename) - data := share.ReadFile(filename) - dsl := &DSL{ID: id} - err := jsoniter.Unmarshal(data, dsl) - if err != nil { - messages = append(messages, fmt.Sprintf("[%s] %s", id, err.Error())) - return - } - - Logins[id] = dsl - }) - - if len(messages) > 0 { - return fmt.Errorf(strings.Join(messages, ";")) + dsl := &DSL{ID: id} + err = application.Parse(file, data, dsl) + if err != nil { + return fmt.Errorf("[%s] %s", id, err.Error()) } - return err + Logins[id] = dsl + return nil } // Export export login api @@ -72,25 +67,25 @@ func Export() error { // exportAPI export login api func exportAPI() error { - http := gou.HTTP{ + http := api.HTTP{ Name: "Widget Login API", Description: "Widget Login API", Version: share.VERSION, Guard: "bearer-jwt", Group: "__yao/login", - Paths: []gou.Path{}, + Paths: []api.Path{}, } for _, dsl := range Logins { // login action process := "yao.login.Admin" - args := []string{":payload"} + args := []interface{}{":payload"} if dsl.Action.Process != "" { process = dsl.Action.Process args = dsl.Action.Args } - path := gou.Path{ + path := api.Path{ Label: fmt.Sprintf("%s login", dsl.ID), Description: fmt.Sprintf("%s login", dsl.ID), Guard: "-", @@ -98,18 +93,18 @@ func exportAPI() error { Method: "POST", Process: process, In: args, - Out: gou.Out{Status: 200, Type: "application/json"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // captcha process = "yao.utils.Captcha" - args = []string{":query"} + args = []interface{}{":query"} if dsl.Layout.Captcha != "" { process = dsl.Layout.Captcha } - path = gou.Path{ + path = api.Path{ Label: fmt.Sprintf("%s captcha", dsl.ID), Description: fmt.Sprintf("%s captcha", dsl.ID), Guard: "-", @@ -117,7 +112,7 @@ func exportAPI() error { Method: "GET", Process: process, In: args, - Out: gou.Out{Status: 200, Type: "application/json"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) @@ -130,6 +125,6 @@ func exportAPI() error { } // load apis - _, err = gou.LoadAPIReturn(string(source), "widgets.login") + _, err = api.LoadSource(".yao", source, "widgets.login") return err } diff --git a/widgets/login/login_test.go b/widgets/login/login_test.go index 28596c27..daa0dc95 100644 --- a/widgets/login/login_test.go +++ b/widgets/login/login_test.go @@ -6,14 +6,16 @@ import ( "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/runtime" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - runtime.Load(config.Conf) + test.Prepare(t, config.Conf) + defer test.Clean() + i18n.Load(config.Conf) err := Load(config.Conf) if err != nil { @@ -25,7 +27,7 @@ func TestLoad(t *testing.T) { assert.Equal(t, "admin", Logins["admin"].ID) assert.Equal(t, "::Admin Login", Logins["admin"].Name) assert.Equal(t, "yao.login.Admin", Logins["admin"].Action.Process) - assert.Equal(t, []string{":payload"}, Logins["admin"].Action.Args) + assert.Equal(t, []interface{}{":payload"}, Logins["admin"].Action.Args) assert.Equal(t, "yao.utils.Captcha", Logins["admin"].Layout.Captcha) assert.Equal(t, "/assets/images/login/cover.svg", Logins["admin"].Layout.Cover) assert.Equal(t, "/x/Chart/dashboard", Logins["admin"].Layout.Entry) @@ -35,7 +37,7 @@ func TestLoad(t *testing.T) { assert.Equal(t, "user", Logins["user"].ID) assert.Equal(t, "::User Login", Logins["user"].Name) assert.Equal(t, "scripts.user.Login", Logins["user"].Action.Process) - assert.Equal(t, []string{":payload"}, Logins["user"].Action.Args) + assert.Equal(t, []interface{}{":payload"}, Logins["user"].Action.Args) assert.Equal(t, "yao.utils.Captcha", Logins["user"].Layout.Captcha) assert.Equal(t, "/assets/images/login/cover.svg", Logins["user"].Layout.Cover) assert.Equal(t, "/x/Table/pet", Logins["user"].Layout.Entry) @@ -44,7 +46,10 @@ func TestLoad(t *testing.T) { } func TestLoadHK(t *testing.T) { - runtime.Load(config.Conf) + test.Prepare(t, config.Conf) + defer test.Clean() + + // runtime.Load(config.Conf) i18n.Load(config.Conf) err := Load(config.Conf) if err != nil { @@ -55,7 +60,7 @@ func TestLoadHK(t *testing.T) { assert.Equal(t, "admin", Logins["admin"].ID) assert.Equal(t, "::Admin Login", Logins["admin"].Name) assert.Equal(t, "yao.login.Admin", Logins["admin"].Action.Process) - assert.Equal(t, []string{":payload"}, Logins["admin"].Action.Args) + assert.Equal(t, []interface{}{":payload"}, Logins["admin"].Action.Args) assert.Equal(t, "yao.utils.Captcha", Logins["admin"].Layout.Captcha) assert.Equal(t, "/assets/images/login/cover.svg", Logins["admin"].Layout.Cover) assert.Equal(t, "/x/Chart/dashboard", Logins["admin"].Layout.Entry) @@ -65,7 +70,7 @@ func TestLoadHK(t *testing.T) { assert.Equal(t, "user", Logins["user"].ID) assert.Equal(t, "::User Login", Logins["user"].Name) assert.Equal(t, "scripts.user.Login", Logins["user"].Action.Process) - assert.Equal(t, []string{":payload"}, Logins["user"].Action.Args) + assert.Equal(t, []interface{}{":payload"}, Logins["user"].Action.Args) assert.Equal(t, "yao.utils.Captcha", Logins["user"].Layout.Captcha) assert.Equal(t, "/assets/images/login/cover.svg", Logins["user"].Layout.Cover) assert.Equal(t, "/x/Table/pet", Logins["user"].Layout.Entry) @@ -77,7 +82,7 @@ func TestLoadHK(t *testing.T) { assert.Equal(t, "admin", admin.ID) assert.Equal(t, "管理員登錄", admin.Name) assert.Equal(t, "yao.login.Admin", admin.Action.Process) - assert.Equal(t, []string{":payload"}, admin.Action.Args) + assert.Equal(t, []interface{}{":payload"}, admin.Action.Args) assert.Equal(t, "yao.utils.Captcha", admin.Layout.Captcha) assert.Equal(t, "/assets/images/login/cover.svg", admin.Layout.Cover) assert.Equal(t, "/x/Chart/dashboard", admin.Layout.Entry) @@ -89,7 +94,7 @@ func TestLoadHK(t *testing.T) { assert.Equal(t, "user", user.ID) assert.Equal(t, "用戶登錄", user.Name) assert.Equal(t, "scripts.user.Login", user.Action.Process) - assert.Equal(t, []string{":payload"}, user.Action.Args) + assert.Equal(t, []interface{}{":payload"}, user.Action.Args) assert.Equal(t, "yao.utils.Captcha", user.Layout.Captcha) assert.Equal(t, "/assets/images/login/cover.svg", user.Layout.Cover) assert.Equal(t, "/x/Table/pet", user.Layout.Entry) @@ -99,6 +104,9 @@ func TestLoadHK(t *testing.T) { func TestLoadCN(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + os.Setenv("YAO_LANG", "zh-cn") i18n.Load(config.Conf) err := Load(config.Conf) @@ -114,7 +122,7 @@ func TestLoadCN(t *testing.T) { assert.Equal(t, "admin", admin.ID) assert.Equal(t, "管理员登录", admin.Name) assert.Equal(t, "yao.login.Admin", admin.Action.Process) - assert.Equal(t, []string{":payload"}, admin.Action.Args) + assert.Equal(t, []interface{}{":payload"}, admin.Action.Args) assert.Equal(t, "yao.utils.Captcha", admin.Layout.Captcha) assert.Equal(t, "/assets/images/login/cover.svg", admin.Layout.Cover) assert.Equal(t, "/x/Chart/dashboard", admin.Layout.Entry) @@ -127,7 +135,7 @@ func TestLoadCN(t *testing.T) { assert.Equal(t, "user", user.ID) assert.Equal(t, "用户登录", user.Name) assert.Equal(t, "scripts.user.Login", user.Action.Process) - assert.Equal(t, []string{":payload"}, user.Action.Args) + assert.Equal(t, []interface{}{":payload"}, user.Action.Args) assert.Equal(t, "yao.utils.Captcha", user.Layout.Captcha) assert.Equal(t, "/assets/images/login/cover.svg", user.Layout.Cover) assert.Equal(t, "/x/Table/pet", user.Layout.Entry) @@ -137,6 +145,9 @@ func TestLoadCN(t *testing.T) { func TestExport(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + err := Load(config.Conf) if err != nil { t.Fatal(err) @@ -147,7 +158,7 @@ func TestExport(t *testing.T) { t.Fatal(err) } - api, has := gou.APIs["widgets.login"] + api, has := api.APIs["widgets.login"] assert.True(t, has) assert.Equal(t, 4, len(api.HTTP.Paths)) } diff --git a/widgets/login/process.go b/widgets/login/process.go index 334c158c..dec4bb45 100644 --- a/widgets/login/process.go +++ b/widgets/login/process.go @@ -3,7 +3,8 @@ package login import ( "time" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" @@ -11,7 +12,6 @@ import ( "github.com/yaoapp/kun/maps" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/helper" - "github.com/yaoapp/yao/user" "golang.org/x/crypto/bcrypt" ) @@ -23,11 +23,11 @@ var loginTypes = map[string]string{ // Export process func exportProcess() { - gou.RegisterProcessHandler("yao.login.admin", processLoginAdmin) + process.Register("yao.login.admin", processLoginAdmin) } // processLoginAdmin yao.admin.login 用户登录 -func processLoginAdmin(process *gou.Process) interface{} { +func processLoginAdmin(process *process.Process) interface{} { process.ValidateArgNums(1) payload := process.ArgsMap(0).Dot() log.With(log.F{"payload": payload}).Debug("processLoginAdmin") @@ -40,7 +40,7 @@ func processLoginAdmin(process *gou.Process) interface{} { if value == "" { exception.New("请输入验证码", 400).Ctx(maps.Map{"id": id, "code": value}).Throw() } - if !user.ValidateCaptcha(id, value) { + if !helper.CaptchaValidate(id, value) { log.With(log.F{"id": id, "code": value}).Debug("ProcessLogin") exception.New("验证码不正确", 403).Ctx(maps.Map{"id": id, "code": value}).Throw() return nil @@ -70,11 +70,11 @@ func auth(field string, value string, password string, sid string) maps.Map { exception.New("登录方式(%s)尚未支持", 400, field).Throw() } - user := gou.Select("xiang.user") - rows, err := user.Get(gou.QueryParam{ + user := model.Select("xiang.user") + rows, err := user.Get(model.QueryParam{ Select: []interface{}{"id", "password", "name", "type", "email", "mobile", "extra", "status"}, Limit: 1, - Wheres: []gou.QueryWhere{ + Wheres: []model.QueryWhere{ {Column: column, Value: value}, {Column: "status", Value: "enabled"}, }, @@ -126,7 +126,7 @@ func auth(field string, value string, password string, sid string) maps.Map { } // 读取菜单 - menus := gou.NewProcess("yao.app.menu").WithSID(sid).Run() + menus := process.New("yao.app.menu").WithSID(sid).Run() return maps.Map{ "expires_at": token.ExpiresAt, "token": token.Token, diff --git a/widgets/login/types.go b/widgets/login/types.go index f3fb4d84..df0ae9d2 100644 --- a/widgets/login/types.go +++ b/widgets/login/types.go @@ -11,8 +11,8 @@ type DSL struct { // ActionDSL the login action DSL type ActionDSL struct { - Process string `json:"process,omitempty"` - Args []string `json:"args,omitempty"` + Process string `json:"process,omitempty"` + Args []interface{} `json:"args,omitempty"` } // LayoutDSL the login page layoutDSL diff --git a/widgets/models.go b/widgets/models.go index f547d9d1..63463803 100644 --- a/widgets/models.go +++ b/widgets/models.go @@ -5,14 +5,14 @@ import ( "os" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" ) // Models return loaded models func Models() []Item { models := map[string]interface{}{} - for id, widget := range gou.Models { + for id, widget := range model.Models { if strings.HasPrefix(id, "xiang.") { continue diff --git a/widgets/process.go b/widgets/process.go index f65677e5..03d7685b 100644 --- a/widgets/process.go +++ b/widgets/process.go @@ -1,11 +1,11 @@ package widgets import ( - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" ) // WidgetHandlers Processes -var WidgetHandlers = map[string]gou.ProcessHandler{ +var WidgetHandlers = map[string]process.Handler{ "apis": processApis, "actions": processActions, "models": processModels, @@ -14,31 +14,31 @@ var WidgetHandlers = map[string]gou.ProcessHandler{ } func init() { - gou.RegisterProcessGroup("widget", WidgetHandlers) + process.RegisterGroup("widget", WidgetHandlers) } // Get the loaded APIs -func processApis(process *gou.Process) interface{} { +func processApis(process *process.Process) interface{} { return Apis() } // Get the actions of each widget -func processActions(process *gou.Process) interface{} { +func processActions(process *process.Process) interface{} { return Actions() } // Get the loaded Models -func processModels(process *gou.Process) interface{} { +func processModels(process *process.Process) interface{} { return Models() } // Get the loaded Fields -func processFields(process *gou.Process) interface{} { +func processFields(process *process.Process) interface{} { return Fields() } // Get the loaded Filters -func processFilters(process *gou.Process) interface{} { +func processFilters(process *process.Process) interface{} { return Filters() } diff --git a/widgets/process_test.go b/widgets/process_test.go index 5ce3fd56..7e453ffa 100644 --- a/widgets/process_test.go +++ b/widgets/process_test.go @@ -4,14 +4,18 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) func TestProcessApis(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + testData(t) args := []interface{}{} - res, err := gou.NewProcess("widget.apis", args...).Exec() + res, err := process.New("widget.apis", args...).Exec() if err != nil { t.Fatal(err) } @@ -19,9 +23,12 @@ func TestProcessApis(t *testing.T) { } func TestProcessActions(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + testData(t) args := []interface{}{} - res, err := gou.NewProcess("widget.actions", args...).Exec() + res, err := process.New("widget.actions", args...).Exec() if err != nil { t.Fatal(err) } @@ -29,9 +36,12 @@ func TestProcessActions(t *testing.T) { } func TestProcessModels(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + testData(t) args := []interface{}{} - res, err := gou.NewProcess("widget.models", args...).Exec() + res, err := process.New("widget.models", args...).Exec() if err != nil { t.Fatal(err) } @@ -39,9 +49,12 @@ func TestProcessModels(t *testing.T) { } func TestProcessFields(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + testData(t) args := []interface{}{} - res, err := gou.NewProcess("widget.fields", args...).Exec() + res, err := process.New("widget.fields", args...).Exec() if err != nil { t.Fatal(err) } @@ -49,9 +62,12 @@ func TestProcessFields(t *testing.T) { } func TestProcessFilters(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + testData(t) args := []interface{}{} - res, err := gou.NewProcess("widget.filters", args...).Exec() + res, err := process.New("widget.filters", args...).Exec() if err != nil { t.Fatal(err) } @@ -59,7 +75,7 @@ func TestProcessFilters(t *testing.T) { } func testData(t *testing.T) { - prepare(t) + err := Load(config.Conf) if err != nil { t.Fatal(err) diff --git a/widgets/table/action.go b/widgets/table/action.go index ab5419dc..c36b37e5 100644 --- a/widgets/table/action.go +++ b/widgets/table/action.go @@ -3,7 +3,7 @@ package table import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/action" "github.com/yaoapp/yao/widgets/hook" ) @@ -188,7 +188,7 @@ func (act *ActionDSL) SetDefaultProcess() { } // BindModel bind model -func (act *ActionDSL) BindModel(m *gou.Model) error { +func (act *ActionDSL) BindModel(m *model.Model) error { name := m.ID act.Search.Bind(fmt.Sprintf("models.%s.Paginate", name)) diff --git a/widgets/table/api.go b/widgets/table/api.go index bc4e6439..fe562df5 100644 --- a/widgets/table/api.go +++ b/widgets/table/api.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/api" "github.com/yaoapp/yao/share" "github.com/yaoapp/yao/widgets/action" ) @@ -87,96 +87,96 @@ func (table *DSL) getAction(path string) (*action.Process, error) { // export API func exportAPI() error { - http := gou.HTTP{ + http := api.HTTP{ Name: "Widget Table API", Description: "Widget Table API", Version: share.VERSION, Guard: "widget-table", Group: "__yao/table", - Paths: []gou.Path{}, + Paths: []api.Path{}, } // GET /api/__yao/table/:id/setting -> Default process: yao.table.Xgen - path := gou.Path{ + path := api.Path{ Label: "Setting", Description: "Setting", Path: "/:id/setting", Method: "GET", Process: "yao.table.Setting", - In: []string{"$param.id"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/table/:id/search -> Default process: yao.table.Search $param.id :query $query.page $query.pagesize - path = gou.Path{ + path = api.Path{ Label: "Search", Description: "Search", Path: "/:id/search", Method: "GET", Process: "yao.table.Search", - In: []string{"$param.id", ":query-param", "$query.page", "$query.pagesize"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":query-param", "$query.page", "$query.pagesize"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/table/:id/get -> Default process: yao.table.Get $param.id :query - path = gou.Path{ + path = api.Path{ Label: "Get", Description: "Get", Path: "/:id/get", Method: "GET", Process: "yao.table.Get", - In: []string{"$param.id", ":query-param"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":query-param"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/table/:id/find/:primary -> Default process: yao.table.Find $param.id $param.primary :query - path = gou.Path{ + path = api.Path{ Label: "Find", Description: "Find", Path: "/:id/find/:primary", Method: "GET", Process: "yao.table.Find", - In: []string{"$param.id", "$param.primary", ":query-param"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.primary", ":query-param"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/table/:id/component/:xpath/:method -> Default process: yao.table.Component $param.id $param.xpath $param.method :query - path = gou.Path{ + path = api.Path{ Label: "Component", Description: "Component", Path: "/:id/component/:xpath/:method", Method: "GET", Process: "yao.table.Component", - In: []string{"$param.id", "$param.xpath", "$param.method", ":query"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", ":query"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/upload/:xpath/:method -> Default process: yao.table.Upload $param.id $param.xpath $param.method $file.file - path = gou.Path{ + path = api.Path{ Label: "Upload", Description: "Upload", Path: "/:id/upload/:xpath/:method", Method: "POST", Process: "yao.table.Upload", - In: []string{"$param.id", "$param.xpath", "$param.method", "$file.file"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.xpath", "$param.method", "$file.file"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // GET /api/__yao/table/:id/download/:field -> Default process: yao.table.Download $param.id $param.xpath $param.field $query.name $query.token - path = gou.Path{ + path = api.Path{ Label: "Download", Description: "Download", Path: "/:id/download/:field", Method: "GET", Process: "yao.table.Download", - In: []string{"$param.id", "$param.field", "$query.name", "$query.token"}, - Out: gou.Out{ + In: []interface{}{"$param.id", "$param.field", "$query.name", "$query.token"}, + Out: api.Out{ Status: 200, Body: "{{content}}", Headers: map[string]string{"Content-Type": "{{type}}"}, @@ -185,110 +185,110 @@ func exportAPI() error { http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/save -> Default process: yao.table.Save $param.id :payload - path = gou.Path{ + path = api.Path{ Label: "Save", Description: "Save", Path: "/:id/save", Method: "POST", Process: "yao.table.Save", - In: []string{"$param.id", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/create -> Default process: yao.table.Create $param.id :payload - path = gou.Path{ + path = api.Path{ Label: "Create", Description: "Create", Path: "/:id/create", Method: "POST", Process: "yao.table.Create", - In: []string{"$param.id", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/insert -> Default process: yao.table.Insert :payload - path = gou.Path{ + path = api.Path{ Label: "Insert", Description: "Insert", Path: "/:id/insert", Method: "POST", Process: "yao.table.Insert", - In: []string{"$param.id", "$payload.columns", "$payload.values"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$payload.columns", "$payload.values"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/update/:primary -> Default process: yao.table.Update $param.id $param.primary :payload - path = gou.Path{ + path = api.Path{ Label: "Update", Description: "Update", Path: "/:id/update/:primary", Method: "POST", Process: "yao.table.Update", - In: []string{"$param.id", "$param.primary", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.primary", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/update/where -> Default process: yao.table.UpdateWhere $param.id :query :payload - path = gou.Path{ + path = api.Path{ Label: "Update Where", Description: "Update Where", Path: "/:id/update/where", Method: "POST", Process: "yao.table.UpdateWhere", - In: []string{"$param.id", ":query-param", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":query-param", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/update/in -> Default process: yao.table.UpdateIn $param.id $query.ids :payload - path = gou.Path{ + path = api.Path{ Label: "Update In", Description: "Update In", Path: "/:id/update/in", Method: "POST", Process: "yao.table.UpdateIn", - In: []string{"$param.id", "$query.ids", ":payload"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$query.ids", ":payload"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/delete/:primary -> Default process: yao.table.Delete $param.id $param.primary - path = gou.Path{ + path = api.Path{ Label: "Delete", Description: "Delete", Path: "/:id/delete/:primary", Method: "POST", Process: "yao.table.Delete", - In: []string{"$param.id", "$param.primary"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$param.primary"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/delete/where -> Default process: yao.table.DeleteWhere $param.id :query - path = gou.Path{ + path = api.Path{ Label: "Delete Where", Description: "Delete Where", Path: "/:id/delete/where", Method: "POST", Process: "yao.table.DeleteWhere", - In: []string{"$param.id", ":query-param"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", ":query-param"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) // POST /api/__yao/table/:id/delete/in -> Default process: yao.table.DeleteIn $param.id $query.ids - path = gou.Path{ + path = api.Path{ Label: "Delete In", Description: "Delete In", Path: "/:id/delete/in", Method: "POST", Process: "yao.table.DeleteIn", - In: []string{"$param.id", "$query.ids"}, - Out: gou.Out{Status: 200, Type: "application/json"}, + In: []interface{}{"$param.id", "$query.ids"}, + Out: api.Out{Status: 200, Type: "application/json"}, } http.Paths = append(http.Paths, path) @@ -299,6 +299,6 @@ func exportAPI() error { } // load apis - _, err = gou.LoadAPIReturn(string(source), "widgets.table") + _, err = api.LoadSource(".yao", source, "widgets.table") return err } diff --git a/widgets/table/api_test.go b/widgets/table/api_test.go index 19cbc86d..29e75001 100644 --- a/widgets/table/api_test.go +++ b/widgets/table/api_test.go @@ -4,13 +4,12 @@ import ( "fmt" "net/url" "testing" - "time" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/yaoapp/kun/any" - "github.com/yaoapp/yao/network" - "github.com/yaoapp/yao/widgets/test" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) var guards = map[string]gin.HandlerFunc{ @@ -19,8 +18,9 @@ var guards = map[string]gin.HandlerFunc{ } func TestAPISetting(t *testing.T) { + port := start(t) - defer test.Stop(func() {}) + defer stop() req := test.NewRequest(port).Route("/api/__yao/table/pet/setting") res, err := req.Get() @@ -40,6 +40,7 @@ func TestAPISetting(t *testing.T) { if err != nil { t.Fatal(err) } + data := any.Of(v).MapStr().Dot() assert.Equal(t, "/api/xiang/import/pet", data.Get("header.preset.import.api.import")) // assert.Equal(t, "跳转", data.Get("header.preset.import.operation.0.title")) @@ -49,7 +50,7 @@ func TestAPISetting(t *testing.T) { func TestAPISearch(t *testing.T) { port := start(t) - defer test.Stop(func() {}) + defer test.Stop() req := test.NewRequest(port).Route("/api/__yao/table/session/search") res, err := req.Get() @@ -80,7 +81,7 @@ func TestAPISearch(t *testing.T) { func TestAPISave(t *testing.T) { port := start(t) - defer test.Stop(func() {}) + defer test.Stop() payload := map[string]interface{}{ "name": "New Pet", @@ -117,7 +118,7 @@ func TestAPISave(t *testing.T) { func TestAPICustomGuard(t *testing.T) { port := start(t) - defer test.Stop(func() {}) + defer test.Stop() req := test.NewRequest(port).Route("/api/__yao/table/pet/find/1") res, err := req.Get() @@ -150,7 +151,7 @@ func TestAPICustomGuard(t *testing.T) { func TestAPIGlobalCustomGuard(t *testing.T) { port := start(t) - defer test.Stop(func() {}) + defer test.Stop() req := test.NewRequest(port).Route("/api/__yao/table/guard/find/1") res, err := req.Get() @@ -182,13 +183,17 @@ func TestAPIGlobalCustomGuard(t *testing.T) { } func start(t *testing.T) int { - port := network.FreePort() - load(t) + test.Prepare(t, config.Conf) + prepare(t) clear(t) testData(t) - go test.Start(t, guards, port) - time.Sleep(200 * time.Millisecond) - return port + test.Start(t, guards, config.Conf) + + return test.Port(t) +} + +func stop() { + test.Stop() } func token(t *testing.T) string { diff --git a/widgets/table/bind.go b/widgets/table/bind.go index ed7c9fe2..52f7c4f7 100644 --- a/widgets/table/bind.go +++ b/widgets/table/bind.go @@ -3,7 +3,7 @@ package table import ( "fmt" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" ) // Bind model / store / table / ... @@ -31,7 +31,7 @@ func (dsl *DSL) Bind() error { func (dsl *DSL) bindModel() error { id := dsl.Action.Bind.Model - m, has := gou.Models[id] + m, has := model.Models[id] if !has { return fmt.Errorf("%s does not exist", id) } diff --git a/widgets/table/excel.go b/widgets/table/excel.go index aa83fbc3..b2ff8c0d 100644 --- a/widgets/table/excel.go +++ b/widgets/table/excel.go @@ -7,10 +7,10 @@ import ( "path/filepath" "github.com/xuri/excelize/v2" + "github.com/yaoapp/gou/fs" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/log" "github.com/yaoapp/kun/maps" - "github.com/yaoapp/yao/xfs" ) // Export Export query result to Excel @@ -43,7 +43,13 @@ func (dsl *DSL) Export(filename string, data interface{}, page int, chunkSize in return fmt.Errorf("the table does not support export") } - filename = filepath.Join(xfs.Stor.Root, filename) + // filename = filepath.Join(xfs.Stor.Root, filename) + fs, err := fs.Get("system") + if err != nil { + return err + } + + filename = filepath.Join(fs.Root(), filename) if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) { f := excelize.NewFile() index := f.GetActiveSheetIndex() diff --git a/widgets/table/fields.go b/widgets/table/fields.go index 959f651c..90c4e12f 100644 --- a/widgets/table/fields.go +++ b/widgets/table/fields.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/widgets/component" "github.com/yaoapp/yao/widgets/field" @@ -16,7 +16,7 @@ func (fields *FieldsDSL) TableMap() map[string]field.ColumnDSL { } // BindModel cast model to fields -func (fields *FieldsDSL) BindModel(m *gou.Model) error { +func (fields *FieldsDSL) BindModel(m *model.Model) error { fields.filterMap = map[string]field.FilterDSL{} fields.tableMap = map[string]field.ColumnDSL{} @@ -34,6 +34,11 @@ func (fields *FieldsDSL) BindModel(m *gou.Model) error { } // append columns if _, has := fields.Table[tableField.Key]; !has { + + if fields.Table == nil { + continue + } + fields.Table[tableField.Key] = *tableField // PASSWORD Fields diff --git a/widgets/table/fields_test.go b/widgets/table/fields_test.go index 16674f54..a7fcfd74 100644 --- a/widgets/table/fields_test.go +++ b/widgets/table/fields_test.go @@ -4,20 +4,26 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) func TestFiledsBindModel(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) - m := gou.Select("pet") + + m := model.Select("pet") tab := New("unit-test") err := tab.Fields.BindModel(m) if err != nil { t.Fatal(err) } assert.Equal(t, "id", tab.Fields.Table["ID"].Bind) - assert.Equal(t, 17, len(tab.Fields.Table)) - assert.Equal(t, 6, len(tab.Fields.Filter)) + assert.Equal(t, 18, len(tab.Fields.Table)) + assert.Equal(t, 7, len(tab.Fields.Filter)) } diff --git a/widgets/table/handler.go b/widgets/table/handler.go index 9c0c3ddb..7c567b63 100644 --- a/widgets/table/handler.go +++ b/widgets/table/handler.go @@ -4,7 +4,9 @@ import ( "fmt" "strings" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" + gouProcess "github.com/yaoapp/gou/process" + "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/i18n" @@ -18,7 +20,7 @@ import ( // Execute Compute Filter On: Search, Get, Find // Execute Compute Edit On: Save, Create, Update, UpdateWhere, UpdateIn, Insert // Execute Compute View On: Search, Get, Find -func processHandler(p *action.Process, process *gou.Process) (interface{}, error) { +func processHandler(p *action.Process, process *gouProcess.Process) (interface{}, error) { tab, err := Get(process) if err != nil { @@ -62,7 +64,7 @@ func processHandler(p *action.Process, process *gou.Process) (interface{}, error } // Execute Process - act, err := gou.ProcessOf(name, args...) + act, err := gouProcess.Of(name, args...) if err != nil { log.Error("[table] %s %s -> %s %s %v", tab.ID, p.Name, name, err.Error(), args) return nil, fmt.Errorf("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error()) @@ -102,7 +104,7 @@ func processHandler(p *action.Process, process *gou.Process) (interface{}, error } // translateSetting -func (dsl *DSL) translate(name string, process *gou.Process, data interface{}) (interface{}, error) { +func (dsl *DSL) translate(name string, process *gouProcess.Process, data interface{}) (interface{}, error) { if strings.ToLower(name) != "yao.table.setting" { return data, nil @@ -110,7 +112,7 @@ func (dsl *DSL) translate(name string, process *gou.Process, data interface{}) ( widgets := []string{} if dsl.Action.Bind.Model != "" { - m := gou.Select(dsl.Action.Bind.Model) + m := model.Select(dsl.Action.Bind.Model) widgets = append(widgets, fmt.Sprintf("model.%s", m.ID)) } @@ -119,7 +121,7 @@ func (dsl *DSL) translate(name string, process *gou.Process, data interface{}) ( } widgets = append(widgets, fmt.Sprintf("table.%s", dsl.ID)) - res, err := i18n.Trans(process.Lang(config.Conf.Lang), widgets, data) + res, err := i18n.Trans(session.Lang(process, config.Conf.Lang), widgets, data) if err != nil { return nil, err } diff --git a/widgets/table/layout.go b/widgets/table/layout.go index b50a4a04..e5a07043 100644 --- a/widgets/table/layout.go +++ b/widgets/table/layout.go @@ -5,13 +5,13 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/widgets/component" "github.com/yaoapp/yao/widgets/mapping" ) // BindModel bind model -func (layout *LayoutDSL) BindModel(m *gou.Model, fields *FieldsDSL, option map[string]interface{}) error { +func (layout *LayoutDSL) BindModel(m *model.Model, fields *FieldsDSL, option map[string]interface{}) error { if option == nil { option = map[string]interface{}{} diff --git a/widgets/table/mapping.go b/widgets/table/mapping.go index 07b23b64..dc2b794e 100644 --- a/widgets/table/mapping.go +++ b/widgets/table/mapping.go @@ -38,6 +38,10 @@ func (dsl *DSL) mapping() error { } } + if dsl.CProps == nil { + dsl.CProps = field.CloudProps{} + } + if dsl.Mapping == nil { dsl.Mapping = &mapping.Mapping{} } diff --git a/widgets/table/mapping_test.go b/widgets/table/mapping_test.go index 615a5ab6..29dc7164 100644 --- a/widgets/table/mapping_test.go +++ b/widgets/table/mapping_test.go @@ -5,12 +5,17 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/any" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) func TestMapping(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) @@ -29,12 +34,15 @@ func TestMapping(t *testing.T) { func TestMappingFind(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{"compute", 1} - res, err := gou.NewProcess("yao.table.find", args...).Exec() + res, err := process.New("yao.table.find", args...).Exec() if err != nil { t.Fatal(err) } @@ -44,14 +52,17 @@ func TestMappingFind(t *testing.T) { func TestMappingGet(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) params := map[string]interface{}{"limit": 2} args := []interface{}{"compute", params} - res, err := gou.NewProcess("yao.table.get", args...).Exec() + res, err := process.New("yao.table.get", args...).Exec() if err != nil { t.Fatal(err) } @@ -63,13 +74,16 @@ func TestMappingGet(t *testing.T) { func TestMappingSearch(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) params := map[string]interface{}{} args := []interface{}{"compute", params, 1, 5} - res, err := gou.NewProcess("yao.table.search", args...).Exec() + res, err := process.New("yao.table.search", args...).Exec() if err != nil { t.Fatal(err) } @@ -78,9 +92,13 @@ func TestMappingSearch(t *testing.T) { } func TestMappingSave(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"compute", map[string]interface{}{ "name": " New Pet ", "type": "cat", @@ -91,13 +109,13 @@ func TestMappingSave(t *testing.T) { "doctor_id": 1, }} - res, err := gou.NewProcess("yao.table.Save", args...).Exec() + res, err := process.New("yao.table.Save", args...).Exec() if err != nil { t.Fatal(err) } assert.Equal(t, "4", fmt.Sprintf("%v", res)) - res, err = gou.NewProcess("yao.table.find", "compute", res).Exec() + res, err = process.New("yao.table.find", "compute", res).Exec() if err != nil { t.Fatal(err) } @@ -107,9 +125,13 @@ func TestMappingSave(t *testing.T) { } func TestMappingUpdate(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"compute", 1, map[string]interface{}{ "name": " New Pet ", "type": "cat", @@ -120,12 +142,12 @@ func TestMappingUpdate(t *testing.T) { "doctor_id": 1, }} - _, err := gou.NewProcess("yao.table.Update", args...).Exec() + _, err := process.New("yao.table.Update", args...).Exec() if err != nil { t.Fatal(err) } - res, err := gou.NewProcess("yao.table.find", "compute", 1).Exec() + res, err := process.New("yao.table.find", "compute", 1).Exec() if err != nil { t.Fatal(err) } @@ -135,8 +157,12 @@ func TestMappingUpdate(t *testing.T) { } func TestMappingInsert(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) + args := []interface{}{"compute", []string{"name", "type", "status", "mode", "stay", "cost", "doctor_id"}, [][]interface{}{ @@ -146,12 +172,12 @@ func TestMappingInsert(t *testing.T) { }, } - _, err := gou.NewProcess("yao.table.Insert", args...).Exec() + _, err := process.New("yao.table.Insert", args...).Exec() if err != nil { t.Fatal(err) } - res, err := gou.NewProcess("yao.table.find", "compute", 1).Exec() + res, err := process.New("yao.table.find", "compute", 1).Exec() if err != nil { t.Fatal(err) } diff --git a/widgets/table/process.go b/widgets/table/process.go index bcba7db6..c6b5d31d 100644 --- a/widgets/table/process.go +++ b/widgets/table/process.go @@ -10,8 +10,10 @@ import ( "strings" "time" - "github.com/yaoapp/gou" "github.com/yaoapp/gou/fs" + "github.com/yaoapp/gou/model" + gouProcess "github.com/yaoapp/gou/process" + "github.com/yaoapp/gou/types" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" @@ -23,27 +25,27 @@ import ( // Export process func exportProcess() { - gou.RegisterProcessHandler("yao.table.setting", processSetting) - gou.RegisterProcessHandler("yao.table.xgen", processXgen) - gou.RegisterProcessHandler("yao.table.component", processComponent) - gou.RegisterProcessHandler("yao.table.upload", processUpload) - gou.RegisterProcessHandler("yao.table.download", processDownload) - gou.RegisterProcessHandler("yao.table.search", processSearch) - gou.RegisterProcessHandler("yao.table.get", processGet) - gou.RegisterProcessHandler("yao.table.find", processFind) - gou.RegisterProcessHandler("yao.table.save", processSave) - gou.RegisterProcessHandler("yao.table.create", processCreate) - gou.RegisterProcessHandler("yao.table.insert", processInsert) - gou.RegisterProcessHandler("yao.table.update", processUpdate) - gou.RegisterProcessHandler("yao.table.updatewhere", processUpdateWhere) - gou.RegisterProcessHandler("yao.table.updatein", processUpdateIn) - gou.RegisterProcessHandler("yao.table.delete", processDelete) - gou.RegisterProcessHandler("yao.table.deletewhere", processDeleteWhere) - gou.RegisterProcessHandler("yao.table.deletein", processDeleteIn) - gou.RegisterProcessHandler("yao.table.export", processExport) + gouProcess.Register("yao.table.setting", processSetting) + gouProcess.Register("yao.table.xgen", processXgen) + gouProcess.Register("yao.table.component", processComponent) + gouProcess.Register("yao.table.upload", processUpload) + gouProcess.Register("yao.table.download", processDownload) + gouProcess.Register("yao.table.search", processSearch) + gouProcess.Register("yao.table.get", processGet) + gouProcess.Register("yao.table.find", processFind) + gouProcess.Register("yao.table.save", processSave) + gouProcess.Register("yao.table.create", processCreate) + gouProcess.Register("yao.table.insert", processInsert) + gouProcess.Register("yao.table.update", processUpdate) + gouProcess.Register("yao.table.updatewhere", processUpdateWhere) + gouProcess.Register("yao.table.updatein", processUpdateIn) + gouProcess.Register("yao.table.delete", processDelete) + gouProcess.Register("yao.table.deletewhere", processDeleteWhere) + gouProcess.Register("yao.table.deletein", processDeleteIn) + gouProcess.Register("yao.table.export", processExport) } -func processXgen(process *gou.Process) interface{} { +func processXgen(process *gouProcess.Process) interface{} { tab := MustGet(process) data := process.ArgsMap(1, map[string]interface{}{}) @@ -55,7 +57,7 @@ func processXgen(process *gou.Process) interface{} { return setting } -func processDownload(process *gou.Process) interface{} { +func processDownload(process *gouProcess.Process) interface{} { process.ValidateArgNums(4) tab := MustGet(process) @@ -83,7 +85,7 @@ func processDownload(process *gou.Process) interface{} { } // Create process - p, err := gou.ProcessOf(name, file) + p, err := gouProcess.Of(name, file) if err != nil { log.Error("[downalod] %s.%s %s", tab.ID, field, err.Error()) exception.New("[downalod] %s.%s %s", 400, tab.ID, field, err.Error()).Throw() @@ -99,7 +101,7 @@ func processDownload(process *gou.Process) interface{} { return res } -func processUpload(process *gou.Process) interface{} { +func processUpload(process *gouProcess.Process) interface{} { process.ValidateArgNums(4) tab := MustGet(process) @@ -114,7 +116,7 @@ func processUpload(process *gou.Process) interface{} { } // $file.file - tmpfile, ok := process.Args[3].(gou.UploadFile) + tmpfile, ok := process.Args[3].(types.UploadFile) if !ok { exception.New("parameters error: %v", 400, process.Args[3]).Throw() } @@ -134,7 +136,7 @@ func processUpload(process *gou.Process) interface{} { return res } -func processComponent(process *gou.Process) interface{} { +func processComponent(process *gouProcess.Process) interface{} { process.ValidateArgNums(3) tab := MustGet(process) @@ -163,80 +165,80 @@ func processComponent(process *gou.Process) interface{} { return res } -func processSetting(process *gou.Process) interface{} { +func processSetting(process *gouProcess.Process) interface{} { tab := MustGet(process) process.Args = append(process.Args, process.Args[0]) // table name return tab.Action.Setting.MustExec(process) } -func processSearch(process *gou.Process) interface{} { +func processSearch(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.Search.MustExec(process) } -func processGet(process *gou.Process) interface{} { +func processGet(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.Get.MustExec(process) } -func processSave(process *gou.Process) interface{} { +func processSave(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.Save.MustExec(process) } -func processCreate(process *gou.Process) interface{} { +func processCreate(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.Create.MustExec(process) } -func processFind(process *gou.Process) interface{} { +func processFind(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.Find.MustExec(process) } -func processInsert(process *gou.Process) interface{} { +func processInsert(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.Insert.MustExec(process) } -func processUpdate(process *gou.Process) interface{} { +func processUpdate(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.Update.MustExec(process) } -func processUpdateWhere(process *gou.Process) interface{} { +func processUpdateWhere(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.UpdateWhere.MustExec(process) } -func processUpdateIn(process *gou.Process) interface{} { +func processUpdateIn(process *gouProcess.Process) interface{} { process.ValidateArgNums(3) tab := MustGet(process) ids := strings.Split(process.ArgsString(1), ",") - process.Args[1] = gou.QueryParam{ - Wheres: []gou.QueryWhere{ + process.Args[1] = model.QueryParam{ + Wheres: []model.QueryWhere{ {Column: tab.Layout.Primary, OP: "in", Value: ids}, }, } return tab.Action.UpdateIn.MustExec(process) } -func processDelete(process *gou.Process) interface{} { +func processDelete(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.Delete.MustExec(process) } -func processDeleteWhere(process *gou.Process) interface{} { +func processDeleteWhere(process *gouProcess.Process) interface{} { tab := MustGet(process) return tab.Action.DeleteWhere.MustExec(process) } -func processDeleteIn(process *gou.Process) interface{} { +func processDeleteIn(process *gouProcess.Process) interface{} { process.ValidateArgNums(2) tab := MustGet(process) ids := strings.Split(process.ArgsString(1), ",") - process.Args[1] = gou.QueryParam{ - Wheres: []gou.QueryWhere{ + process.Args[1] = model.QueryParam{ + Wheres: []model.QueryWhere{ {Column: tab.Layout.Primary, OP: "in", Value: ids}, }, } @@ -244,10 +246,10 @@ func processDeleteIn(process *gou.Process) interface{} { } // processExport yao.table.Export (:table, :queryParam, :chunkSize) -func processExport(process *gou.Process) interface{} { +func processExport(process *gouProcess.Process) interface{} { process.ValidateArgNums(1) tab := MustGet(process) // 0 - params := process.ArgsQueryParams(1, gou.QueryParam{}) + params := process.ArgsQueryParams(1, types.QueryParam{}) pagesize := process.ArgsInt(2, 50) // Filename diff --git a/widgets/table/process_test.go b/widgets/table/process_test.go index 4f2a0149..3bdb5c28 100644 --- a/widgets/table/process_test.go +++ b/widgets/table/process_test.go @@ -7,18 +7,24 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/gou" "github.com/yaoapp/gou/fs" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" + "github.com/yaoapp/gou/types" "github.com/yaoapp/kun/any" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/helper" q "github.com/yaoapp/yao/query" + "github.com/yaoapp/yao/test" ) func TestProcessSearch(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) @@ -29,7 +35,7 @@ func TestProcessSearch(t *testing.T) { } args := []interface{}{"pet", params, 1, 5} - res, err := gou.NewProcess("yao.table.search", args...).Exec() + res, err := process.New("yao.table.search", args...).Exec() if err != nil { t.Fatal(err) } @@ -43,7 +49,10 @@ func TestProcessSearch(t *testing.T) { func TestProcessGet(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) @@ -55,7 +64,7 @@ func TestProcessGet(t *testing.T) { } args := []interface{}{"pet", params} - res, err := gou.NewProcess("yao.table.get", args...).Exec() + res, err := process.New("yao.table.get", args...).Exec() if err != nil { t.Fatal(err) } @@ -70,12 +79,15 @@ func TestProcessGet(t *testing.T) { func TestProcessFind(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) args := []interface{}{"pet", 1} - res, err := gou.NewProcess("yao.table.find", args...).Exec() + res, err := process.New("yao.table.find", args...).Exec() if err != nil { t.Fatal(err) } @@ -85,9 +97,13 @@ func TestProcessFind(t *testing.T) { } func TestProcessSave(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"pet", map[string]interface{}{ "name": "New Pet", "type": "cat", @@ -98,13 +114,13 @@ func TestProcessSave(t *testing.T) { "doctor_id": 1, }} - res, err := gou.NewProcess("yao.table.Save", args...).Exec() + res, err := process.New("yao.table.Save", args...).Exec() if err != nil { t.Fatal(err) } assert.Equal(t, "4", fmt.Sprintf("%v", res)) - res, err = gou.NewProcess("yao.table.find", "pet", res).Exec() + res, err = process.New("yao.table.find", "pet", res).Exec() if err != nil { t.Fatal(err) } @@ -114,9 +130,13 @@ func TestProcessSave(t *testing.T) { } func TestProcessCreate(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"pet", map[string]interface{}{ "id": 6, "name": "New Pet", @@ -128,13 +148,13 @@ func TestProcessCreate(t *testing.T) { "doctor_id": 1, }} - res, err := gou.NewProcess("yao.table.Create", args...).Exec() + res, err := process.New("yao.table.Create", args...).Exec() if err != nil { t.Fatal(err) } assert.Equal(t, "6", fmt.Sprintf("%v", res)) - res, err = gou.NewProcess("yao.table.find", "pet", res).Exec() + res, err = process.New("yao.table.find", "pet", res).Exec() if err != nil { t.Fatal(err) } @@ -144,9 +164,13 @@ func TestProcessCreate(t *testing.T) { } func TestProcessUpdate(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"pet", 1, map[string]interface{}{ "name": "New Pet", "type": "cat", @@ -157,12 +181,12 @@ func TestProcessUpdate(t *testing.T) { "doctor_id": 1, }} - _, err := gou.NewProcess("yao.table.Update", args...).Exec() + _, err := process.New("yao.table.Update", args...).Exec() if err != nil { t.Fatal(err) } - res, err := gou.NewProcess("yao.table.find", "pet", 1).Exec() + res, err := process.New("yao.table.find", "pet", 1).Exec() if err != nil { t.Fatal(err) } @@ -172,9 +196,13 @@ func TestProcessUpdate(t *testing.T) { } func TestProcessUpdateWhere(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"pet", map[string]interface{}{"wheres": []map[string]interface{}{{"column": "id", "value": 1}}}, map[string]interface{}{ @@ -187,12 +215,12 @@ func TestProcessUpdateWhere(t *testing.T) { "doctor_id": 1, }} - _, err := gou.NewProcess("yao.table.UpdateWhere", args...).Exec() + _, err := process.New("yao.table.UpdateWhere", args...).Exec() if err != nil { t.Fatal(err) } - res, err := gou.NewProcess("yao.table.find", "pet", 1).Exec() + res, err := process.New("yao.table.find", "pet", 1).Exec() if err != nil { t.Fatal(err) } @@ -202,9 +230,13 @@ func TestProcessUpdateWhere(t *testing.T) { } func TestProcessUpdateIn(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"pet", "1", map[string]interface{}{ "name": "New Pet", @@ -216,12 +248,12 @@ func TestProcessUpdateIn(t *testing.T) { "doctor_id": 1, }} - _, err := gou.NewProcess("yao.table.UpdateIn", args...).Exec() + _, err := process.New("yao.table.UpdateIn", args...).Exec() if err != nil { t.Fatal(err) } - res, err := gou.NewProcess("yao.table.find", "pet", 1).Exec() + res, err := process.New("yao.table.find", "pet", 1).Exec() if err != nil { t.Fatal(err) } @@ -231,8 +263,12 @@ func TestProcessUpdateIn(t *testing.T) { } func TestProcessInsert(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) + args := []interface{}{"pet", []string{"name", "type", "status", "mode", "stay", "cost", "doctor_id"}, [][]interface{}{ @@ -242,12 +278,12 @@ func TestProcessInsert(t *testing.T) { }, } - _, err := gou.NewProcess("yao.table.Insert", args...).Exec() + _, err := process.New("yao.table.Insert", args...).Exec() if err != nil { t.Fatal(err) } - res, err := gou.NewProcess("yao.table.find", "pet", 1).Exec() + res, err := process.New("yao.table.find", "pet", 1).Exec() if err != nil { t.Fatal(err) } @@ -257,57 +293,73 @@ func TestProcessInsert(t *testing.T) { } func TestProcessDelete(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"pet", 1} - _, err := gou.NewProcess("yao.table.Delete", args...).Exec() + _, err := process.New("yao.table.Delete", args...).Exec() if err != nil { t.Fatal(err) } - _, err = gou.NewProcess("yao.table.find", "pet", 1).Exec() + _, err = process.New("yao.table.find", "pet", 1).Exec() assert.Contains(t, err.Error(), "ID=1") } func TestProcessDeleteWhere(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{ "pet", map[string]interface{}{"wheres": []map[string]interface{}{{"column": "id", "value": 1}}}, } - _, err := gou.NewProcess("yao.table.DeleteWhere", args...).Exec() + _, err := process.New("yao.table.DeleteWhere", args...).Exec() if err != nil { t.Fatal(err) } - _, err = gou.NewProcess("yao.table.find", "pet", 1).Exec() + _, err = process.New("yao.table.find", "pet", 1).Exec() assert.Contains(t, err.Error(), "ID=1") } func TestProcessDeleteIn(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"pet", "1"} - _, err := gou.NewProcess("yao.table.DeleteIn", args...).Exec() + _, err := process.New("yao.table.DeleteIn", args...).Exec() if err != nil { t.Fatal(err) } - _, err = gou.NewProcess("yao.table.find", "pet", 1).Exec() + _, err = process.New("yao.table.find", "pet", 1).Exec() assert.Contains(t, err.Error(), "ID=1") } func TestProcessComponent(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{ "pet", "fields.filter.状态.edit.props.xProps", @@ -321,7 +373,7 @@ func TestProcessComponent(t *testing.T) { }, } - res, err := gou.NewProcess("yao.table.Component", args...).Exec() + res, err := process.New("yao.table.Component", args...).Exec() if err != nil { t.Fatal(err) } @@ -336,31 +388,39 @@ func TestProcessComponent(t *testing.T) { } func TestProcessComponentError(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{ "pet", "fields.filter.edit.props.状态.::not-exist", "remote", map[string]interface{}{"select": []string{"name", "status"}, "limit": 2}, } - _, err := gou.NewProcess("yao.table.Component", args...).Exec() + _, err := process.New("yao.table.Component", args...).Exec() assert.Contains(t, err.Error(), "fields.filter.edit.props.状态.::not-exist") } func TestProcessUpload(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{ "pet", "fields.table.相关图片.edit.props", "api", - gou.UploadFile{TempFile: tempFile(t)}, + types.UploadFile{TempFile: tempFile(t)}, } - res, err := gou.NewProcess("yao.table.Upload", args...).Exec() + res, err := process.New("yao.table.Upload", args...).Exec() if err != nil { t.Fatal(err) } @@ -371,7 +431,10 @@ func TestProcessUpload(t *testing.T) { } func TestProcessDownload(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) @@ -383,7 +446,7 @@ func TestProcessDownload(t *testing.T) { } args := []interface{}{"pet", "images", "/text.txt", jwt.Token} - res, err := gou.NewProcess("yao.table.Download", args...).Exec() + res, err := process.New("yao.table.Download", args...).Exec() if err != nil { t.Fatal(err) } @@ -395,11 +458,15 @@ func TestProcessDownload(t *testing.T) { } func TestProcessSetting(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"pet"} - res, err := gou.NewProcess("yao.table.Setting", args...).Exec() + res, err := process.New("yao.table.Setting", args...).Exec() if err != nil { t.Fatal(err) } @@ -414,11 +481,15 @@ func TestProcessSetting(t *testing.T) { } func TestProcessXgen(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) + args := []interface{}{"pet"} - res, err := gou.NewProcess("yao.table.Xgen", args...).Exec() + res, err := process.New("yao.table.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -434,7 +505,10 @@ func TestProcessXgen(t *testing.T) { } func TestProcessXgenWithPermissions(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) @@ -448,7 +522,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { }) args := []interface{}{"pet"} - res, err := gou.NewProcess("yao.table.Xgen", args...).Exec() + res, err := process.New("yao.table.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -464,7 +538,7 @@ func TestProcessXgenWithPermissions(t *testing.T) { assert.Len(t, data.Get("table.operation.actions"), 4) session.Global().Set("__permissions", nil) - res, err = gou.NewProcess("yao.table.Xgen", args...).Exec() + res, err = process.New("yao.table.Xgen", args...).Exec() if err != nil { t.Fatal(err) } @@ -482,11 +556,15 @@ func TestProcessXgenWithPermissions(t *testing.T) { } func TestProcessExport(t *testing.T) { - load(t) + test.Prepare(t, config.Conf) + defer test.Clean() + + prepare(t) clear(t) testData(t) - args := []interface{}{"pet", gou.QueryParam{Wheres: []gou.QueryWhere{{Column: "mode", Value: "enabled"}}}, 2} - response := gou.NewProcess("yao.table.Export", args...).Run() + + args := []interface{}{"pet", model.QueryParam{Wheres: []model.QueryWhere{{Column: "mode", Value: "enabled"}}}, 2} + response := process.New("yao.table.Export", args...).Run() assert.NotNil(t, response) fs := fs.MustGet("system") size, _ := fs.Size(response.(string)) @@ -503,7 +581,7 @@ func load(t *testing.T) { } func testData(t *testing.T) { - pet := gou.Select("pet") + pet := model.Select("pet") err := pet.Insert( []string{"name", "type", "status", "mode", "stay", "cost", "doctor_id"}, [][]interface{}{ @@ -533,7 +611,7 @@ func tempFile(t *testing.T) string { } func clear(t *testing.T) { - for _, m := range gou.Models { + for _, m := range model.Models { err := m.DropTable() if err != nil { t.Fatal(err) diff --git a/widgets/table/table.go b/widgets/table/table.go index 750fcd0c..d751a13f 100644 --- a/widgets/table/table.go +++ b/widgets/table/table.go @@ -6,13 +6,13 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" + "github.com/yaoapp/gou/application" + "github.com/yaoapp/gou/process" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/share" "github.com/yaoapp/yao/widgets/component" - "github.com/yaoapp/yao/widgets/environment" "github.com/yaoapp/yao/widgets/field" ) @@ -101,70 +101,81 @@ func LoadAndExport(cfg config.Config) error { return Load(cfg) } -// Load load table +// Load load table dsl func Load(cfg config.Config) error { - var root = filepath.Join(cfg.Root, "tables") - return LoadFrom(root, "") -} - -// LoadID load by id -func LoadID(id string, root string) error { - dirs := strings.Split(id, ".") - name := fmt.Sprintf("%s.tab.json", dirs[len(dirs)-1]) - elems := []string{root} - elems = append(elems, dirs[0:len(dirs)-1]...) - elems = append(elems, "tables", name) - filename := filepath.Join(elems...) - data, err := environment.ReadFile(filename) - if err != nil { - return fmt.Errorf("[table] LoadID %s root=%s %s", id, root, err.Error()) - } - return LoadData(data, id, root) -} - -// LoadFrom load from dir -func LoadFrom(dir string, prefix string) error { - - if share.DirNotExists(dir) { - return fmt.Errorf("%s does not exists", dir) - } - messages := []string{} - err := share.Walk(dir, ".json", func(root, filename string) { - id := prefix + share.ID(root, filename) - data, err := environment.ReadFile(filename) - if err != nil { - messages = append(messages, fmt.Sprintf("[%s] %s", id, err.Error())) - return + exts := []string{"*.tab.yao", "*.tab.json", "*.tab.jsonc"} + err := application.App.Walk("tables", func(root, file string, isdir bool) error { + if isdir { + return nil + } + if err := LoadFile(root, file); err != nil { + messages = append(messages, err.Error()) } - err = LoadData(data, id, filepath.Dir(dir)) - if err != nil { - messages = append(messages, fmt.Sprintf("[%s] %s", id, err.Error())) - return - } - }) + return nil + }, exts...) if len(messages) > 0 { - return fmt.Errorf(strings.Join(messages, ";")) + return fmt.Errorf(strings.Join(messages, ";\n")) } return err } -// LoadData load table from source -func LoadData(data []byte, id string, root string) error { - dsl := New(id) - dsl.Root = root +// LoadID load table dsl by id +func LoadID(id string, root string) error { - err := jsoniter.Unmarshal(data, dsl) - if err != nil { - return fmt.Errorf("[Table] LoadData %s %s", id, err.Error()) + file := filepath.Join("tables", share.File(id, ".tab.yao")) + if exists, _ := application.App.Exists(file); exists { + return LoadFile("tables", file) } + file = filepath.Join("tables", share.File(id, ".tab.jsonc")) + if exists, _ := application.App.Exists(file); exists { + return LoadFile("tables", file) + } + + file = filepath.Join("tables", share.File(id, ".tab.json")) + if exists, _ := application.App.Exists(file); exists { + return LoadFile("tables", file) + } + + return fmt.Errorf("table %s not found", id) +} + +// LoadFile load table dsl by file +func LoadFile(root string, file string) error { + + id := share.ID(root, file) + data, err := application.App.Read(file) + if err != nil { + return err + } + + dsl := &DSL{ID: id} + err = application.Parse(file, data, dsl) + if err != nil { + return fmt.Errorf("[%s] %s", id, err.Error()) + } + + err = dsl.parse(id, root) + if err != nil { + return err + } + + Tables[id] = dsl + return nil +} + +// parse parse table dsl source +func (dsl *DSL) parse(id string, root string) error { + + dsl.Root = root // remove next version if dsl.Action == nil { dsl.Action = &ActionDSL{} } + dsl.Action.SetDefaultProcess() if dsl.Layout == nil { @@ -181,7 +192,7 @@ func LoadData(data []byte, id string, root string) error { } // Bind model / store / table / ... - err = dsl.Bind() + err := dsl.Bind() if err != nil { return fmt.Errorf("[Table] LoadData Bind %s %s", id, err.Error()) } @@ -208,8 +219,8 @@ func Get(table interface{}) (*DSL, error) { switch table.(type) { case string: id = table.(string) - case *gou.Process: - id = table.(*gou.Process).ArgsString(0) + case *process.Process: + id = table.(*process.Process).ArgsString(0) default: return nil, fmt.Errorf("%v type does not support", table) } @@ -233,6 +244,10 @@ func MustGet(table interface{}) *DSL { // Xgen trans to xgen setting func (dsl *DSL) Xgen(data map[string]interface{}, excludes map[string]bool) (map[string]interface{}, error) { + if dsl.Config == nil { + dsl.Config = map[string]interface{}{} + } + layout, err := dsl.Layout.Xgen(data, excludes, dsl.Mapping) if err != nil { return nil, err diff --git a/widgets/table/table_test.go b/widgets/table/table_test.go index b50a5f6f..89acbb8a 100644 --- a/widgets/table/table_test.go +++ b/widgets/table/table_test.go @@ -7,20 +7,20 @@ import ( "github.com/stretchr/testify/assert" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/flow" - "github.com/yaoapp/yao/fs" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/script" + "github.com/yaoapp/yao/test" _ "github.com/yaoapp/yao/utils" "github.com/yaoapp/yao/widgets/app" "github.com/yaoapp/yao/widgets/component" "github.com/yaoapp/yao/widgets/expression" "github.com/yaoapp/yao/widgets/field" - "github.com/yaoapp/yao/widgets/test" ) func TestLoad(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() prepare(t) + err := Load(config.Conf) if err != nil { t.Fatal(err) @@ -29,7 +29,11 @@ func TestLoad(t *testing.T) { } func TestLoadID(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() prepare(t) + err := LoadID("pet", filepath.Join(config.Conf.Root)) if err != nil { t.Fatal(err) @@ -37,37 +41,41 @@ func TestLoadID(t *testing.T) { } func prepare(t *testing.T, language ...string) { - runtime.Load(config.Conf) - err := test.LoadEngine(language...) - if err != nil { - t.Fatal(err) - } - // load fs - err = fs.Load(config.Conf) - if err != nil { - t.Fatal(err) - } + // test.Prepare(t, config.Conf) + // defer test.Clean() - // load scripts - err = script.Load(config.Conf) - if err != nil { - t.Fatal(err) - } + // // runtime.Load(config.Conf) + // err := test.LoadEngine(language...) + // if err != nil { + // t.Fatal(err) + // } - // load models - err = model.Load(config.Conf) - if err != nil { - t.Fatal(err) - } + // // load fs + // err = fs.Load(config.Conf) + // if err != nil { + // t.Fatal(err) + // } + + // // load scripts + // err = script.Load(config.Conf) + // if err != nil { + // t.Fatal(err) + // } + + // // load models + // err = model.Load(config.Conf) + // if err != nil { + // t.Fatal(err) + // } // load flows - err = flow.Load(config.Conf) + err := flow.Load(config.Conf) if err != nil { t.Fatal(err) } - // load app widget + // load app err = app.LoadAndExport(config.Conf) if err != nil { t.Fatal(err) @@ -91,10 +99,15 @@ func prepare(t *testing.T, language ...string) { t.Fatal(err) } + // Load table + err = Load(config.Conf) + if err != nil { + t.Fatal(err) + } + // export err = Export() if err != nil { t.Fatal(err) } - } diff --git a/widgets/test/engine.go b/widgets/test/engine.go deleted file mode 100644 index 9b9ca8cc..00000000 --- a/widgets/test/engine.go +++ /dev/null @@ -1,40 +0,0 @@ -package test - -import ( - "os" - "path/filepath" - - "github.com/yaoapp/gou" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/share" -) - -// LoadEngine load engine -func LoadEngine(language ...string) error { - - runtime.Load(config.Conf) - i18n.Load(config.Conf) - share.DBConnect(config.Conf.DB) // removed later - gou.LoadCrypt(`{}`, "PASSWORD") - gou.LoadCrypt(`{}`, "AES") - - // Session server - err := share.SessionStart() - if err != nil { - return err - } - - // load engine models - dev := os.Getenv("YAO_DEV") - if dev != "" { - err := model.LoadFrom(filepath.Join(dev, "yao", "models"), "xiang.") - if err != nil { - return err - } - } - - return nil -} diff --git a/widgets/test/server.go b/widgets/test/server.go deleted file mode 100644 index e517ec8f..00000000 --- a/widgets/test/server.go +++ /dev/null @@ -1,65 +0,0 @@ -package test - -import ( - "strings" - "testing" - - "github.com/gin-gonic/gin" - "github.com/yaoapp/gou" - "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/helper" - "github.com/yaoapp/yao/share" -) - -var shutdown = make(chan bool, 1) -var shutdownComplete = make(chan bool, 1) - -// Start the api server -func Start(t *testing.T, guards map[string]gin.HandlerFunc, port int) error { - - err := share.SessionStart() - if err != nil { - return err - } - - gin.SetMode(gin.ReleaseMode) - gou.SetHTTPGuards(guards) - gou.ServeHTTP( - gou.Server{ - Host: "127.0.0.1", - Port: port, - Root: "/api", - Allows: config.Conf.AllowFrom, - }, - shutdown, func(s gou.Server) { - shutdownComplete <- true - }, - ) - - return nil -} - -// Stop the api server -func Stop(onComplete func()) { - shutdown <- true - select { - case <-shutdownComplete: - share.SessionStop() - onComplete() - } -} - -// GuardBearerJWT test guard -func GuardBearerJWT(c *gin.Context) { - tokenString := c.Request.Header.Get("Authorization") - tokenString = strings.TrimSpace(strings.TrimPrefix(tokenString, "Bearer ")) - - if tokenString == "" { - c.JSON(403, gin.H{"code": 403, "message": "No permission"}) - c.Abort() - return - } - - claims := helper.JwtValidate(tokenString) - c.Set("__sid", claims.SID) -} diff --git a/widgets/widgets_test.go b/widgets/widgets_test.go index 71e17d6c..aa5e1c2e 100644 --- a/widgets/widgets_test.go +++ b/widgets/widgets_test.go @@ -4,49 +4,13 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/yao/api" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/i18n" - "github.com/yaoapp/yao/model" - "github.com/yaoapp/yao/runtime" - "github.com/yaoapp/yao/script" - "github.com/yaoapp/yao/share" - "github.com/yaoapp/yao/widgets/test" + "github.com/yaoapp/yao/test" ) func TestLoad(t *testing.T) { - prepare(t) + test.Prepare(t, config.Conf) + defer test.Clean() err := Load(config.Conf) assert.Nil(t, err) } - -func prepare(t *testing.T, language ...string) { - - err := test.LoadEngine(language...) - if err != nil { - t.Fatal(err) - } - - runtime.Load(config.Conf) - i18n.Load(config.Conf) - share.DBConnect(config.Conf.DB) // removed later - - // load scripts - err = script.Load(config.Conf) - if err != nil { - t.Fatal(err) - } - - // load models - err = model.Load(config.Conf) - if err != nil { - t.Fatal(err) - } - - // load apis - err = api.Load(config.Conf) - if err != nil { - t.Fatal(err) - } - -} diff --git a/xfs/process.go b/xfs/process.go deleted file mode 100644 index a2baf54f..00000000 --- a/xfs/process.go +++ /dev/null @@ -1,166 +0,0 @@ -package xfs - -import ( - "crypto/md5" - "encoding/hex" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - "github.com/aliyun/alibaba-cloud-sdk-go/services/sts" - "github.com/yaoapp/gou" - "github.com/yaoapp/gou/session" - "github.com/yaoapp/kun/any" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/kun/maps" - "github.com/yaoapp/xun" - "github.com/yaoapp/yao/data" - "github.com/yaoapp/yao/helper" - "github.com/yaoapp/yao/share" -) - -// DEPRECATED -func init() { - // 注册处理器 - gou.RegisterProcessHandler("xiang.fs.Upload", processUpload) - gou.RegisterProcessHandler("xiang.fs.GetToken", processGetToken) - gou.RegisterProcessHandler("xiang.fs.GetURL", processGetURL) - gou.RegisterProcessHandler("xiang.fs.ReadFile", processReadFile) -} - -// processUpload 上传文件到本地服务器 -func processUpload(process *gou.Process) interface{} { - process.ValidateArgNums(1) - tmpfile, ok := process.Args[0].(xun.UploadFile) - if !ok { - exception.New("上传文件参数错误", 400, process.Args[0]).Throw() - } - - hash := md5.Sum([]byte(time.Now().Format("20060102-15:04:05"))) - fingerprint := string(hex.EncodeToString(hash[:])) - fingerprint = strings.ToUpper(fingerprint) - - dir := time.Now().Format("20060102") - ext := filepath.Ext(tmpfile.Name) - filename := filepath.Join(dir, fmt.Sprintf("%s%s", fingerprint, ext)) - Stor.MustMkdirAll(dir, os.ModePerm) - - content, err := New("/").ReadFile(tmpfile.TempFile) - if err != nil { - exception.New("不能读取上传文件 %s", 500, err.Error()).Throw() - } - - Stor.MustWriteFile(filename, content, os.ModePerm) - return filename -} - -// processGetContent 返回文件正文 -func processReadFile(process *gou.Process) interface{} { - process.ValidateArgNums(1) - filename := process.ArgsString(0) - encode := process.ArgsBool(1, true) - - stats, err := Stor.Stat(filename) - if err != nil { - exception.New("读取文件信息失败 %s", 500, err.Error()).Throw() - } - - var content string - body := Stor.MustReadFile(filename) - if encode { - content = Encode(body) - } else { - content = string(body) - } - - return maps.Map{ - "size": stats.Size(), - "content": content, - } -} - -// processGetToken 上传文件到腾讯云对象存储 COS -func processGetToken(process *gou.Process) interface{} { - process.ValidateArgNums(1) - name := process.ArgsString(0, "oss") - // bucket := process.ArgsString(1) - if name != "oss" { - exception.New("暂时支持 oss 存储", 400).Throw() - } - - if share.App.Storage.OSS == nil { - exception.New("未配置 OSS 存储", 400).Throw() - } - - app := share.App.Storage.OSS - id := strings.Split(app.Endpoint, ".")[0] - client, err := sts.NewClientWithAccessKey(id, app.ID, app.Secret) - if err != nil { - exception.New("配置错误 %s", 400, err.Error()).Throw() - } - - //构建请求对象。 - request := sts.CreateAssumeRoleRequest() - request.Scheme = "https" - request.Domain = "sts.aliyuncs.com" - - //设置参数。关于参数含义和设置方法,请参见API参考。 - request.RoleArn = app.RoleArn - request.RoleSessionName = app.SessionName - - //发起请求,并得到响应。 - response, err := client.AssumeRole(request) - if err != nil { - exception.New("配置错误 %s", 400, err.Error()).Throw() - } - - res := any.Of(response.Credentials).Map() - res.Set("Endpoint", app.Endpoint) - return res.MapStrAny -} - -// 临时 -const svg404 = ` - - - - Created with Fabric.js 4.2.0 - - - - - - - - - 404 - - -` - -// processGetURL 返回文件CDN地址 -func processGetURL(process *gou.Process) interface{} { - if len(process.Args) < 1 { - return string(data.MustAsset("yao/data/icons/404.png")) - } - - token := process.ArgsString(2) - if token == "" { - exception.New("token is null", 403).Throw() - } - - claims := helper.JwtValidate(token) - userID, err := session.Global().ID(claims.SID).Get("user_id") - if err != nil || userID == nil { - exception.New("session expired", 403).Throw() - } - - filename := process.ArgsString(0) - body, err := Stor.ReadFile(filename) - if err != nil { - return string(data.MustAsset("yao/data/icons/404.png")) - } - return string(body) -} diff --git a/xfs/process_test.go b/xfs/process_test.go deleted file mode 100644 index 415c6cd1..00000000 --- a/xfs/process_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package xfs - -import ( - "os" - "testing" - - "github.com/yaoapp/yao/share" -) - -// DEPRECATED - -func init() { - share.App = share.AppInfo{ - Storage: share.AppStorage{ - Default: "oss", - OSS: &share.AppStorageOSS{ - Endpoint: "oss-cn-hangzhou.aliyuncs.com", - ID: os.Getenv("OSS_TEST_ID"), - Secret: os.Getenv("OSS_TEST_SECRET"), - RoleArn: "acs:ram::31524094:role/ramosstest", - SessionName: "SessionTest", - }, - }, - } -} -func TestProcessGetToken(t *testing.T) { - - // the oss support will be using new process - - // args := []interface{}{"oss"} - // process := gou.NewProcess("xiang.fs.GetToken", args...) - // response := processGetToken(process) - // assert.NotNil(t, response) - // res := any.Of(response).Map() - // assert.True(t, res.Has("AccessKeyId")) - // assert.True(t, res.Has("AccessKeySecret")) - // assert.True(t, res.Has("Expiration")) - // assert.True(t, res.Has("SecurityToken")) - // assert.True(t, res.Has("Endpoint")) - - // // 使用token - // client, err := oss.New( - // res.Get("Endpoint").(string), - // res.Get("AccessKeyId").(string), - // res.Get("AccessKeySecret").(string), - // oss.SecurityToken(res.Get("SecurityToken").(string)), - // ) - // assert.Nil(t, err) - - // bucket, err := client.Bucket("image-appcook") - // assert.Nil(t, err) - - // // 上传字符串。 - // now := fmt.Sprintf("%d", time.Now().UnixNano()) - // err = bucket.PutObject("xiang/unit-test."+now+".txt", strings.NewReader(now)) - // assert.Nil(t, err) -} diff --git a/xfs/xfs.go b/xfs/xfs.go deleted file mode 100644 index 6dbea5b3..00000000 --- a/xfs/xfs.go +++ /dev/null @@ -1,263 +0,0 @@ -package xfs - -import ( - "archive/tar" - "archive/zip" - "bytes" - "encoding/base64" - "fmt" - "io" - "io/fs" - "os" - "path/filepath" - "strings" - - "github.com/spf13/afero" - "github.com/spf13/afero/tarfs" - "github.com/spf13/afero/zipfs" - "github.com/yaoapp/kun/exception" - "github.com/yaoapp/yao/config" -) - -// DEPRECATED - -// Stor 文件系统实例 -var Stor Xfs - -// Xfs 文件系统 -type Xfs struct { - Root string // Root - afero.Afero -} - -// Fs 文件系统 Interface -type Fs interface { - afero.Fs -} - -// File file fs -type File interface { - afero.File -} - -func init() { - Stor = New(filepath.Join(config.Conf.Root, "data")) -} - -// New 创建文件系统 -func New(path string) Xfs { - pinfo := strings.Split(path, "://") - fs := "fs" - root := path - if len(pinfo) == 2 { - fs = pinfo[0] - root = strings.TrimPrefix(path, fmt.Sprintf("%s://", fs)) - } - - xfs := Xfs{ - Root: root, - } - switch fs { - case "fs", "file": - xfs.Fs = afero.NewBasePathFs(afero.NewOsFs(), root) - break - case "mem", "memory": - xfs.Fs = afero.NewMemMapFs() - break - default: - exception.New("尚未支持 %s 文件系统", 500, fs).Throw() - } - - // exists := xfs.MustDirExists("/") - // if !exists && root != "/" { - // xfs.MustMkdirAll("/", os.ModePerm) - // } - - return xfs -} - -// NewZip 创建Zip文件系统 -func NewZip(zipfile string) Xfs { - r, err := zip.OpenReader(zipfile) - if err != nil { - exception.New("ZIP文件打开失败 %s ", 500, err).Throw() - } - return Xfs{ - Root: "/", - Afero: afero.Afero{ - Fs: zipfs.New(&r.Reader), - }, - } -} - -// NewTar tar文件系统 -func NewTar(tarfile string) Xfs { - var buf bytes.Buffer - r := tar.NewReader(&buf) - return Xfs{ - Root: "/", - Afero: afero.Afero{ - Fs: tarfs.New(r), - }, - } -} - -// Encode Base64编码 -func Encode(content []byte) string { - return base64.StdEncoding.EncodeToString(content) -} - -// Decode Base64编码 -func Decode(content []byte) []byte { - var data []byte - _, err := base64.StdEncoding.Decode(data, content) - if err != nil { - exception.New("文件解码失败 %s ", 500, err).Throw() - } - return data -} - -// DecodeString Base64编码 -func DecodeString(content string) string { - dst, err := base64.StdEncoding.DecodeString(content) - if err != nil { - exception.New("文件解码失败 %s ", 500, err).Throw() - } - return string(dst) -} - -// MustOpen 打开文件 -func (xfs *Xfs) MustOpen(filename string) File { - file, err := xfs.Open(filename) - if err != nil { - exception.New("打开文件失败 %s 失败 ", 500, filename).Throw() - } - return file -} - -// MustReadFile 阅读文件内容 -func (xfs *Xfs) MustReadFile(filename string) []byte { - bytes, err := xfs.ReadFile(filename) - if err != nil { - exception.New("阅读文件内容失败 %s 失败 %s ", 500, filename, err).Throw() - } - return bytes -} - -// MustReadDir 阅读文件夹内容 -func (xfs *Xfs) MustReadDir(dirname string) []os.FileInfo { - fileinfos, err := xfs.ReadDir(dirname) - if err != nil { - exception.New("阅读文件夹失败 %s 失败 ", 500, dirname).Throw() - } - return fileinfos -} - -// MustExists 检查文件是否存在 -func (xfs *Xfs) MustExists(filename string) bool { - has, err := xfs.Exists(filename) - if err != nil { - exception.New("检查文件失败 %s 失败 ", 500, filename).Throw() - } - return has -} - -// MustDirExists 检查文件夹是否存在 -func (xfs *Xfs) MustDirExists(dirname string) bool { - res, err := xfs.DirExists(dirname) - if err != nil { - exception.New("检查文件夹 %s 失败 ", 500, dirname).Throw() - } - return res -} - -// MustMkdirAll 创建文件夹及子文件夹 -func (xfs *Xfs) MustMkdirAll(dirname string, pterm fs.FileMode) { - err := xfs.MkdirAll(dirname, pterm) - if err != nil { - exception.New("创建文件夹及子文件 %s 失败 ", 500, dirname).Throw() - } -} - -// MustMkdir 创建文件夹 -func (xfs *Xfs) MustMkdir(dirname string, pterm fs.FileMode) { - err := xfs.Mkdir(dirname, pterm) - if err != nil { - exception.New("创建文件夹 %s 失败 ", 500, dirname).Throw() - } -} - -// MustIsDir 是否为文件夹 -func (xfs *Xfs) MustIsDir(dirname string) bool { - isdir, err := xfs.IsDir(dirname) - if err != nil { - exception.New("创建文件夹 %s 失败 ", 500, dirname).Throw() - } - return isdir -} - -// MustIsEmpty 文件是否为空 -func (xfs *Xfs) MustIsEmpty(filename string) bool { - isEmpty, err := xfs.IsEmpty(filename) - if err != nil { - exception.New("检查文件是否为空 %s 失败 ", 500, filename).Throw() - } - return isEmpty - -} - -// GetTempDir 临时目录 -func (xfs *Xfs) GetTempDir(subPath string) string { - return afero.GetTempDir(xfs.Fs, subPath) -} - -// MustTempDir 创建临时文件夹 -func (xfs *Xfs) MustTempDir(dirname string, prefix string) string { - name, err := xfs.TempDir(dirname, prefix) - if err != nil { - exception.New("创建文件夹 %s %s 失败 ", 500, dirname, prefix).Throw() - } - return name -} - -// MustTempFile 临时文件 -func (xfs *Xfs) MustTempFile(dirname, prefix string) File { - f, err := xfs.TempFile(dirname, prefix) - if err != nil { - exception.New("创建临时文件 %s %s 失败 ", 500, dirname, prefix).Throw() - } - return f -} - -// MustWalk 文件夹遍历 -func (xfs *Xfs) MustWalk(root string, walkFn filepath.WalkFunc) { - err := xfs.Walk(root, walkFn) - if err != nil { - exception.New("遍历文件夹 %s 失败 ", 500, root).Throw() - } -} - -// MustWriteFile 写入文件 -func (xfs *Xfs) MustWriteFile(filename string, data []byte, perm os.FileMode) { - err := xfs.WriteFile(filename, data, perm) - if err != nil { - exception.New("写入文件 %s 失败 ", 500, filename).Throw() - } -} - -// MustFileContainsBytes 文件是否包含内容 -func (xfs *Xfs) MustFileContainsBytes(filename string, subslice []byte) bool { - has, err := xfs.FileContainsBytes(filename, subslice) - if err != nil { - exception.New("检查文件是否包含内容 %s 失败 ", 500, filename).Throw() - } - return has -} - -// MustSafeWriteReader 检查文件能否写入 -func (xfs *Xfs) MustSafeWriteReader(filename string, r io.Reader) { - err := xfs.SafeWriteReader(filename, r) - if err != nil { - exception.New("检查文件是否可以写入 Reader %s 失败 ", 500, filename).Throw() - } -} diff --git a/xfs/xfs_test.go b/xfs/xfs_test.go deleted file mode 100644 index c21a279a..00000000 --- a/xfs/xfs_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package xfs - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -// DEPRECATED - -func TestOSFsMustMkdirAll(t *testing.T) { - // fs := New(config.Conf.Source) - fs := New(os.Getenv("YAO_DEV")) - assert.NotPanics(t, func() { - fs.MustMkdirAll("/.tmp/unit-test/dir", os.ModePerm) - }) -}