This commit is contained in:
wh_zcy 2023-06-09 21:58:02 +08:00
parent 8e146aca8b
commit 79a94117a4
817 changed files with 256803 additions and 0 deletions

75
cmd/bench/base.go Normal file
View File

@ -0,0 +1,75 @@
package bench
import (
"fmt"
"testing"
)
type Target struct {
//这里定义需要测试的方法
Set func()
Get func()
}
//测试基准
type Benchmark struct {
Parallelism int //并行数目
Data interface{} //bench 参数数据
TargetBuilder TargetBuilder
}
//构建基准目标
type TargetBuilder struct {
Name string
Make func(bench Benchmark) (Target, error)
}
func compose(parallelisms, dataSize []int, numKeys int, builders []TargetBuilder) []Benchmark {
benchmarks := make([]Benchmark, 0, len(parallelisms)*len(dataSize)*len(builders))
for _, p := range parallelisms {
for range dataSize {
for _, builder := range builders {
benchmarks = append(benchmarks, Benchmark{
Parallelism: p,
// Data: d,
TargetBuilder: builder,
})
}
}
}
return benchmarks
}
func RunBenchmark(b *testing.B, benchmarks []Benchmark) {
for _, bench := range benchmarks {
bench := bench
b.Run(fmt.Sprintf("%s-parallelism(%d)- ", bench.TargetBuilder.Name, bench.Parallelism), func(b *testing.B) {
target, err := bench.TargetBuilder.Make(bench)
if err != nil {
b.Fatalf("%s setup fail: %v", bench.TargetBuilder.Name, err)
}
if bench.Parallelism == 0 {
b.ResetTimer()
for i := 0; i < b.N; i++ {
//执行测试方法
target.Get()
}
} else {
b.SetParallelism(bench.Parallelism) //指定并行数目
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) { //并行执行
for pb.Next() {
//执行测试方法
target.Get()
}
})
}
b.StopTimer()
})
}
}

71
cmd/bench/main_test.go Normal file
View File

@ -0,0 +1,71 @@
package bench
import (
"os"
"testing"
)
var LusScriptgetList = `
local key = tostring(KEYS[1])
local keyss = redis.call("HGETALL", key)
local data = {}
local n = 1
for i, v in ipairs(keyss) do
if i%2 == 0 then
data[n] = redis.call("HGETALL", v)
n = n+1
end
end
return data
`
var res string
func TestMain(m *testing.M) {
defer os.Exit(m.Run())
}
func BenchmarkRedis(b *testing.B) {
// uid := "2_62ec8ab8828fb7bf67b220b6"
// heroRedisKey := fmt.Sprintf("hero_%s%s", uid, "{herotest}")
var (
// ncpu = runtime.NumCPU()
parallelisms = []int{4, 16, 64}
dataSizes = []int{100, 1000, 10000}
numKeys = 1024
// key = heroRedisKey
// keys = []string{
// "hero:0_62d6131a3a719b898a90b5fb-62d6131a3a719b898a90b5fe",
// "hero:0_62d6131a3a719b898a90b5fb-62d6131a3a719b898a90b600",
// "hero:0_62d6131a3a719b898a90b5fb-62d661d6bca6eab554879fc6",
// "hero:0_62d6131a3a719b898a90b5fb-62d6694dbca6eab554879fef",
// // "hero:0_62bbf14cf88fbd265e61a121-62bbf1849d64e5ee7ff12529",
// // "hero:0_62bbf364f88fbd265e61a122-62bbf5189b1970ed6b28b638",
// // "hero:0_62bbf364f88fbd265e61a122-62bbf5189b1970ed6b28b63e",
// // "hero:0_62bbf57346a32c12466c2e64-62bbf58d9b1970ed6b28b643",
// }
builders = []TargetBuilder{
{
Name: "pipeline",
Make: func(bench Benchmark) (Target, error) {
return Target{
Get: func() {
},
}, nil
},
},
{
Name: "lua",
Make: func(bench Benchmark) (Target, error) {
return Target{
Get: func() {
},
}, nil
},
},
}
)
RunBenchmark(b, compose(parallelisms, dataSizes, numKeys, builders))
}

88
cmd/cmd.go Normal file
View File

@ -0,0 +1,88 @@
package main
import (
"fmt"
"go_dreamfactory/cmd/robot"
"go_dreamfactory/pb"
"os"
"github.com/sirupsen/logrus"
flag "github.com/spf13/pflag"
"go.mongodb.org/mongo-driver/bson/primitive"
"github.com/spf13/cobra"
)
func emptyRun(*cobra.Command, []string) {}
var RootCmd = &cobra.Command{
Use: "robot",
Short: "命令行",
Long: "命令行工具",
Run: emptyRun,
}
func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stdin, err)
os.Exit(1)
}
}
func init() {
RootCmd.AddCommand(runCmd)
initLog()
robot.InitDb()
}
var account = flag.String("account", "", "登录账号")
var sid = flag.String("sid", "0", "区服ID")
var create = flag.Bool("create", false, "是否创建新账号") //false 不创建新账号
var role = flag.Bool("role", false, "创角")
func CloneNewHero(hero *pb.DBHero) (newHero *pb.DBHero) {
newHero = &*hero
newHero.Id = primitive.NewObjectID().Hex()
return
}
func main() {
Execute()
}
var runCmd = &cobra.Command{
Use: "run",
Short: "启动",
Run: func(cmd *cobra.Command, args []string) {
opts := robot.DefaultOpts()
opts.Create = *create
opts.Account = *account
opts.ServerId = *sid
opts.Role = *role
r := robot.NewRobot(opts)
r.Run()
},
}
func initLog() {
logrus.New()
logrus.SetFormatter(&logrus.TextFormatter{
ForceColors: false,
FullTimestamp: false,
})
//设置output,默认为stderr,可以为任何io.Writer比如文件*os.File
// file, err := os.OpenFile("robot.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
// writers := []io.Writer{
// file,
// os.Stdout}
// //同时写文件和屏幕
// fileAndStdoutWriter := io.MultiWriter(writers...)
// if err == nil {
// logrus.SetOutput(fileAndStdoutWriter)
// } else {
// logrus.Info("failed to log to file.")
// }
}

0
cmd/upgrade/build.sh Normal file
View File

View File

@ -0,0 +1,5 @@
SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build -o update main.go

153
cmd/upgrade/main.go Normal file
View File

@ -0,0 +1,153 @@
package main
import (
"embed"
"flag"
"fmt"
"go_dreamfactory/cmd/upgrade/tools"
"html/template"
"io/ioutil"
"log"
"net/http"
"path"
"sort"
"strings"
"time"
"github.com/gin-gonic/gin"
)
//go:embed views/*
var f embed.FS
var version = "0.0.1"
func main() {
host := flag.String("host", "10.0.0.9", "Host")
port := flag.String("port", "8080", "Port")
uploadsDir := flag.String("uploadsDir", "./uploads", "上传文件存储地址")
wwwDir := flag.String("wwwDir", "./www", "www服务地址即解压地址")
flag.Parse()
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
r.Static("/prd", "./www")
tmpl := template.Must(template.New("").ParseFS(f, "views/*"))
r.SetHTMLTemplate(tmpl)
r.GET("/version", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"version": version,
})
})
r.GET("/", func(c *gin.Context) {
fmt.Println(tmpl.DefinedTemplates())
c.HTML(http.StatusOK, "index.html", nil)
})
r.GET("/upload", func(c *gin.Context) {
fmt.Println(tmpl.DefinedTemplates())
c.HTML(http.StatusOK, "upload.html", nil)
})
r.POST("/upload", func(c *gin.Context) {
f, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
}
//从上传的文件名获取到版本号
s := strings.SplitN(f.Filename, "-", 2)
if len(s) != 2 {
c.JSON(http.StatusBadRequest, gin.H{
"error": "上传的文件名中没有包含版本号 eg. update.zip-0.0.1",
})
return
}
version = s[1] //设置版本号
dst := path.Join(*uploadsDir, f.Filename)
err = c.SaveUploadedFile(f, dst)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
}
_, err = tools.Unzip(dst, *wwwDir)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
}
c.JSON(http.StatusOK, gin.H{
"msg": "upload success",
"url": strings.Join([]string{"http://" + *host + ":" + *port, "/prd/", strings.Split(f.Filename, ".")[0], "/index.html"}, ""),
})
})
r.GET("/dirs", func(c *gin.Context) {
dir, err := ioutil.ReadDir(*wwwDir)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
}
type Item struct {
Name string `json:"name"`
Url string `json:"url"`
DateTime string `json:"dateTime"`
}
var dirs []Item
for _, f := range dir {
// if f.IsDir() && len(f.Name()) > 0 && f.Name() != "__MACOSX" {
dirs = append(dirs, Item{
Name: f.Name(),
Url: strings.Join([]string{"http://" + *host + ":" + *port, "/prd/", f.Name()}, ""),
DateTime: f.ModTime().Format(time.RFC3339),
})
// }
}
// 排序
sort.SliceStable(dirs, func(i, j int) bool {
return dirs[i].DateTime > dirs[j].DateTime
})
c.JSON(http.StatusOK, gin.H{
"dirs": dirs,
})
})
// 同步配置
// 接受客户端上传的文件覆盖服务器上文件
r.POST("/sync", func(c *gin.Context) {
f, err := c.FormFile("file")
if err != nil {
log.Print(err)
return
}
if f.Filename!="config.json"{
log.Print("上传的配置文件不是config.json")
return
}
dst := path.Join(*wwwDir, f.Filename)
err = c.SaveUploadedFile(f, dst)
if err != nil {
log.Print(err)
return
}
})
err := r.Run("0.0.0.0:" + *port)
if err != nil {
log.Fatal(err)
}
}

5
cmd/upgrade/readme.md Normal file
View File

@ -0,0 +1,5 @@
解压文件
- resources
- RobotGUI.exe
建议覆盖所有文件

127
cmd/upgrade/tools/gz.go Normal file
View File

@ -0,0 +1,127 @@
package tools
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
func UnGz(tarFile, dest string) error {
srcFile, err := os.Open(tarFile)
if err != nil {
return err
}
defer srcFile.Close()
gr, err := gzip.NewReader(srcFile)
if err != nil {
return err
}
defer gr.Close()
tr := tar.NewReader(gr)
for {
hdr, err := tr.Next()
if err != nil {
if err == io.EOF {
break
} else {
return err
}
}
filename := dest + hdr.Name
file, err := createFile(filename)
if err != nil {
return err
}
_, _ = io.Copy(file, tr)
}
return nil
}
func createFile(name string) (*os.File, error) {
err := os.MkdirAll(string([]rune(name)[0:strings.LastIndex(name, "/")]), 0755)
if err != nil {
return nil, err
}
return os.Create(name)
}
func UnGzip(tarName, xpath string) (err error) {
tarFile, err := os.Open(tarName)
if err != nil {
return err
}
defer func() {
err = tarFile.Close()
}()
if err := os.Mkdir(xpath, 0755); err != nil {
return err
}
absPath, err := filepath.Abs(xpath)
if err != nil {
return err
}
tr := tar.NewReader(tarFile)
if strings.HasSuffix(tarName, ".gz") || strings.HasSuffix(tarName, ".gzip") {
gz, err := gzip.NewReader(tarFile)
if err != nil {
return err
}
defer gz.Close()
tr = tar.NewReader(gz)
}
// untar each segment
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
// determine proper file path info
finfo := hdr.FileInfo()
fileName := hdr.Name
if filepath.IsAbs(fileName) {
fmt.Printf("removing / prefix from %s\n", fileName)
fileName, err = filepath.Rel("/", fileName)
if err != nil {
return err
}
}
absFileName := filepath.Join(absPath, fileName)
if finfo.Mode().IsDir() {
if err := os.MkdirAll(absFileName, 0755); err != nil {
return err
}
continue
}
// create new file with original file mode
file, err := os.OpenFile(absFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, finfo.Mode().Perm())
if err != nil {
return err
}
fmt.Printf("x %s\n", absFileName)
n, cpErr := io.Copy(file, tr)
if closeErr := file.Close(); closeErr != nil { // close file immediately
return err
}
if cpErr != nil {
return cpErr
}
if n != finfo.Size() {
return fmt.Errorf("unexpected bytes written: wrote %d, want %d", n, finfo.Size())
}
}
return nil
}

56
cmd/upgrade/tools/zip.go Normal file
View File

@ -0,0 +1,56 @@
package tools
import (
"archive/zip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
func Unzip(src string, dest string) ([]string, error) {
var filenames []string
r, err := zip.OpenReader(src)
if err != nil {
return filenames, err
}
defer r.Close()
for _, f := range r.File {
// Store filename/path for returning and using later on
fpath := filepath.Join(dest, f.Name)
// Check for ZipSlip. More Info: http://bit.ly/2MsjAWE
if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
return filenames, fmt.Errorf("%s: illegal file path", fpath)
}
filenames = append(filenames, fpath)
if f.FileInfo().IsDir() {
// Make Folder
if err := os.MkdirAll(fpath, os.ModePerm); err != nil {
return nil, err
}
continue
}
// Make File
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return filenames, err
}
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return filenames, err
}
rc, err := f.Open()
if err != nil {
return filenames, err
}
_, err = io.Copy(outFile, rc)
// Close the file without defer to close before next iteration of loop
outFile.Close()
rc.Close()
if err != nil {
return filenames, err
}
}
return filenames, nil
}

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>下载更新包</title>
<!-- import Vue.js -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.6/vue.min.js"></script>
<!-- import stylesheet -->
<link href="https://cdn.bootcdn.net/ajax/libs/iview/3.5.5-rc.1/styles/iview.min.css" rel="stylesheet">
<!-- import iView -->
<script src="https://cdn.bootcdn.net/ajax/libs/iview/3.5.5-rc.1/iview.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<style>
#app {
margin: 20px 40px;
}
</style>
</head>
<body>
<div id="app">
<h1>下载更新包</h1>
<strong>历史prd:</strong>
<br><br>
<list border>
<list-item v-for="dir in dirs">
<a :href="dir.url" target="_blank">${ dir.name }</a> &nbsp;&nbsp; <span>${ dir.dateTime}</span>
</list-item>
</list>
</div>
<script>
new Vue({
el: "#app",
delimiters: ['${', '}'],
data: {
zips: [],
dirs: [],
},
methods: {
handleSuccess(response, file, fileList) {
this.zips.push(response.url)
}
},
created() {
axios.get("/dirs").then((res) => {
this.dirs = res.data.dirs
})
},
})
</script>
</body>
</html>

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传</title>
<!-- import Vue.js -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.6/vue.min.js"></script>
<!-- import stylesheet -->
<link href="https://cdn.bootcdn.net/ajax/libs/iview/3.5.5-rc.1/styles/iview.min.css" rel="stylesheet">
<!-- import iView -->
<script src="https://cdn.bootcdn.net/ajax/libs/iview/3.5.5-rc.1/iview.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<style>
#app {
margin: 20px 40px;
}
</style>
</head>
<body>
<div id="app">
<h1>上传文件</h1>
<upload type="drag" action="/upload" paste="true" accept=".zip" :on-success="handleSuccess">
<div style="padding: 20px 0">
<icon type="ios-cloud-upload" size="52" style="color: #3399ff"></icon>
<p>点击或者拖拽上传</p>
</div>
</upload>
<alert type="success" v-for="url in zips">
浏览地址:${ url }
</alert>
<strong>历史prd:</strong>
<br><br>
<list border>
<list-item v-for="dir in dirs">
<a :href="dir.url" target="_blank">${ dir.name }</a> &nbsp;&nbsp; <span>${ dir.dateTime}</span>
</list-item>
</list>
</div>
<script>
new Vue({
el: "#app",
delimiters: ['${', '}'],
data: {
zips: [],
dirs: [],
},
methods: {
handleSuccess(response, file, fileList) {
this.zips.push(response.url)
}
},
created() {
axios.get("/dirs").then((res) => {
this.dirs = res.data.dirs
})
},
})
</script>
</body>
</html>

BIN
cmd/v2/.DS_Store vendored Normal file

Binary file not shown.

719
cmd/v2/.cache.meta Normal file
View File

@ -0,0 +1,719 @@
c:/tmp/go/Game.ActiveReward.go,2A4C58CF623C66F5DAE5EED5FA3B2FC,1264,1675761410251
c:/tmp/go/Game.ActiveRewardData.go,BB5CFACC5905463C958AD4DC4260E0,2482,1682218335291
c:/tmp/go/Game.ArenaActiveReward.go,CF58C256CADAF3D5679205973F1BE,1340,1675761410246
c:/tmp/go/Game.ArenaActiveRewardData.go,AB854D4D4EEA6498469BD7D92864B6D,3190,1675761410197
c:/tmp/go/Game.ArenaBuyChallenge.go,9758AC77DCC231E389792F729123157,1339,1675761410269
c:/tmp/go/Game.ArenaBuyChallengeData.go,C92E3D28BAB432704E72DB8C5131C6,1802,1675761410208
c:/tmp/go/Game.ArenaChallengeNpc.go,FF10972C6BB39C5C22CFCBE0E5D05721,1338,1675761410246
c:/tmp/go/Game.ArenaChallengeNpcData.go,DEE170B4F368857A646E7FB44B65506D,4951,1675761410221
c:/tmp/go/Game.ArenaRankReward.go,4BE39C4E12E8C6A1515ED7A8B378D50,1308,1675761410269
c:/tmp/go/Game.ArenaRankRewardData.go,5F8C9ED0253F3520D040964E6D712031,2214,1675761410208
c:/tmp/go/Game.ArenaRobot.go,CAED277469FA633986FCF6ABBE856F,1233,1675761410257
c:/tmp/go/Game.ArenaRobotData.go,7E1D4424304D904C40B88D9326BE1C0,1660,1675761410235
c:/tmp/go/Game.atn.go,C139B84B8BFE3221FCB10871B501C9E,1210,1675761410265
c:/tmp/go/Game.atr.go,7F6D48BC9BB4FA17FA1CA6493E7EC819,1092,1675761410196
c:/tmp/go/Game.basepool.go,C52EBBAE85F780685FB93393A86DD150,1294,1675761410253
c:/tmp/go/Game.Battleready_capskill.go,63C11EEAA389EBE8F61FE47A98EB2A,1383,1677038179630
c:/tmp/go/Game.Battleready_capskillData.go,E3159601E87988D15E95DC2389ECC7,4140,1677038179616
c:/tmp/go/Game.BattleReady.go,FBE5ACCFC2CDEDEF25554260BAB2EA,1248,1675761410230
c:/tmp/go/Game.BattleReadyData.go,9DB84583F363DA80CCF171959D82E85F,4495,1681962385973
c:/tmp/go/Game.Battletasktesting.go,54449FABECEBEE4B2082B27C7C4B5D0,1338,1675761410254
c:/tmp/go/Game.BattletasktestingData.go,359E55381E658F6379283E7D21B28966,1809,1675761410193
c:/tmp/go/Game.Breakingbad.go,B5A342ABAFD95367B37133A471AAB418,1260,1679653220443
c:/tmp/go/Game.BreakingbadData.go,8990C42A561478ABAF2AA6D6260C1C2,2391,1679653220443
c:/tmp/go/Game.BreakingbadTag.go,8BD327BF28CD5F22594410B71D16775C,1295,1680144063080
c:/tmp/go/Game.BreakingbadTagData.go,4CC6B5F571FDD6B971B4257844ABD,1533,1680144063054
c:/tmp/go/Game.BubbleGroup.go,2F534939A578E993A553CBAAE4AA34F,1251,1677661885207
c:/tmp/go/Game.BubbleGroupData.go,4B36BAC583FCC6CB42627A3FA41F73D,2500,1677661885207
c:/tmp/go/Game.BuffType.go,84A8B77D2EDE5F4FDE950EF8EBA4155,4133,1677729194807
c:/tmp/go/Game.BuzkashiFm.go,9790C180543996CD9FBBAC6EC98A8E4A,1234,1682218335288
c:/tmp/go/Game.BuzkashiFmData.go,6AD1B6C2D21BFADEE74C2F04152C0C,1956,1682218335303
c:/tmp/go/Game.BuzkashiGrade.go,2A399692DCDB394ABCFEF628847CDB4,1279,1682057107480
c:/tmp/go/Game.BuzkashiGradeData.go,57E858BEE143A2ED19ADA7F4FA30E6D5,1411,1682057107480
c:/tmp/go/Game.BuzkashiLv.go,82902BAD1533FCB6B0EB97BE2DD4C150,1234,1682057107465
c:/tmp/go/Game.BuzkashiLvData.go,EDDCFE57F22837BC75647ECC1F956A,1380,1682057107465
c:/tmp/go/Game.BuzkashiMount.go,D4EB7944C4C9CBB728B516AC9419D244,1282,1682307537253
c:/tmp/go/Game.BuzkashiMountData.go,D5896D807CD943ED184CCFE4DA2AD3,4273,1682318037405
c:/tmp/go/Game.BuzkashiOpen.go,5EB177235A1849D0F77BCE291C829BF,1264,1682218335288
c:/tmp/go/Game.BuzkashiOpenData.go,B0B0232A7494B1DC1984E8E32974D2,1833,1682218335277
c:/tmp/go/Game.BuzkashiReward.go,3BB3D18DCB94ED9F68C15F643BD8A0,1294,1682057107465
c:/tmp/go/Game.BuzkashiRewardData.go,B0F347899D71E47ECD47F24B71F124EA,1994,1682057107465
c:/tmp/go/Game.CampLv.go,B2F720E6C77B16377AB520BE2C7545C1,1173,1681962385984
c:/tmp/go/Game.CampLvData.go,BF3E5CF18079FE516E7B2659E006124,1912,1681962385964
c:/tmp/go/Game.ChatChannel.go,255A77714FAE1CF5DE6C14811358D470,1253,1675761410251
c:/tmp/go/Game.ChatChannelCom.go,5D2A2CD37069567DB414AB168E14E5,1298,1675761410262
c:/tmp/go/Game.ChatChannelComData.go,F3FFCBC678324620AFE76EC48513127,1345,1675761410239
c:/tmp/go/Game.ChatChannelData.go,7F2F68195692654A592631423EFE6492,1405,1675761410239
c:/tmp/go/Game.ChatExpression.go,8242E791111470685E76F7EDDAA7EAAD,1294,1675761410201
c:/tmp/go/Game.ChatExpressionData.go,85E4E397338D9FD291FEA997FEF42,1652,1675761410212
c:/tmp/go/Game.ChatServerGroup.go,6FC9A7446A5EF694AA15EEA56D71D35,1316,1675761410226
c:/tmp/go/Game.ChatServerGroupData.go,FD99FB68BD87CF1F16F64D6CC0CE662A,1236,1675761410250
c:/tmp/go/Game.ChatSystem.go,9C4C28E7F89D15A1C840DB755B2413,1234,1675761410202
c:/tmp/go/Game.ChatSystemData.go,1D24C9D746E6BE586CA4DC5CEE6DC6FF,3364,1675761410274
c:/tmp/go/Game.ChildSkills.go,922FAC4CD0961B9B224588026ED83F4,1458,1675761410253
c:/tmp/go/Game.Coin.go,1349FF95D707E6BCC34A264A4372B3,1148,1675761410201
c:/tmp/go/Game.CoinData.go,744923CD8CC8DC38BCD9D3BC67EE9D44,2468,1681972759520
c:/tmp/go/Game.CombatBox.go,42667B2BA872367DCECE7B5D267016E0,1218,1675761410205
c:/tmp/go/Game.CombatBoxData.go,8DC5435952D1A5AB3F838B0A2214DE,1229,1675761410230
c:/tmp/go/Game.CombatLevel.go,BB32B0B37B213A73B7BDC2422EBE1346,1248,1675761410255
c:/tmp/go/Game.CombatLevelData.go,8A9978FE96CDD87A50591CDD351F82B3,3688,1675761410219
c:/tmp/go/Game.CombatManster.go,1A12286B5065733FFB364BE056514BFE,1278,1675761410193
c:/tmp/go/Game.CombatMansterData.go,99D045C7EAA67D227912D4A952E0385F,3870,1675761410243
c:/tmp/go/Game.Dispatch_Battle.go,EF269EDA2AB24187FE3AC87FE5EE32D7,1308,1677038179621
c:/tmp/go/Game.Dispatch_BattleData.go,497F218955C1B5DED2BB218C574EE344,6642,1681962385984
c:/tmp/go/Game.Dispatch_Lv.go,9B254F9CB7ED221D8151B8FF50DAC247,1248,1677038179627
c:/tmp/go/Game.Dispatch_LvData.go,6FFDB8723372C64B667793344EFAE59D,1908,1677142535210
c:/tmp/go/Game.Dispatch_Task.go,5CEBAC2124C0ED2BE09DEEACC48385F3,1278,1677038179618
c:/tmp/go/Game.Dispatch_TaskData.go,F29B6EB9785BA34686F61C8AA6E641,6972,1677661885207
c:/tmp/go/Game.dispatch.go,11E6C4EF7C33F3DCC5EECFD532ADFB1,1687,1677123193278
c:/tmp/go/Game.DispatchCond.go,22356882B4901B94B186849B43A,1230,1677226965191
c:/tmp/go/Game.DispatchCondType.go,1E174CC34C6D73C2A42D268158D36,444,1677226965193
c:/tmp/go/Game.DrawCard.go,90C51587F78BB9A35B5606965AA5D5C,1204,1675761410276
c:/tmp/go/Game.DrawCardData.go,8E5E5CA24F2F804AEDF7FA2D75EC768,2117,1681962385979
c:/tmp/go/Game.DreamlandBoos.go,ECAB104849265818FC51EFE3A85C7E92,1286,1675761410199
c:/tmp/go/Game.DreamlandBoosData.go,A558CD5CA0D1669B44CE1D0D55A4CA9,4883,1675761410210
c:/tmp/go/Game.DreamlandChallenge.go,E82C6BF569667EC9D353D2CFA68189A,1357,1675761410260
c:/tmp/go/Game.DreamlandChallengeData.go,D96E1D6267907A798D67399A5D81EFC,1821,1675761410223
c:/tmp/go/Game.DreamlandTrigger.go,9E9F37FA9CCB7EE2F4E25D3EB77B6D8A,1323,1675761410271
c:/tmp/go/Game.DreamlandTriggerData.go,952970A66BEC2297B5D574F2CBB1217F,1440,1675761410237
c:/tmp/go/Game.Drop.go,55BD3930367DDA4F886A27283A66EBD6,1143,1675761410211
c:/tmp/go/Game.DropData.go,3342529A163A3B8DD13D138AD08E5E3C,2461,1677661885222
c:/tmp/go/Game.EnchantBoss.go,4D46566E197D16B45C27B81E94AB8A3A,1248,1675761410193
c:/tmp/go/Game.EnchantBossData.go,BD3D794251FA272228B19321CDD719,9255,1675761410243
c:/tmp/go/Game.EnchantLineup.go,D4B91419B79DFDC473AFA76B05DD382,1278,1675761410230
c:/tmp/go/Game.EnchantLineupData.go,1DD1C2ED36E6D5E14524C5B72094B7,1775,1675761410265
c:/tmp/go/Game.EnchantShop.go,AA2D17324C32244E4F2E1BDE85F65,1252,1675761410266
c:/tmp/go/Game.EnchantShopData.go,53DB80733785154E652F424E37F44875,1772,1675761410218
c:/tmp/go/Game.Equip.go,9C164DE97A449B535FDC6D15675988,1162,1675761410242
c:/tmp/go/Game.EquipAttribute.go,1A45B554B4BECF3E713FD0794DE79A63,1298,1675761410251
c:/tmp/go/Game.EquipAttributeData.go,FED91140C4D297AC1071D1EE92995730,2820,1675761410242
c:/tmp/go/Game.EquipAttrlibrary.go,39CECBB16F43FC2DCDDE5C188AA7ABF7,1066,1678261258950
c:/tmp/go/Game.EquipAttrlibraryData.go,7CE1BE10931D026525AF13B3B8885C,2712,1675761410228
c:/tmp/go/Game.EquipColor.go,BEEF34ED81AE1D6EA3574E82F7186E6,1234,1675761410215
c:/tmp/go/Game.EquipColorData.go,B9AE164A7F18CFF047CB69C8C8AE32C3,1493,1675761410251
c:/tmp/go/Game.EquipData.go,2930CDCB6C39ECCF4857D4678E40BDB,6752,1677038179623
c:/tmp/go/Game.EquipEnchanting.go,92448D8E82F4BAE26C7C60EFA3B3B283,1314,1675761410263
c:/tmp/go/Game.EquipEnchantingData.go,77D3D341E3AD3E17C335C29FC13AD5,2478,1675761410203
c:/tmp/go/Game.EquipIntensify.go,285189CB91B06B7F53153B8F265BD7,1294,1675761410263
c:/tmp/go/Game.EquipIntensifyData.go,45F342D7A6FDF510FF515F64FBF1FFB,3073,1675761410227
c:/tmp/go/Game.EquipPos.go,AAAF6EA38990FF3DDAC7BCEF5ADC633D,1204,1675761410262
c:/tmp/go/Game.EquipPosData.go,1E63B156CBCFB20E8C691898AE25E6,2446,1675761410273
c:/tmp/go/Game.EquipSCompose.go,C7236D5F82A62C8CD3F4F6174B4E1E,1279,1675761410203
c:/tmp/go/Game.EquipSComposeData.go,B312B57FDEF5EDA145844FC9668DBDA1,3957,1675761410214
c:/tmp/go/Game.EquipSuit.go,1DBACFCC95AB7D3A0774E47C6E641D,1224,1675761410204
c:/tmp/go/Game.EquipSuitData.go,B7ABBAA106A3AA297CF2C7DABBFA5C,3592,1680333520466
c:/tmp/go/Game.Extservers.go,24E68E3B8FFC59B781DA294CC3F5847B,1236,1675761410243
c:/tmp/go/Game.ExtserversData.go,52249AAE1D2EB94DCB6A9288D84A2B9,1871,1675761410229
c:/tmp/go/Game.Facemod.go,696B7D4AC948AA8271077188397D428,1188,1675761410212
c:/tmp/go/Game.FacemodData.go,30AEF98DBD79A51C4A93E741AED26D,1896,1675761410250
c:/tmp/go/Game.Favorability.go,3C2511F6F1D0FB53AE57B2DA87B5572B,1022,1682307537247
c:/tmp/go/Game.FavorabilityData.go,4865A5619D22F92254EF6316DD6999B6,13476,1682218335290
c:/tmp/go/Game.FightEventType.go,3764E69F8E6A6D457FE301131809B54,1449,1675761410242
c:/tmp/go/Game.FightGlobalEvent.go,409C726DCE4AA9B8A52CE7DA1B72354,1323,1675761410195
c:/tmp/go/Game.FightGlobalEventData.go,F9E56770C69839536E63381F269E576D,4409,1675761410219
c:/tmp/go/Game.Figure.go,9FBAAA6E489C6C9B11B813D8420E4C,2169,1675761410261
c:/tmp/go/Game.Friends.go,7790A592B6D3BEE53FF6FC37E8C7EE32,1188,1681962385984
c:/tmp/go/Game.FriendsData.go,6C2799F690B9F11A2292057D064AE83,4189,1681962385973
c:/tmp/go/Game.GetResource.go,16426C584C734CACBB6BED9BA5881A3B,1248,1675761410210
c:/tmp/go/Game.GetResourceData.go,1E11E21D4B3297D66567FD6CF7A091F9,1755,1675761410248
c:/tmp/go/Game.Global.go,9092173F7DF6CF18DC2D1FD0B2BEE8A,1178,1675761410253
c:/tmp/go/Game.GlobalData.go,B39DE084445A86E113701F5D44949,69195,1682307537195
c:/tmp/go/Game.GM.go,A5074D93B98C124CEFBD8F73178EC8,1113,1675761410219
c:/tmp/go/Game.GMData.go,604CF1227DE557E0D48AB86C3396A3,1878,1675761410193
c:/tmp/go/Game.Gourmet.go,CA1C778A379332C0716D30561658D7B,967,1675761410238
c:/tmp/go/Game.GourmetData.go,A9A7BB74770AAD806766E1EDD2B8C,8164,1675761410200
c:/tmp/go/Game.GrowReward.go,2A925988749D11CAB754A7E581FED53C,1235,1675761410232
c:/tmp/go/Game.GrowRewardData.go,EEACB4D8C0C845AF1C3C428B65A84833,2221,1675761410221
c:/tmp/go/Game.GrowTask.go,7244F62F6FB0BCD7FCC5CCC5A7CA24B7,1203,1675761410256
c:/tmp/go/Game.GrowTaskData.go,CA5515AD797579648B663BD0D40F6BB,3505,1675761410195
c:/tmp/go/Game.Guide.go,933A9C98166249698370EEA9183E44D0,1158,1675761410243
c:/tmp/go/Game.GuideData.go,492888C8E3BA62ED8FA49BDA6DF25D23,7402,1675761410205
c:/tmp/go/Game.GuildActivity.go,AC5BBC11BECD9EA038561EA593F2881C,1278,1675761410222
c:/tmp/go/Game.GuildActivityData.go,0A54FE4126E57ADBD33A52BD3937B6,1981,1675761410233
c:/tmp/go/Game.GuildBossTask.go,F58CAEDEC61D4C38AFA8A3261FEA3DC,1278,1675761410256
c:/tmp/go/Game.GuildBossTaskData.go,28AB24D1DF646E94979351F6E4BDF7,2434,1675761410221
c:/tmp/go/Game.GuildLv.go,D3E86616C1644AE037FB22E322DD81DC,1188,1675761410245
c:/tmp/go/Game.GuildLvData.go,F19B2E8B4383F9C2C923EEF38D4AFADE,1414,1675761410207
c:/tmp/go/Game.GuildPng.go,9679AD4D302E32CBD2E9DF68BB1CE2D,1203,1675761410196
c:/tmp/go/Game.GuildPngData.go,B28B7B9C99F4D6E3CB60E24EB76EA644,1165,1675761410256
c:/tmp/go/Game.GuildSign.go,FDDA8A37B478D1D443A25E107457FC,1218,1675761410269
c:/tmp/go/Game.GuildSignData.go,697B162D3BEF3014F416838B1FE731,2805,1675761410245
c:/tmp/go/Game.GuildTask.go,61C5DF09B39879C8A483F2D12422643,1222,1675761410207
c:/tmp/go/Game.GuildTaskData.go,3EC4EFACCAB29A882D465EB9C48CF4C,3612,1675761410197
c:/tmp/go/Game.Help.go,A94E51B9F7ABAA87547443672CB923B,1148,1675761410255
c:/tmp/go/Game.HelpData.go,267EF0A8DCD0656589654CD0ADCDA580,1826,1675761410267
c:/tmp/go/Game.Hero.go,E07FC7E26518CE4886779D4528CD9C74,1148,1675761410228
c:/tmp/go/Game.HeroAwaken.go,C0DB8169DEA54CA5D19A5FB43FCC3E,1000,1675761410240
c:/tmp/go/Game.HeroAwakenData.go,37CD89396C7A693B61AC307EE3A64AD0,3753,1675761410263
c:/tmp/go/Game.HeroConvertdata.go,9F2AB5796FC99EB98165B2CAB35C2FD,1310,1675761410209
c:/tmp/go/Game.HeroConvertdataData.go,2B3B3695CDE3256E121D762D26CCE83,1220,1675761410270
c:/tmp/go/Game.HeroData.go,319137D6E9D359F412E169F5FAB83F3D,11635,1678157698141
c:/tmp/go/Game.HeroExp.go,A385FC37766A4642A24DEC24345AA2,1196,1675761410264
c:/tmp/go/Game.HeroExpData.go,AEB319CEE85EBFFEDCFBE484C3393D5,1375,1675761410228
c:/tmp/go/Game.Herofusion.go,5E9BC88C372F2CB0E3D95AFD327D64B,1239,1675761410222
c:/tmp/go/Game.HerofusionData.go,D66C2D2316D2B4E7E1B7ABA476136,3056,1675761410208
c:/tmp/go/Game.HeroLevelgrow.go,62F860BC46B98EE3D4CF7C1388F99696,1282,1675761410217
c:/tmp/go/Game.HeroLevelgrowData.go,37140A53ED2B4B3FE94293F87EA4D30,3285,1675761410228
c:/tmp/go/Game.HeroLevelup.go,3F2BECF97236DD798F0B08F546272B5,1251,1675761410203
c:/tmp/go/Game.HeroLevelupData.go,3D4339CCF262D4D3EC71E7031DA8D84,1810,1675761410215
c:/tmp/go/Game.HeroMethod.go,AB325C916459C45B86A355E196384FB,1238,1675761410276
c:/tmp/go/Game.HeroMethodComment.go,86ACB82FD48C164AF871DD482C5C3412,1338,1675761410251
c:/tmp/go/Game.HeroMethodCommentData.go,E821B41420AE45BF1D59E3CC8AAA0,1542,1675761410240
c:/tmp/go/Game.HeroMethodData.go,4B2248B2BB54E0B6573D3E321CBB559B,3952,1675761410203
c:/tmp/go/Game.HeroSkill.go,DAE82818874931F13EA6E49E16E57AF,1220,1675761410216
c:/tmp/go/Game.HeroSkillData.go,446DD726A7453FFEFADCDD574ECC73,1774,1675761410252
c:/tmp/go/Game.HeroSkillLevel.go,8D2859A0BBCA17996549BA564FB01422,1293,1675761410251
c:/tmp/go/Game.HeroSkillLevelData.go,C5B9DC433F5E27C27D52BCC6DEDA049,1736,1675761410214
c:/tmp/go/Game.HeroStargrow.go,492BA607A1884D4EBB49E7E6C4C854E,1265,1675761410242
c:/tmp/go/Game.HeroStargrowData.go,6019EAC8CE1372867D90F89E2ED94164,2301,1675761410253
c:/tmp/go/Game.HeroStarup.go,92EB8FF34029EB6419A1765AAB86E81E,1000,1675761410264
c:/tmp/go/Game.HeroStarupData.go,A4793267B362932A5331AA2129FBFF,2792,1675761410242
c:/tmp/go/Game.HeroStrategy.go,9973410C0D9C081EDD1D4452374B6F,1271,1675761410255
c:/tmp/go/Game.HeroStrategyData.go,A9CA8CB14D58884C5AD37839F7FF81,3868,1680513216864
c:/tmp/go/Game.HeroTalent.go,6264DCAF544AB03B831FB59DF3F8FCC9,1233,1675761410198
c:/tmp/go/Game.HeroTalentData.go,749A96D22062A710628DFA198A59D529,3586,1675761410258
c:/tmp/go/Game.Horoscope.go,BDF321486A49AE5AB0BA1316EF15DBF0,1218,1675761410256
c:/tmp/go/Game.HoroscopeData.go,79433F42E8E9E55A7D1C0B5C32DDE55,5694,1675761410244
c:/tmp/go/Game.HuntingBoss.go,311D49EDF068E84BB17F994BC7EB1CE3,1011,1675761410248
c:/tmp/go/Game.HuntingBossData.go,A9D97FD4F8A1A9757ADD536113C0B77F,5976,1675761410200
c:/tmp/go/Game.HuntingChallenge.go,3AD58AB0168FFF7380D819F1E72BDD9B,1327,1675761410260
c:/tmp/go/Game.HuntingChallengeData.go,77A35BF882453D69A9F6326134E7788,1807,1675761410223
c:/tmp/go/Game.HuntingLineup.go,62EEE3FDB904063CA62AA475655A59,1278,1675761410210
c:/tmp/go/Game.HuntingLineupData.go,9019AF4B463FDB893041DF5BBC4B1011,1661,1675761410248
c:/tmp/go/Game.HuntingOpenTime.go,96F7469AD2F779F3FE69D69D90D32047,1310,1675761410271
c:/tmp/go/Game.HuntingOpenTimeData.go,331AE9F326C5ED6E8C5531DA55B31D,3197,1675761410237
c:/tmp/go/Game.Initial.go,635F41467132462445C61D22F4DBC899,1195,1675761410226
c:/tmp/go/Game.InitialData.go,2EB61924191FD8F08B66BB694A60342C,1681,1675761410213
c:/tmp/go/Game.Item.go,AD5F8B4F3FE72A282CA629AA2E85,1147,1675761410276
c:/tmp/go/Game.ItemBox.go,A96F4F987C964A2E18D6C4D1A1ADFB2,1196,1675761410196
c:/tmp/go/Game.ItemBoxData.go,BBB7D95490F3598D3CA444B72E3ACA,1135,1675761410257
c:/tmp/go/Game.ItemData.go,D9A2C8C91228307D2DECFCD42D005,7136,1681962385973
c:/tmp/go/Game.itinerant_city.go,521EA56852E953C5F91CA8AEADFD28F,1293,1681962385972
c:/tmp/go/Game.itinerant_cityData.go,21EBB2968FA58C3F5845E35680E8BD,5825,1681962386033
c:/tmp/go/Game.itinerant_lv.go,3594774D51FD4B7FF946B9B408E2390,1263,1681962385982
c:/tmp/go/Game.itinerant_lvData.go,88612F1C5A87E9E9C4AAEE78A91AB4E3,1986,1681962385972
c:/tmp/go/Game.itinerant_reward.go,99ACE7B84ACFB49B674712DF97997FC,1324,1681962385986
c:/tmp/go/Game.itinerant_rewardData.go,558B40F8C45748ED19431A7445047D,1807,1681962385972
c:/tmp/go/Game.itinerant_thing.go,9C6A37B7E6F93A7B2615AF5D828865,1308,1681962385979
c:/tmp/go/Game.itinerant_thingData.go,88B75FA60CEF81CB351438913278C,6680,1681962385978
c:/tmp/go/Game.kv.go,FD719127588B2570CA6C764B5A3BBA1F,1136,1682307537148
c:/tmp/go/Game.LibraryBubble.go,F8A001DE17E77B4DE7E079BADE41,1278,1675761410259
c:/tmp/go/Game.LibraryBubbleData.go,F762FCF378AA30E8B8A1CFB4F8A939B,1500,1675761410198
c:/tmp/go/Game.LibraryComplot.go,E44641F1682F8527A599D29EE6EE,1293,1675761410198
c:/tmp/go/Game.LibraryComplotData.go,FDD21B3B8EFA299D4E3FDFB58ECFD,3202,1675761410223
c:/tmp/go/Game.LibraryFavor.go,2A34F748C8379E7A8E65E4FA3998D6,1022,1675761410236
c:/tmp/go/Game.LibraryFavorData.go,55848D72E7E9DA1738B55AD22A244,2340,1675761410209
c:/tmp/go/Game.LibraryFavorlv.go,19BDF396B7D0B5DEB62463385B16A843,1298,1675761410270
c:/tmp/go/Game.LibraryFavorlvData.go,33D02C16A23575FA26138B6AEF501139,1657,1675761410246
c:/tmp/go/Game.LibraryFetter.go,A1355BDA3FF33B8E21EB36D33A87E4C,1033,1675761410271
c:/tmp/go/Game.LibraryFetterData.go,531A8A4177861CAE4748064FCEB8D6,4075,1675761410209
c:/tmp/go/Game.LibraryHero.go,34F04069AD3E532A816C917AC25E182,1253,1675761410236
c:/tmp/go/Game.LibraryHeroData.go,EBB061E65F995B9385D5913BCFA614,6618,1675761410248
c:/tmp/go/Game.LibraryHistory.go,1F1A3EA6E45ACA8D3D1F5CFB8922CDE,1302,1675761410270
c:/tmp/go/Game.LibraryHistoryData.go,CEA74480729040961747BF5269171DE,2606,1675761410259
c:/tmp/go/Game.LibraryPlayBack.go,2F69CE6BDF33ED16DB93F143AEA422E,1313,1675761410236
c:/tmp/go/Game.LibraryPlayBackData.go,C0644CB6407125B96347987FD288A6,2044,1675761410210
c:/tmp/go/Game.LibraryStory.go,9A7987EB27ECC6D4A141BDC50405FF4,1264,1675761410199
c:/tmp/go/Game.LibraryStoryData.go,FD3A637B4D396CEA1F78939886DA210,1960,1675761410223
c:/tmp/go/Game.LinestoryMainTask.go,D1A9F77A18E8A617DF04E16534894F4,1338,1675761410198
c:/tmp/go/Game.LinestoryMainTaskData.go,2C92B7BB4303061573012BB2E0A4A,3645,1682318038099
c:/tmp/go/Game.LinestoryTimeLine.go,1C42F2CB5EA373A2CE6DC76AEBAEFA59,1341,1682318037761
c:/tmp/go/Game.LinestoryTimeLineData.go,F39DEB17F5F8D4C5B2ACD0AA975249D,3326,1682318037624
c:/tmp/go/Game.Lottery.go,433ADD2BECD6C41A17A419A32289755,1188,1681198061135
c:/tmp/go/Game.LotteryData.go,E9495B4FE19922F8F921DD16A4E55D8,4296,1681198061214
c:/tmp/go/Game.Mail.go,2CCB7CF093BE78F516DCDFEC05EA0,1147,1675761410231
c:/tmp/go/Game.MailData.go,E274CF9D08A6EEB3D3FE4863E637A3,3629,1678261258950
c:/tmp/go/Game.MainChapter.go,7E1BF84D63C3316377CF93468CBDE7,1248,1675761410218
c:/tmp/go/Game.MainChapterData.go,CE9127C4D8CFB5F8AE59B33B9447FCDC,3038,1675761410193
c:/tmp/go/Game.MainStage.go,431DA6BC2D5F8B51B68EF9D9257B77,1218,1675761410254
c:/tmp/go/Game.MainStageData.go,6491A7EA8892FC144209A4E8F69CD26,11127,1681972759520
c:/tmp/go/Game.MainStarreward.go,88819424B3CBEB1F5697B90CAC155F2,1293,1675761410230
c:/tmp/go/Game.MainStarrewardData.go,82C859415EA7A23AC6425C3B8746A0F3,2169,1675761410265
c:/tmp/go/Game.MainStartype.go,C803E13932B2A686D4957A0F93B5B4B,1263,1675761410218
c:/tmp/go/Game.MainStartypeData.go,C3FDCE5FE973BD54CB70F16A72A7C8,1513,1675761410243
c:/tmp/go/Game.Model.go,4999368ACDD4EA1AA7A7B0F45D3F30,1162,1675761410214
c:/tmp/go/Game.ModelData.go,7284A2299C35387A68EC47255DDB14,5880,1682218335290
c:/tmp/go/Game.ModelStateType.go,C18E5EB988FEC4C136C8EE58CC4D4C87,475,1675761410265
c:/tmp/go/Game.Monster.go,DCC8EED950F78592F3EAB351401EA4B,1195,1675761410250
c:/tmp/go/Game.MonsterData.go,78AD18C6F69AF9B49AA3B15E81DC0C1,2953,1675761410202
c:/tmp/go/Game.MonsterFormat.go,6BC29CD8E3D5A270964E5616ADF1B6BC,1033,1675761410259
c:/tmp/go/Game.MonsterFormatData.go,F76DFC4DC971105098BBDB82B194BDA,3008,1680601670124
c:/tmp/go/Game.Msgdistrib.go,7D776BA16AD6E9FEE1A14DFD47282CCD,1240,1675761410273
c:/tmp/go/Game.MsgdistribData.go,2A17D832F91DA826297AB0269A401455,1434,1675761410201
c:/tmp/go/Game.NameLibrary.go,79B0B576224A96DFB0C3AF98B984317,1248,1675761410197
c:/tmp/go/Game.NameLibraryData.go,90732082E97654A7488AF3BD49B42BAD,1928,1675761410236
c:/tmp/go/Game.Navigation.go,3C5AD8314849EDB2BF9A857DEF7B7C,1233,1681972759483
c:/tmp/go/Game.NavigationData.go,B1F69D982280B87BA98F946D5FEA8D96,2553,1681972759483
c:/tmp/go/Game.NewRedDot.go,5852E83FC4CA63D87D8172ADB45D2F,1218,1681962385982
c:/tmp/go/Game.NewRedDotData.go,1D47B6AFD13E53D958C738FE27C4EB0,2250,1681962385979
c:/tmp/go/Game.NewSmithy.go,D469AF878FFF8381B3863F6EDFAA4F1,1218,1676518692457
c:/tmp/go/Game.NewSmithyData.go,19B7778B8E753B5521EDF118ED2205A,5910,1679909505407
c:/tmp/go/Game.Opencond.go,1F68A5B032F3CFF2B0131A38546AEE1,1207,1675761410239
c:/tmp/go/Game.OpencondData.go,97E5882198742EA74274EA630346AD6,4198,1680601670124
c:/tmp/go/Game.OpencondType.go,96DBF7463A0C961739BB9B844FD89C6,511,1675761410276
c:/tmp/go/Game.OpenedCond.go,D9244B4D44EF4E41EB8A70D78B1B9F97,1218,1675761410226
c:/tmp/go/Game.Pagoda.go,4D356552318B111BE2B4CFB1961D88F0,1174,1679542643325
c:/tmp/go/Game.PagodaData.go,77B2A12C94294CB9B27054EDE4F94C80,5185,1679542643190
c:/tmp/go/Game.PagodaTaskReward.go,A185223EB8F3EB73654242898BE60,1324,1675761410238
c:/tmp/go/Game.PagodaTaskRewardData.go,73E72E27BCA9AC178B13F9FA7B3E98E6,2219,1675761410225
c:/tmp/go/Game.PandamasBuff.go,26D88E88F317A7FAEA8A7846FB24C7B,1263,1677226965190
c:/tmp/go/Game.PandamasBuffData.go,A152AF66A560C251365124DAE64C58A,2909,1677226965193
c:/tmp/go/Game.PandamasJs.go,74F3CB7776FB319BE511FBAFDF63DC87,1233,1677226965191
c:/tmp/go/Game.PandamasJsData.go,F49416080E83A4D59E79D3BB125D95,1771,1679542643180
c:/tmp/go/Game.PandamasJx.go,D0847C6324EA5F28FEA4AABA9F8B90,1237,1677226965186
c:/tmp/go/Game.PandamasJxData.go,97B23A18FDABC658DE5AE99691A3089,7041,1681962385979
c:/tmp/go/Game.PandamasMryl.go,3F8E41E3B96819112E47CC7E92F1,1263,1677226965188
c:/tmp/go/Game.PandamasMrylData.go,AC877F9235CC8C85304997F2492F55DD,3190,1681962385984
c:/tmp/go/Game.PandamasMz.go,B354BCB3152A3982225874F09F41BA,1233,1677226965187
c:/tmp/go/Game.PandamasMzData.go,6255D83E89A4BFDCE0FFD9AE21BEC8D,4272,1677226965188
c:/tmp/go/Game.PandamasTj.go,C671C04FBF5B8FCB2207DC26057A938,1237,1677226965186
c:/tmp/go/Game.PandamasTjData.go,813474EA84B63AADF954E41222F960,2872,1680494109544
c:/tmp/go/Game.PandamasTjjl.go,456BB15F9994578B41DB7632C432E386,1268,1677226965187
c:/tmp/go/Game.PandamasTjjlData.go,E452B6D769B82E133EE2129306AAA99,2008,1677226965188
c:/tmp/go/Game.PandamasYxjx.go,68DAFE10A4119A9CC547EFE6F39635D,1263,1677661885207
c:/tmp/go/Game.PandamasYxjxData.go,2CEB308E69B1BEF43792574D3B21941,2305,1677661885224
c:/tmp/go/Game.PassCheck.go,80982CAF6A7F345EA4BF3CA4103DF0,1218,1675761410193
c:/tmp/go/Game.PassCheckData.go,DEC969FF4DCF889490DBDE84E3A02E5F,2593,1675761410209
c:/tmp/go/Game.PayPackage.go,3E57B994B1D847B2F9AFC93AC390B,1233,1675761410267
c:/tmp/go/Game.PayPackageData.go,30533C4BEDBABA987DC0133C3D1E5F5A,3933,1675761410244
c:/tmp/go/Game.PlayerInfor.go,596C8A9D4AE64BAB26D15BF8DB5FC94B,1248,1675761410269
c:/tmp/go/Game.PlayerInforData.go,E4B4ADB854FD69DDBC4E5E59564DB60,1649,1675761410246
c:/tmp/go/Game.Playerlv.go,593E2F4848329C83D1E1A5B3A14D9A7,1203,1675761410239
c:/tmp/go/Game.PlayerlvData.go,618EF84561B44786F0EB1F52DDE14B,2352,1675761410226
c:/tmp/go/Game.PotionFly.go,16DB7E99F4981DB3558C664136259E,1542,1681126805544
c:/tmp/go/Game.Potions.go,67BA8089876BE652144AFC2E92279234,1188,1680056393181
c:/tmp/go/Game.PotionsData.go,825CBCF3B8772455A3D764E9E3D0A4,7660,1680494109582
c:/tmp/go/Game.Privilege.go,A7942237F4DCEBDB92DB7D68C439770,1227,1675761410231
c:/tmp/go/Game.PrivilegeCard.go,3DBC6B483893844BB13E8D5A7BF1D,1282,1675761410244
c:/tmp/go/Game.PrivilegeCardData.go,2EF0885473BB89E511DEAD8CA7D78D1B,3899,1675761410255
c:/tmp/go/Game.PrivilegeData.go,25541DCCA4997445E28198D08E5FD83,2548,1675761410196
c:/tmp/go/Game.PropertyType.go,A520805A56672C27373103F71290FB,3107,1675761410217
c:/tmp/go/Game.RdtaskCondi.go,A9C52348E4B3E048D862FAA6F3E99AE2,1248,1675761410200
c:/tmp/go/Game.RdtaskCondiData.go,704B4AB7647CFB647AF34FF3BAD731CB,3179,1680494109544
c:/tmp/go/Game.RdtaskNpc.go,8303FEA3729A34A6C42A8A0D9DBCA7E,1218,1679651030018
c:/tmp/go/Game.RdtaskNpcData.go,D76447578211DB94233971EF7D10B354,2613,1681962385979
c:/tmp/go/Game.Recharge.go,CD9CEA129FD2B7E8A182D2AE292D2EB9,1207,1675761410268
c:/tmp/go/Game.RechargeData.go,96E682AA6A58EB1282D8DD29C32511,3594,1675761410206
c:/tmp/go/Game.RedDot.go,4258EE22DEAA981D18A4EE22DE5BF9B,1173,1675761410193
c:/tmp/go/Game.RedDotData.go,579A366069A8549B59FB1F1F25B8A1E,2136,1675761410267
c:/tmp/go/Game.RedDotGroup.go,F838A6313C46125BEE82A673AD8275F8,1248,1675761410255
c:/tmp/go/Game.RedDotGroupData.go,CC59A060E2DF66558DEEC57CDE30E6,1651,1675761410244
c:/tmp/go/Game.RedDotTalkid.go,34F41711EB70CBCFB514D431EA86A55,1267,1675761410231
c:/tmp/go/Game.RedDotTalkidData.go,AA3CFE3FA79EF06FC49F6CB6EC40C25A,1513,1675761410219
c:/tmp/go/Game.Robot.go,D09D3DE1A46A9C6E6552A2E443249FE,1163,1675761410270
c:/tmp/go/Game.RobotData.go,B4D515ABDA2F2C2628E89DF4A4E0B6F0,7379,1675761410223
c:/tmp/go/Game.SellCoefficient.go,F073B3D7FD151232BDF7D87627DE521,1315,1680179912087
c:/tmp/go/Game.SellCoefficientData.go,D2E71712AA58BEDD89D36FDA5E452DB,1323,1680179912040
c:/tmp/go/Game.Shop.go,CF679DDEC3647AFA6BDCE3713CCD99B2,1147,1675761410213
c:/tmp/go/Game.ShopData.go,C0C88D365AD827F32EEC193BED1225,4719,1675761410274
c:/tmp/go/Game.Shopitem.go,B7D6921622189F85578E787CC73B1B82,1204,1675761410227
c:/tmp/go/Game.ShopitemData.go,7CB82627F9737ED8936E7A6840AD8A32,5158,1675761410262
c:/tmp/go/Game.Show.go,5D15E4F9362328FFFC5B21139CED5CE,1143,1675761410234
c:/tmp/go/Game.ShowData.go,B643E79E4E72EDB8E84554C661842EA5,2241,1675761410269
c:/tmp/go/Game.Sign.go,23FEDD52B1DA9D60C3B1D6DE3455F3D,934,1680248624386
c:/tmp/go/Game.SignData.go,7FFB1D358C63DC8354D88F34724575,2819,1680234417438
c:/tmp/go/Game.SignExtra.go,AAE12E1DCE621959278EF43AA978557,989,1680248624370
c:/tmp/go/Game.SignExtraData.go,9E3C8EB18CCBF2B9D5B497CBFF2380A7,2544,1680576438283
c:/tmp/go/Game.SignReset.go,BBC3B1BE73A4F2C02CDEBC1AC59EDACF,1218,1675761410221
c:/tmp/go/Game.SignResetData.go,E9772BD6B78F3EAB1199134CEAA9E60,1237,1675761410206
c:/tmp/go/Game.SkillAfteratk.go,8F66AF7A9C42132CB3C5FA2AB5F26,1278,1675761410264
c:/tmp/go/Game.SkillAfteratkData.go,B8CFFB8B098C3CA5EA45B443527B684,6651,1675761410242
c:/tmp/go/Game.SkillAtk.go,6B7A4B2BAE054AAA7FAE193E30498D,978,1675761410217
c:/tmp/go/Game.SkillAtkData.go,D5A2ECF1D186BF242E70EA48F63F3781,5220,1675761410204
c:/tmp/go/Game.SkillBuff.go,C477B24C65A9E283C9BA79591E1E9,1218,1675761410229
c:/tmp/go/Game.SkillBuffData.go,56E6A61D9DD9CC79D4A7FD5A249CEE1,5753,1681962385984
c:/tmp/go/Game.SkillEffectType.go,2A51476E33DC9A780DE21F2D5807D88,1827,1681962385979
c:/tmp/go/Game.SkillPassive.go,9B287E1A5BCDCAED93EEE9EA71B58CD,1263,1675761410253
c:/tmp/go/Game.SkillPassiveData.go,44EC745353A7734F7808DCFB455E31,3799,1675761410264
c:/tmp/go/Game.SmithyAtlas.go,5B5AABF2AA7BF212FD8CA97C766D7C4F,1252,1677038179629
c:/tmp/go/Game.SmithyAtlasData.go,BFC954D5A8835912E92D976CA6DE51,3158,1677059652972
c:/tmp/go/Game.SmithyAtlasLv.go,F86EA4795B59D25AB79703766A272D,1283,1677038179630
c:/tmp/go/Game.SmithyAtlasLvData.go,E85FA0129FDCC348EAE6785CC48DE2A2,2015,1677038179619
c:/tmp/go/Game.SmithyAtlasScore.go,12E094F46395632E8A6B5EF9C889ACC6,1323,1677038179615
c:/tmp/go/Game.SmithyAtlasScoreData.go,18BCB139BEB6192E3F17B4A5B78D7D2,1652,1677038179617
c:/tmp/go/Game.SmithyCustomer.go,55F61C14D9D7B551E449EC4390BC341E,1301,1676518692454
c:/tmp/go/Game.SmithyCustomerData.go,2EC2224D34934FB052C6837D666AF29B,3400,1676526348334
c:/tmp/go/Game.SmithyIdTool.go,5B551B697E9DCD4DE9BAE46317E77454,1263,1677226965191
c:/tmp/go/Game.SmithyIdToolData.go,B9C35177B62E1EEADF33C3D2451EFB3,3671,1678073991276
c:/tmp/go/Game.SmithyMake.go,C011169752EBE4B81BA5C8C0168F501F,1239,1679906281543
c:/tmp/go/Game.SmithyMakeData.go,4321F6CF81B013DC4CF08C48B76F12EA,1739,1679906281542
c:/tmp/go/Game.SmithyModelPool.go,CD4B1CBFE957EB8945C60B05E498BE0,1308,1677226965375
c:/tmp/go/Game.SmithyModelPoolData.go,6C86F150B054D0C81834F6BD757B383E,1496,1677226965192
c:/tmp/go/Game.SmithyProficiency.go,3F4E21F7E5A8FBAC83B1F16969569A7,1077,1677038179619
c:/tmp/go/Game.SmithyProficiencyData.go,C57C14E515189738F730CF85DFF0585E,3202,1677038179628
c:/tmp/go/Game.SmithyStoveV1.go,EDC5BCC4F9716514F3ACC7E444EC2C5,1280,1676518692454
c:/tmp/go/Game.SmithyStoveV1Data.go,9DE078FD641B767826D5D4D8769DD4,2739,1676518692454
c:/tmp/go/Game.SmithyTask.go,AEA846A9A27F5648A3A68B90D365D9E2,1234,1677038179623
c:/tmp/go/Game.SmithyTaskData.go,6FB0F235B355C28D1F818999A3E214C,2612,1677038179621
c:/tmp/go/Game.SmithyTool.go,BA1AB18ADBEC3ADF684EB9367BC54410,1000,1677038179623
c:/tmp/go/Game.SmithyToolData.go,91A1870553BA719F55B148B9732AB,3659,1678073991288
c:/tmp/go/Game.Talent.go,71C5E99251DF6A3E145E925FD21D6398,1173,1681962385984
c:/tmp/go/Game.TalentData.go,DA1E97255EFCA9E3090E748BE89C62E,4727,1681962385981
c:/tmp/go/Game.TalentSkill.go,F9BF1EF61E90EFA4C9DD1FC9ED975491,1253,1675761410235
c:/tmp/go/Game.TalentSkillData.go,4C4BB723ED2FC6F382A6C7737146C80,3226,1675761410222
c:/tmp/go/Game.TaskActiveReward.go,97E47A5C9F7CB0E61CB7CD9A37BE72D1,1324,1675761410250
c:/tmp/go/Game.TaskActiveRewardData.go,7A3E6C5FBAF21AA96B9FBE9757C618,2512,1682218335278
c:/tmp/go/Game.TaskRound.go,D020EA2F956FF3BDB642ED35F282E45,1219,1675761410226
c:/tmp/go/Game.TaskRoundData.go,CEB761591786919F1CA8AB01901D,3531,1675761410273
c:/tmp/go/Game.Teaching.go,7F2ECF64117D5123826E88EDEC61F689,1203,1675761410268
c:/tmp/go/Game.TeachingData.go,EFBBFBCC922ED6C1B37A86CEA5C94F25,6715,1680513216868
c:/tmp/go/Game.TrollCoefficient.go,5380DB70872B18DB63C738CE5924644E,1323,1675761410268
c:/tmp/go/Game.TrollCoefficientData.go,99F5FA24C3E1DC5089F6671D9113E1D9,1306,1675761410207
c:/tmp/go/Game.TrollGoods.go,145AB09F76C13B7DABCFA9E9C329FBD7,1233,1675761410207
c:/tmp/go/Game.TrollGoodsData.go,EB8C912D59BD09BDD43A12E209EA62,3777,1676518692455
c:/tmp/go/Game.TrollLv.go,7FA827993355F49AD852BA798351EFC,1188,1675761410232
c:/tmp/go/Game.TrollLvData.go,3F116F498DBDB84A3BBB417BE045C5,2499,1675761410221
c:/tmp/go/Game.TrollRule.go,512BD4EDA3F94C1A3CD2FE715CF5D17,1218,1675761410233
c:/tmp/go/Game.TrollRuleData.go,059E5EC96C4953FFCD1864CEB8C7C,1243,1675761410268
c:/tmp/go/Game.TrollTrain.go,C2D8F9A1FBDB2687545A7BCCE1CB91A,1233,1675761410256
c:/tmp/go/Game.TrollTrainData.go,2572D0CF6615D369BCED75549AC6971,1648,1675761410196
c:/tmp/go/Game.VikingBoss.go,E7AB8CB8AA2B84546C5123AC0A1FE56,1000,1675761410225
c:/tmp/go/Game.VikingBossData.go,EF135139EE5F6F6F7FB3B5FC1EF1706E,7198,1675761410237
c:/tmp/go/Game.VikingBossSkill.go,951D4526F1116AF36D673D1BEB482CA9,1312,1675761410237
c:/tmp/go/Game.VikingBossSkillData.go,374832A711689D3D3C2CC58FDE5968,2964,1675761410259
c:/tmp/go/Game.VikingChallenge.go,BC5F1D76EEE9C6B17ADFCD3DBD7CE91,1312,1675761410199
c:/tmp/go/Game.VikingChallengeData.go,CB693365C1D132E8141F14A8B832F8CB,1800,1675761410260
c:/tmp/go/Game.VikingEntrance.go,328C6C2D94321A7D7991385F4B69EA,1297,1675761410272
c:/tmp/go/Game.VikingEntranceData.go,B237211BA19D1377959288D2A3EA8BA3,2671,1675761410248
c:/tmp/go/Game.VikingLineup.go,2D98B23891EDCF72C92196D869DAD8FC,1263,1675761410211
c:/tmp/go/Game.VikingLineupData.go,F3CF9119717CFF771457DE441F244B,1654,1675761410224
c:/tmp/go/Game.Vip.go,102F7FD577B9CE145EB64136276FDB,1131,1675761410230
c:/tmp/go/Game.VipData.go,211F15FBA462EDBF8D9D15982FA47C0,4643,1675761410255
c:/tmp/go/Game.WorldAll.go,9AF391CD98756EB4C74834447E31A,1203,1680055388974
c:/tmp/go/Game.WorldAllData.go,2E35E4903FCFB4E5FEC7F3AC1825BEF,2768,1680055389013
c:/tmp/go/Game.WorldBattle.go,9CE09F1EF199BFF5A6AF63235780703C,1248,1675761410255
c:/tmp/go/Game.WorldBattleData.go,2E1F5F0FB60E811E14C9AAB5A68AE27,2825,1675761410205
c:/tmp/go/Game.WorldTask.go,434D20C017E6DFFBCCA08EBC4754594,1219,1675761410219
c:/tmp/go/Game.WorldTaskData.go,A5F9A7F71EB52893B5A4CFADA99FB7,7131,1681962385978
c:/tmp/go/Tables.go,B971E075C0D6E797733C169D1733BA,42164,1682218335290
c:/tmp/json/game_activereward.json,D3EED68F53E4C4B4365DE3B2157AC33E,841,1682307537195
c:/tmp/json/game_arenaactivereward.json,BF1CCCAC8E9C4484952427885249F8B9,3332,1675761410715
c:/tmp/json/game_arenabuychallenge.json,99244E8D8265F317C1791A8096BCF7B8,1308,1675761410715
c:/tmp/json/game_arenachallengenpc.json,DAE11F4AB0754268A2FAEF5936B54AA,8391,1679296385065
c:/tmp/json/game_arenarankreward.json,43CB1836B7DB877BDE9436FAB2578C53,2883,1675761410716
c:/tmp/json/game_arenarobot.json,32FBEC8F41611E6C8C852174FB7DB1,1530,1675761410715
c:/tmp/json/game_battleready_capskill.json,EA775AAAFEDB8471A3C94E89938983,219,1677038179630
c:/tmp/json/game_battleready.json,587110213BB78332A821AAED1D63DD81,7666,1681962386033
c:/tmp/json/game_battletasktesting.json,5F772617E8A89921C3CFFB1EF3943E60,81,1675761410764
c:/tmp/json/game_breakingbad.json,FFC05F7A6F323B74A81A42F5DAD1E2,2740,1681962386032
c:/tmp/json/game_breakingbadtag.json,19871E5F86D8C3B9DF41F2AE78F39099,217,1680144063147
c:/tmp/json/game_bubblegroup.json,85696680D892AC8BC4A0AF1F72745F44,6674,1681962386040
c:/tmp/json/game_buzkashifm.json,EC7F2F581EE6BA14CC7A64E43E122B5,1919,1682218335507
c:/tmp/json/game_buzkashigrade.json,D47715ABEC98A3D08355929C759D76C9,259,1682218335505
c:/tmp/json/game_buzkashilv.json,3A904DE2A9123159792B15369C342C,380,1682057107480
c:/tmp/json/game_buzkashimount.json,5888FC4FD7D9C78CCABDC97CE1F5BDB4,1155,1682318037909
c:/tmp/json/game_buzkashiopen.json,BEB2D3F76A1A55A63094FA4E75EAF4C5,2422,1682218335506
c:/tmp/json/game_buzkashireward.json,9DF6F6EDED492A97DADB9ECBBE0229,931,1682057107479
c:/tmp/json/game_camplv.json,EA5BD2C3B5146949F5F08E51E476A610,14406,1682057107479
c:/tmp/json/game_chatchannel.json,1F87E9D83A3FC71A26AB2A8F14A0DA,428,1675761410386
c:/tmp/json/game_chatchannelcom.json,58D258F42C2A1911CE87A5BE80BB4A,695,1675761410386
c:/tmp/json/game_chatexpression.json,B432A821EFFBED64D729B541AA3F8A1,1527,1675761410385
c:/tmp/json/game_chatservergroup.json,23D3A6BD53D0AC8AE14B408EEB8A94D1,954,1675761410386
c:/tmp/json/game_chatsystem.json,80EBFB9522C4DBB05EE29ED2A5F34476,8010,1675761410386
c:/tmp/json/game_coin.json,A9A9AE5A4EC5474B8B4247F5D7B93D1,4720,1681972759519
c:/tmp/json/game_combatbox.json,8A3AB7F11E9B9C769A54AA8638BB99,1084,1678677256017
c:/tmp/json/game_combatlevel.json,D7406EB1BA1329D9992A34DBBD72EE1,4920,1678677256017
c:/tmp/json/game_combatmanster.json,6E581C2DA6262A536F085E3916AEEE7,5851,1675761410751
c:/tmp/json/game_dispatch_battle.json,8B44C83A9F7A4E662EB4C20797B47AF,1591,1681962386032
c:/tmp/json/game_dispatch_lv.json,34A2726CDC3BF7DCFCE3B7D6C773767,579,1680144063145
c:/tmp/json/game_dispatch_task.json,36A8854A8812B5F96E0115E8577AD,61669,1682218335498
c:/tmp/json/game_drawcard.json,66D94B48C4B37CACBE7740F771CA7A,19860,1681962386238
c:/tmp/json/game_dreamlandboos.json,1888542E62918AE029C779C6FB6B4F44,1036,1675761410629
c:/tmp/json/game_dreamlandchallenge.json,4335C14D448FA128DB60BB96197CBC1D,1335,1675761410611
c:/tmp/json/game_dreamlandtrigger.json,28F884577B6D1A1956E322C6CC78B742,358,1675761410629
c:/tmp/json/game_drop.json,7527B859F8AD5C1E666CF38E41EEA9,49284,1677661885209
c:/tmp/json/game_enchantboss.json,15B0447F42A1ACA9918F5FD26F9FB9,38976,1675761410758
c:/tmp/json/game_enchantlineup.json,EF9670D72E9BBAA16B1D8C1807A3D1C,3132,1675761410759
c:/tmp/json/game_enchantshop.json,4335C14D448FA128DB60BB96197CBC1D,1335,1675761410755
c:/tmp/json/game_equip.json,8EBBC6B2589ADEC7CAF501A9BE6229,540254,1680333520554
c:/tmp/json/game_equipattribute.json,41E55A8C70C1D823E477A72A3C5CF12,17487,1675761410677
c:/tmp/json/game_equipattrlibrary.json,7AA7643ADD129C81BB8CD7D6688D349,147142,1680333520466
c:/tmp/json/game_equipcolor.json,23E75AC519FB233B146AAD1F873817DF,513,1675761410679
c:/tmp/json/game_equipenchanting.json,E498711A7E962CA6F19BB3F3F5224F,4302,1675761410625
c:/tmp/json/game_equipintensify.json,5F667FFE282E3429CF353FCC58246A15,79062,1675761410488
c:/tmp/json/game_equippos.json,AF6858A6A6DFDBF3254186A81BFD7E8,2029,1680333520453
c:/tmp/json/game_equipscompose.json,EC69D955CBCE89EB317A461952521385,25080,1675761410679
c:/tmp/json/game_equipsuit.json,67A562B61E407C9B64CF9447DE1BCCA,7435,1680333520554
c:/tmp/json/game_extservers.json,3646EC79C54DD7CAAC569C7F963BB0,1068,1676518692477
c:/tmp/json/game_facemod.json,2BBA2B6F9A82AB2EC8844559D3BB961,6593,1675761410656
c:/tmp/json/game_favorability.json,4E3EFA49AA4D8D14760B0F2F7ED3639,67752,1682218335505
c:/tmp/json/game_fightglobalevent.json,E837C59BA038C4215526E661918BACC,12340,1675761410750
c:/tmp/json/game_friends.json,BDF1452119B56592733A6357F28E9BC,8725,1682218335499
c:/tmp/json/game_getresource.json,C46E1E2C5D426C4ECA4A894552BE9444,653,1675761410348
c:/tmp/json/game_global.json,1066827E2E81DEB540959BEE511E196E,14307,1682307537246
c:/tmp/json/game_gm.json,FB48EFD74530879D2A296651ACB1AB32,5966,1681962386232
c:/tmp/json/game_gourmet.json,CC74365D5C8D55A4D7872321E5FB6065,55949,1675761410364
c:/tmp/json/game_growreward.json,4A588FDE3912B77387372D9EDB6562,965,1675761410741
c:/tmp/json/game_growtask.json,AF7BAC9838F83F887A4CE3F4092F4E6,8061,1675761410741
c:/tmp/json/game_guide.json,D751713988987E933198363E24189CE,2,1680164540236
c:/tmp/json/game_guildactivity.json,FBC9A5965931DFB7BEB5CF3D98AC514,774,1675761410718
c:/tmp/json/game_guildbosstask.json,49AC51648FDDA5E7A04154456B693F71,2585,1675761410719
c:/tmp/json/game_guildlv.json,854216455BDAF1C4B35A728B9AA2A,1321,1675761410718
c:/tmp/json/game_guildpng.json,34F523EBCD9D3702C9A968D961720BB,588,1675761410718
c:/tmp/json/game_guildsign.json,5AC221945DD8EBF17E4D396E8673AED1,2100,1675761410718
c:/tmp/json/game_guildtask.json,C2CF857203DAEB0F7202853A21363F6,4875,1675761410718
c:/tmp/json/game_help.json,EA598AF8FDAC88C32EFF99A04881E,460,1675761410752
c:/tmp/json/game_hero.json,D3709DAFE361BDA74EC1AAA3CFD78D,159832,1681962386232
c:/tmp/json/game_heroawaken.json,71519DD7974DA3B322F4781EC3DBB37F,476483,1675761410481
c:/tmp/json/game_heroconvertdata.json,9A577728E5D9D9B68EABF977F9241CB,187,1675761410333
c:/tmp/json/game_heroexp.json,EE1C209B4B92591F664BBDE369B2F45D,235,1680144063077
c:/tmp/json/game_herofusion.json,F93DD49FB7D6DD11699E91AC9BFD4,1488,1675761410331
c:/tmp/json/game_herolevelgrow.json,D5E55EB88D3D3EEDA9ED79174B784C3,27074,1675761410714
c:/tmp/json/game_herolevelup.json,866D2340E76750343B7E18F33271BD,6148,1675761410624
c:/tmp/json/game_heromethod.json,1D6FA854ECD8ECA89452D227F29A8C,22084,1675761410441
c:/tmp/json/game_heromethodcomment.json,306BB7AFCD3082E32392A6CEC3874D53,1281,1675761410440
c:/tmp/json/game_heroskill.json,4A88CE65A0BF37E0ADD37CD840438252,813,1675913368405
c:/tmp/json/game_heroskilllevel.json,1B35D2B4791992C3C948D037A438B1EC,30888,1675761410624
c:/tmp/json/game_herostargrow.json,A79CA6CC1FDB28663861644CEF16091,950,1675761410714
c:/tmp/json/game_herostarup.json,F177296407A285B47DD5EA0BC2CD384,68269,1678677255844
c:/tmp/json/game_herostrategy.json,6745F13142D4532DE6F7BF8DB73BF7,15046,1680513216867
c:/tmp/json/game_herotalent.json,329FDEDC9B9F228ECF3412C4619A6E,7092,1675761410331
c:/tmp/json/game_horoscope.json,81D7D6BAC274963F6D4315F0CBA9ED3D,238307,1675761410739
c:/tmp/json/game_huntingboss.json,DC33AE6BD9CEA106F3DB2DBFC3C40B8,32411,1682218335416
c:/tmp/json/game_huntingchallenge.json,4335C14D448FA128DB60BB96197CBC1D,1335,1675761410611
c:/tmp/json/game_huntinglineup.json,6356433D4FCB4ECE85D25E797D74B5C3,3567,1675761410348
c:/tmp/json/game_huntingopentime.json,132066502E243CFA89E0447788188E1,1821,1675761410611
c:/tmp/json/game_initial.json,498FD44D5B84BB2CEC8CD5853740E56F,21232,1682057107478
c:/tmp/json/game_item.json,DE86B3FEF01617FB98CBFF5F7F321CC,164623,1682307537217
c:/tmp/json/game_itembox.json,F8D458C8CC5E3EB58B35D11E6259EF6E,4192,1675761410717
c:/tmp/json/game_itinerant_city.json,76D06B4496E4FD8E0D7B85237141A5,3332,1681962386029
c:/tmp/json/game_itinerant_lv.json,A42A4E26426C85A00EDEDA4C2B53F,1974,1681962386030
c:/tmp/json/game_itinerant_reward.json,467883995F3C3CF65ABC5E6D6D12FA8,408,1681962386038
c:/tmp/json/game_itinerant_thing.json,B1F162027D1C14CA736F1EFBF310E8,3849,1681962386039
c:/tmp/json/game_librarybubble.json,8154AF3EED738D65113A8E356B11697A,384,1675761410337
c:/tmp/json/game_librarycomplot.json,5B941EF48E4C3D457D926BFA5B2B686,3792,1675761410337
c:/tmp/json/game_libraryfavor.json,EB684CA2609C631516D18BA121C4A97,1662,1675761410337
c:/tmp/json/game_libraryfavorlv.json,8DEEBD3AF3166ED6E5C5837DC2F94BB8,441,1675761410337
c:/tmp/json/game_libraryfetter.json,BAF8A2E4B7887BBD46C485D8AC674,3221,1675761410347
c:/tmp/json/game_libraryhero.json,D3C53DB0D9EA31DBF512FEC9871F618,19393,1675761410347
c:/tmp/json/game_libraryhistory.json,70AAA0D3E0E032D1443AD8E8A5D4DC7,92338,1675761410345
c:/tmp/json/game_libraryplayback.json,94C2236AE30C59D353190301725B6,1159,1675761410337
c:/tmp/json/game_librarystory.json,80B39EF0EBA748851BFD25E320A7B0,477,1675761410347
c:/tmp/json/game_linestorymaintask.json,39D1B19C8B1D2E4773AF38FF2C9FEC12,3329,1682318037448
c:/tmp/json/game_linestorytimeline.json,C66A644E3F77E141A9DFCF4745536EAC,3264,1682318037453
c:/tmp/json/game_lottery.json,75BB73436CA0187AE923BB379C480A9,13722,1681962386039
c:/tmp/json/game_mail.json,696DC699EB6CE88CEED681E42F90759B,5946,1678271315132
c:/tmp/json/game_mainchapter.json,F4352CC5AFD2764A0B390D1F9F41B,14884,1678271315126
c:/tmp/json/game_mainstage.json,EFA1D92EDC3B10979A734C1642BDFE0,37978,1681972759518
c:/tmp/json/game_mainstarreward.json,4625A9BDCA1B4D3A1BE3A3394D1ED38,2070,1675761410764
c:/tmp/json/game_mainstartype.json,DFA5E7D4B19CDC5D642A8044361A8B,372,1675761410764
c:/tmp/json/game_model.json,A64F6A18C7E08F4134CC8A1050D53FA9,76525,1682218335477
c:/tmp/json/game_monster.json,FCCB6CF9743257A813B841D742C36C0,187449,1678677255914
c:/tmp/json/game_monsterformat.json,2C5EE5F6D01147AC2BA535FB1A1471D,1517045,1682218335416
c:/tmp/json/game_msgdistrib.json,16C3102AC7C04E448CFAB7C44D8FAC1,2604,1681972759486
c:/tmp/json/game_namelibrary.json,40EE974E25A19B35376F1F91DC1B2545,23511,1675761410334
c:/tmp/json/game_navigation.json,17C79B5C488D03F4448B3672FBE9C6,8233,1681972759519
c:/tmp/json/game_newreddot.json,8E38D0A5D2F418D109AEEE6AFD342F3,6376,1681962386019
c:/tmp/json/game_newsmithy.json,16F56B8C947046F74B4C26A5F397166,71058,1679909505402
c:/tmp/json/game_opencond.json,4C5F1E1E948E5225EBB39F9D21ABA54,30507,1682218335508
c:/tmp/json/game_pagoda.json,87BDDC4C718F59668B8DA177F46C97E,61205,1681962386215
c:/tmp/json/game_pagodataskreward.json,E17D1136C63FA8D6C76A2156CD3D98,5033,1675761410655
c:/tmp/json/game_pandamasbuff.json,76578B68014C5E6E68ACDB9E34D2391,1812,1677226965402
c:/tmp/json/game_pandamasjs.json,666DA3906D124EEBCF28BAEC1BC82C6A,663,1680333520573
c:/tmp/json/game_pandamasjx.json,A91343344FE7375FDCD1925EB11C718,26058,1681962386234
c:/tmp/json/game_pandamasmryl.json,1D2FA4CD5CFE8FC162672643FA203B,1096,1682218335498
c:/tmp/json/game_pandamasmz.json,3423EF4664A478E1FBCE8E72FB64C9,2676,1680144063145
c:/tmp/json/game_pandamastj.json,8551DDDCE76239FC5D9319A302B29E7,15121,1681962386044
c:/tmp/json/game_pandamastjjl.json,FC5542F0EC16DED5B9B49446601712BC,2583,1681962386031
c:/tmp/json/game_pandamasyxjx.json,DD56E58FF48A953B7A8552ED3D73DB,1859,1679651030030
c:/tmp/json/game_passcheck.json,EC66DF4C4EDDDAC32A16532C7F3E53FA,3782,1675761410751
c:/tmp/json/game_paypackage.json,44618751217D9A12A93B56BBC3DD5BD,4952,1675761410750
c:/tmp/json/game_playerinfor.json,8FC0A9B93292A9C7585ADD4ADAEE836,668,1678677255831
c:/tmp/json/game_playerlv.json,80F2E314FA9E211248E289199A43D0E3,12102,1675761410657
c:/tmp/json/game_potions.json,BC9A83E9E8A533A95FEA4CF018A57E0,16179,1681962386031
c:/tmp/json/game_privilege.json,408075AFCCD9D1AACAB0323EC4F19624,33615,1676518692472
c:/tmp/json/game_privilegecard.json,2128DDB5E7B8CB24770735FD42FF195,1059,1675761410742
c:/tmp/json/game_rdtaskcondi.json,62A645F56868294B7426C923A26C58B1,90980,1682307537206
c:/tmp/json/game_rdtasknpc.json,BF30AC2A685C4CC9F10ACD2736868,7382,1682307537148
c:/tmp/json/game_recharge.json,D0D3904BF129EA74B7A7F27671FFFFA4,11867,1675761410742
c:/tmp/json/game_reddot.json,55DF85673AF2532D1492D124AB83B26,1431,1675761410750
c:/tmp/json/game_reddotgroup.json,356CDD66A5F22D349128653A45713,3999,1675761410751
c:/tmp/json/game_reddottalkid.json,1BAC1E7B94AD24A6C48170811983DE98,1620,1675761410751
c:/tmp/json/game_robot.json,3AE3B94AE169EA4113C089DC4FEF228,36683,1675761410337
c:/tmp/json/game_sellcoefficient.json,64D33D5CCBFE5016BEE386D7EC334BE6,288,1680179912086
c:/tmp/json/game_shop.json,EE68944972702EB5167AD131B8372D3,3900,1679296385073
c:/tmp/json/game_shopitem.json,1668E6C4BA6B1B87214197FD3DE4E16,116423,1679296385064
c:/tmp/json/game_show.json,65A4DF4CF97E22A3BF627EBFDC5B722A,1629,1681962386048
c:/tmp/json/game_sign.json,D018C2FAF498486E33DE1898BEC246F,25295,1680256283923
c:/tmp/json/game_signextra.json,75BB4F433524A45628F61282388EE5AD,2295,1680576438283
c:/tmp/json/game_signreset.json,B458B081A4CB562EC14CC3B4374182C,129,1675761410740
c:/tmp/json/game_skillafteratk.json,304A9C9CEEAD595D6F818646274FFF9,515714,1682307537190
c:/tmp/json/game_skillatk.json,705ADAEFBE2F0B2C8E9DD4A5BC199FB,328331,1682307537244
c:/tmp/json/game_skillbuff.json,F1439BBE20CC5027E4E3A48D222AF,77138,1682218335520
c:/tmp/json/game_skillpassive.json,4F36F139285B29E968F324695DB52C0,48500,1681972759568
c:/tmp/json/game_smithyatlas.json,53FD14E6C8EA11417227C3EA8965F6A,52402,1682218335520
c:/tmp/json/game_smithyatlaslv.json,B5F2DC491CC7118EA2332FF129881B80,2609,1680179912086
c:/tmp/json/game_smithyatlasscore.json,C9D57714FCA1018923141254BAC858,5778,1677038179702
c:/tmp/json/game_smithycustomer.json,4033DFD1B47CA49DB811FFFFBB531E2,1515,1681126805545
c:/tmp/json/game_smithyidtool.json,B98A8198519DFBCF82E73D8999F4AEDE,26469,1678157698235
c:/tmp/json/game_smithymake.json,51109DA3B839B5C521CC453D3C152A6,470,1679906281635
c:/tmp/json/game_smithymodelpool.json,6957C0A652F9524EA0E54797692B5056,147,1678073991461
c:/tmp/json/game_smithyproficiency.json,BA7B4894161C7734FDC99548F9F387B4,144639,1681198061227
c:/tmp/json/game_smithystovev1.json,77F6697AE5D6FE52030147B359851BD,3077,1681962386039
c:/tmp/json/game_smithytask.json,63B5C31C1AEA89EBD14D9C1BF111C5AD,5109,1677059652991
c:/tmp/json/game_smithytool.json,B98A8198519DFBCF82E73D8999F4AEDE,26469,1678157698232
c:/tmp/json/game_talent.json,F641206D8EEBB6B46B76B2C8DC1FB2AF,16694,1681962386037
c:/tmp/json/game_talentskill.json,8B9AE9E68EBFA10D0B750FD15F02C57,8991,1675761410715
c:/tmp/json/game_taskactivereward.json,D3EED68F53E4C4B4365DE3B2157AC33E,841,1682307537206
c:/tmp/json/game_taskround.json,9D2EE2F9BB90C79CDF713C94E57B5,3181,1682307537246
c:/tmp/json/game_teaching.json,344348DE32EA83B05CADD5E36F2E5C59,15247,1680513216864
c:/tmp/json/game_trollcoefficient.json,68CA578584F7C99B68FD9DBD2173F231,600,1675761410719
c:/tmp/json/game_trollgoods.json,EBFCEB3737B0934D1CBD4934DDCA7AF0,6838,1676518692458
c:/tmp/json/game_trolllv.json,7E33F3EEF3F139E0D276266448F3A52,1821,1675761410719
c:/tmp/json/game_trollrule.json,ED2DAD2E962D3F4F5AC09081A75AFD,366,1675761410719
c:/tmp/json/game_trolltrain.json,ADBA2BFEB89584A557A5ADCDB4EC334C,453,1675761410719
c:/tmp/json/game_vikingboss.json,DB9954617885C974EC1F0AA23DCE676,67985,1682218335420
c:/tmp/json/game_vikingbossskill.json,89C2DC9A201BBEA862C47636D29FCFA4,2382,1679296385065
c:/tmp/json/game_vikingchallenge.json,4335C14D448FA128DB60BB96197CBC1D,1335,1675761410629
c:/tmp/json/game_vikingentrance.json,5E40BF66B0F71B467B90D343F4991B,1124,1675761410612
c:/tmp/json/game_vikinglineup.json,16A46E7023443832A8E258764C3083AD,4478,1675761410351
c:/tmp/json/game_vip.json,78A863FC9926A02C9DEFD287102660,11859,1675761410755
c:/tmp/json/game_worldall.json,FDE8BB284D535058DCAF3D30DC2C65C0,3426,1682218335506
c:/tmp/json/game_worldbattle.json,43376C1582FA73922680BFBEB5314D6,2803,1680333520555
c:/tmp/json/game_worldtask.json,A34A24C84F05C5826884E8B785FC2F7,24977,1682218335291
F:/ExcelFile/Datas/__beans__.xlsx,D4BDC647431515D16B26347A8B898BAA,9417,1677038118101
F:/ExcelFile/Datas/__enums__.xlsx,0E576421F2DC8B1F27CEEA3C0D2EDF9,18250,1681788582819
F:/ExcelFile/Datas/__tables__.xlsx,B508BB5CBB19A68B0A6437062197F85,29114,1682303788395
F:/ExcelFile/Datas/arena.xlsx,A971D35BFAA9E237658379B3DBBFD7E,23334,1679019552258
F:/ExcelFile/Datas/battleready.xlsx,3B81C940FC86BD9FD74814608846D091,16352,1681380626795
F:/ExcelFile/Datas/battletask_testing.xlsx,8DE417253683AFE8B4B9FEF829997F,9526,1677038117946
F:/ExcelFile/Datas/breakingbad.xlsx,7CEA43D82E9CBDB8DAF8CB9EA2D7852,14672,1681380626787
F:/ExcelFile/Datas/bubblegroup.xlsx,9DD28365735F480177E3F9616ECEFD,13536,1681903841630
F:/ExcelFile/Datas/buzkashi.xlsx,96E69CA17FC147D6DB8E23940AFB32B,27104,1682317131873
F:/ExcelFile/Datas/cardtalent.xlsx,126CF16B301DCD75264FAD6B12F48E,22408,1677038117896
F:/ExcelFile/Datas/chat.xlsx,D66E7BC8566A2468C12F3B7733F64,20948,1677038118000
F:/ExcelFile/Datas/com.xlsx,EE34D5438A70A5329324BC01CBAF18,29525,1682307439015
F:/ExcelFile/Datas/combat.xlsx,CE70D5448F3B6B3E6CB3E6C761F6827,18533,1678676490311
F:/ExcelFile/Datas/drawcard.xlsx,AB35A77042330EC1A14EAB7A9E28B6F,16729,1681788588866
F:/ExcelFile/Datas/dreamland.xlsx,40C56DCB3D4FD3ECAFFD3E8872193E6,13781,1677038117902
F:/ExcelFile/Datas/drop.xlsx,608CDC2BE7F5865C2793FD8F72BA1C4,21617,1677661861014
F:/ExcelFile/Datas/enchantboss.xlsx,8E87EEDEF557483587854588BA3238FA,21876,1677038117903
F:/ExcelFile/Datas/equip.xlsx,35C2E3B648AAE06A3611FC1C65BBD46,170894,1680333504643
F:/ExcelFile/Datas/extservers.xlsx,C7E29E9F823E9A3923B9AE8B33C09136,12436,1677038117894
F:/ExcelFile/Datas/facemod.xlsx,BBDEC392FF72E37D6DCAAAC5B35F8A2,10903,1677038118103
F:/ExcelFile/Datas/favorability.xlsx,A3FB4AEA4B5C7E20212E6FF37D25B9F3,36061,1682214557100
F:/ExcelFile/Datas/fightevent.xlsx,83E41BCD5E8A81D86CB749176188E,15642,1677038117914
F:/ExcelFile/Datas/getresource.xlsx,D9F0FB7515464729A313A3485271A9,10963,1677038118091
F:/ExcelFile/Datas/GM.xlsx,292F51BEF6314DF4C794E367CF7939D,13106,1681903841561
F:/ExcelFile/Datas/gourmet.xlsx,3DC05C0642437F7F94C26CADE35E5E,19282,1677038118025
F:/ExcelFile/Datas/growthtask.xlsx,39F2BA8B3DA4F14C66B50131924A19,16705,1677038117911
F:/ExcelFile/Datas/guide.xlsx,CB2C2A7F9156BEFEEFFC0ACDBD3CE,191261,1680157040368
F:/ExcelFile/Datas/guild.xlsx,D56B29EBF6811F4615E56C908E4D85,17769,1677038117948
F:/ExcelFile/Datas/guildboss.xlsx,D84314DFB6E69D93CB68F35292A6BBA,59077,1677038117940
F:/ExcelFile/Datas/help_information.xlsx,5E641942C5D9BE65334AFE37CB97E7,9438,1677038118061
F:/ExcelFile/Datas/hero_awaken.xlsx,17241886F99CA6D9C2DE6CBD8535EE8,66257,1677038118043
F:/ExcelFile/Datas/hero_method.xlsx,50AEF20FC5EB9DF8A822632FE39979,15330,1677038118002
F:/ExcelFile/Datas/hero.xlsx,B0E9375A5937E64AB754AF175CD8B85E,96091,1681788588901
F:/ExcelFile/Datas/herofusion.xlsx,5A4C6E879DFAA6B3AD8AFD26B0B65BA7,12628,1677038118049
F:/ExcelFile/Datas/horoscope.xlsx,9560CD4D77B1BD959A6460837913BF45,47327,1677038118035
F:/ExcelFile/Datas/hunting.xlsx,4643CAC69D2E962FB3F3BAF5B0B1CCF0,21253,1682068376991
F:/ExcelFile/Datas/item.xlsx,F81D226679D35BFDFB4818175B1E63A,55010,1682307439025
F:/ExcelFile/Datas/itinerant_trader.xlsx,4B3E519775EED285CD2C882EAEBD,22379,1681380626777
F:/ExcelFile/Datas/jump.xlsx,44C6C4C582EC22B979DB9BD934AC9D3,22284,1682214557119
F:/ExcelFile/Datas/library.xlsx,4DAB219F3139FF5A94D1ACA845B7E66,75318,1677038118003
F:/ExcelFile/Datas/linestory.xlsx,8B252F3D12CEEEA7FFA57735E26986,16205,1682317127037
F:/ExcelFile/Datas/Localize.csv,61639A33195FC94FABF595F3BD822F,873133,1682303788392
F:/ExcelFile/Datas/Lottery.xlsx,A426E6D87B67F955768D79918E5C25C,13931,1681380626798
F:/ExcelFile/Datas/mail.xlsx,22DBD9DFE47AD018BC981A1FCE23D6,12780,1678271305434
F:/ExcelFile/Datas/mainchapter.xlsx,FDD3A1CFE335DA11B9516DED3CA41C,28048,1681962396011
F:/ExcelFile/Datas/material.xlsx,1B4452105322F2851B79F8A589B13D1,14554,1681788588657
F:/ExcelFile/Datas/model.xlsx,E5FB152AF7EA88EC88278E7B985C321,32122,1682214557104
F:/ExcelFile/Datas/monster.xlsx,26A9EEF5D3319C7E77DA58320C2D477,51115,1678676490314
F:/ExcelFile/Datas/monsterformat.xlsx,39C7267D148F9882A4AAAA112CC946,333861,1682068376989
F:/ExcelFile/Datas/msgdistrib.xlsx,EF1B57E58F1C495F531EA37428622,10747,1681962396033
F:/ExcelFile/Datas/name.xlsx,A88D302B5F4EFDD8506F867673722965,16331,1677038118083
F:/ExcelFile/Datas/navigation.xlsx,E4EDBE2E5D78429D8D53B7C27778A94,10762,1681962396025
F:/ExcelFile/Datas/newsmithy.xlsx,7EF071F9FBF8A14EAA780ED2BC019E8,93682,1682068377027
F:/ExcelFile/Datas/opencond.xlsx,DF197291CADAEEEF25D7C068F6132964,23293,1682214557117
F:/ExcelFile/Datas/pandamas.xlsx,383DF17FD28ED4650B1C94EAB6AC4ED,63321,1682214557102
F:/ExcelFile/Datas/passcheck.xlsx,4D1386149C340606EE1FB55A018267B,11975,1677038118024
F:/ExcelFile/Datas/paypackage.xlsx,4F18D9D87B17217FF83C7ACBF17267EE,12232,1677038118066
F:/ExcelFile/Datas/playerinfor.xlsx,181D7352304826D16F43633134C12AC3,12730,1678676490294
F:/ExcelFile/Datas/playerlv.xlsx,E975DF92CF2E69D68F82529E7780,12099,1677038117951
F:/ExcelFile/Datas/privilegecard.xlsx,5935A3E0A03F2FA15D245F407C8E3CA5,18057,1677038117888
F:/ExcelFile/Datas/recharge.xlsx,698CA16B364BB252579F26FDCA82F8B3,13582,1677038118050
F:/ExcelFile/Datas/reddot.xlsx,63127E7F948ECDA8E33D9D38BCA7A4,26327,1681788588661
F:/ExcelFile/Datas/robot.xlsx,1615F3DB6FC51B61711110A3CFE06F5F,20979,1677038118098
F:/ExcelFile/Datas/shop.xlsx,D1387DE5C698B6B634D148AEB8E9C4,44737,1679019552256
F:/ExcelFile/Datas/show.xlsx,2B3B56FAB738CE6F3A86A0A5291E53B0,14266,1681788588658
F:/ExcelFile/Datas/sign.xlsx,D8AFDD1D85A33929EA4C473355D6BD,20011,1680576410799
F:/ExcelFile/Datas/skill.xlsx,1E85246915772B48DFECF16D2B5822,235430,1682307439024
F:/ExcelFile/Datas/task.xlsx,5348EE0BFE6BE40660944690CDDDA,17151,1682303788398
F:/ExcelFile/Datas/taskcond.xlsx,6BB5303F22FE748B3476388F532656A0,131959,1682303788407
F:/ExcelFile/Datas/teaching.xlsx,FEE01F38DBFDC6FC926469C898D546D,20921,1680511806575
F:/ExcelFile/Datas/trolltrain.xlsx,421BDCBB1DBC58C13A639CD69499463F,25782,1677038117892
F:/ExcelFile/Datas/ui.xlsx,C8FF882D450B126913D7E8AA59869F6,38692,1682307439018
F:/ExcelFile/Datas/vikingexpedition.xlsx,101469EDC4A8C2E54A98ACAC329F471,31312,1682068376993
F:/ExcelFile/Datas/vip.xlsx,D195DACDCEFF7989D716F27CDFF265,11585,1677038117949
F:/ExcelFile/Datas/worldtask.xlsx,26498DFACA9EA6B2488014C08664A9B4,273076,1682303788405
F:/ExcelFile/Defines/enum.xml,BDB90FEDFC1ECF9669D632BC75EB871,660,1677225663180
F:/ExcelFile/Defines/format.xml,7E89E7E9857E207BA482E9A078B98012,1634,1682307526410
F:/ExcelFile/Defines/LocalizeParam.xml,5577C9C3A9218B6B26E1D78F526C05E,425,1677038122208
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/__beans__.xlsx,D4BDC647431515D16B26347A8B898BAA,9417,1660808890718
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/__enums__.xlsx,309DCDBBFF11E490D1707F285F63E0F8,18121,1671613786463
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/arena.xlsx,BAFB5448ECA3AF1573E62EFF7462F41,22207,1675669257601
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/battleready.xlsx,ABCC4EC351DF5B2F773D4FA55EDBAD,12361,1675993782723
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/battletask_testing.xlsx,8DE417253683AFE8B4B9FEF829997F,9526,1673334891747
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/cardtalent.xlsx,126CF16B301DCD75264FAD6B12F48E,22408,1675913355296
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/chat.xlsx,D66E7BC8566A2468C12F3B7733F64,20948,1672277335433
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/combat.xlsx,BB13F148B01C8274F29D4A39BEC5822,18173,1675669257605
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/dreamland.xlsx,40C56DCB3D4FD3ECAFFD3E8872193E6,13781,1675669257615
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/drop.xlsx,7CBB387AE913F4EBDC4CC12C9E3CFF,21834,1672384451999
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/enchantboss.xlsx,8E87EEDEF557483587854588BA3238FA,21876,1675669257617
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/equip.xlsx,B48A32328A57B8DFDBAB472F1A713,131355,1673751444962
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/extservers.xlsx,C7E29E9F823E9A3923B9AE8B33C09136,12436,1676354566699
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/facemod.xlsx,BBDEC392FF72E37D6DCAAAC5B35F8A2,10903,1658742301043
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/fightevent.xlsx,83E41BCD5E8A81D86CB749176188E,15642,1675131784842
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/getresource.xlsx,D9F0FB7515464729A313A3485271A9,10963,1661996705967
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/GM.xlsx,FF4B80E53432F05F523E98B7CDBF553,13040,1672305401438
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/gourmet.xlsx,3DC05C0642437F7F94C26CADE35E5E,19282,1671071006973
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/growthtask.xlsx,39F2BA8B3DA4F14C66B50131924A19,16705,1675131784795
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/guide.xlsx,9F85E5EDEB6D14BAB03C7EF6BFAF4A15,192663,1675131784802
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/guild.xlsx,D56B29EBF6811F4615E56C908E4D85,17769,1672991443412
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/guildboss.xlsx,D84314DFB6E69D93CB68F35292A6BBA,59077,1673767098700
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/help_information.xlsx,5E641942C5D9BE65334AFE37CB97E7,9438,1667958528956
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/hero_awaken.xlsx,17241886F99CA6D9C2DE6CBD8535EE8,66257,1669600775287
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/hero_method.xlsx,50AEF20FC5EB9DF8A822632FE39979,15330,1672035654186
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/herofusion.xlsx,5A4C6E879DFAA6B3AD8AFD26B0B65BA7,12628,1669280521033
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/horoscope.xlsx,9560CD4D77B1BD959A6460837913BF45,47327,1669868288742
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/hunting.xlsx,05C744CAB98DC8320579AF37AF187D6,20981,1675669257618
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/ip.xlsx,FCECF489FCBA09B725F9E6E5D5CEC5,12662,1663813033423
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/item.xlsx,0E3EDBA2B4D38FEADC2CC6AFDD448C0,46269,1675913355299
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/jump.xlsx,2E50FDED7221E4C9B67442551642426,23695,1673751445047
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/kungfu.xlsx,80D3E1ECC8D4BD72BD89B227E46E2E,21553,1673866895198
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/library.xlsx,4DAB219F3139FF5A94D1ACA845B7E66,75318,1672024001073
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/linestory.xlsx,8B252F3D12CEEEA7FFA57735E26986,16205,1672384451890
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/link.xlsx,5BA9EDFBF08BE01EB747E727AE2DC2CE,11025,1661996705968
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/mail.xlsx,EEA346AAE7AD69A16ECE0E3C0711FE,12251,1671071006947
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/mainchapter.xlsx,FE1BFBAFDADF8A649D1F67CED92A81,26062,1675669257620
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/mainline.xlsx,56A852F51BACF1F8D9A0F5FF24D08617,76018,1675669257622
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/model.xlsx,52773FF8F8493E47C832D47D6EB1F77,30604,1676517068465
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/monster.xlsx,A0B42C273AB4A932D92DD921AA1DA,47838,1672483211595
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/monsterformat.xlsx,7FBBEB8C9286E77B0C89E827FA4B0B3,304998,1673780060731
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/msgdistrib.xlsx,AF26182EB1CC72F5D1D81570BF84BF,10579,1673334891672
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/name.xlsx,A88D302B5F4EFDD8506F867673722965,16331,1664157402399
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/opencond.xlsx,D2497F3EFE5A1BD594A213882034942,21782,1676450811409
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/pagoda.xlsx,627839BC2E575B19543AB566FF94E338,34234,1675669257624
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/passcheck.xlsx,4D1386149C340606EE1FB55A018267B,11975,1671071006955
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/paypackage.xlsx,4F18D9D87B17217FF83C7ACBF17267EE,12232,1667895839191
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/playerinfor.xlsx,721CF8DD63721771C96BC681623C1F30,13375,1666145807176
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/playerlv.xlsx,E975DF92CF2E69D68F82529E7780,12099,1672819357711
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/privilegecard.xlsx,5935A3E0A03F2FA15D245F407C8E3CA5,18057,1676517068473
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/recharge.xlsx,698CA16B364BB252579F26FDCA82F8B3,13582,1669028376674
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/reddot.xlsx,98A552AFD8B11EA810C323A9858BAD63,20963,1672281763834
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/robot.xlsx,1615F3DB6FC51B61711110A3CFE06F5F,20979,1661407782710
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/shop.xlsx,C58B4394938145682CBDE40D1DAD8A,40598,1676517172729
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/show.xlsx,734A5C1D26E8995F0734B91B62EC7BB,13532,1673675480748
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/sign.xlsx,4BAED0425653ED914922C9B3592E9387,15828,1672384452018
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/skill.xlsx,41D26F1FE8F76C0FA53CBD8E9901FF9,237716,1675913355307
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/smithy.xlsx,D5BF684A6B44EA51B2EBFCF76122E6A3,18677,1672384452047
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/stroy.xlsx,C4EBEAB24A57EE48F75AD7677A79E879,98906,1666856824641
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/task.xlsx,6677CB7FA05294B14E93907751DA80F6,28349,1673408491101
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/taskcond.xlsx,E1F8F4A0EC9CF0A698285DBB67239642,49367,1675669257587
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/teaching.xlsx,7C76799E9162FDAA959B972E0149F8,17032,1676517172738
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/trolltrain.xlsx,421BDCBB1DBC58C13A639CD69499463F,25782,1676517172739
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/vikingexpedition.xlsx,D4BCB092EA2B3028BAFBCA0DA873DEA,30286,1675669257625
F:/svn/dreamworks/client/dreamworks/ExcelFile/Datas/vip.xlsx,D195DACDCEFF7989D716F27CDFF265,11585,1672819357705
F:/svn/dreamworks/client/dreamworks/ExcelFile/Defines/enum.xml,32A3B669765E7B9E3EED389AFE70FDCF,507,1675132985754
F:/svn/dreamworks/client/dreamworks/ExcelFile/Defines/format.xml,DBD3E217262D1DCFE0E65373A0317790,1117,1675132685762
F:/svn/dreamworks/client/dreamworks/ExcelFile/Defines/LocalizeParam.xml,5577C9C3A9218B6B26E1D78F526C05E,425,1658195443477

8
cmd/v2/FyneApp.toml Normal file
View File

@ -0,0 +1,8 @@
Website = "http://legu.cc"
[Details]
Icon = "app.png"
Name = "RobotGUI"
ID = "cc.legu.app"
Version = "1.2.17"
Build = 50

BIN
cmd/v2/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

7
cmd/v2/build.bat Normal file
View File

@ -0,0 +1,7 @@
@echo off
set input=%1%
echo build version:%input%
fyne package --name RobotGUI-%input% -os windows

42
cmd/v2/gen_test.go Normal file
View File

@ -0,0 +1,42 @@
package main_test
import (
"go_dreamfactory/cmd/v2/lib/common"
cfg "go_dreamfactory/sys/configure/structs"
"html/template"
"os"
"testing"
)
type TaskCond struct {
Data map[int32]*cfg.GameRdtaskCondiData
}
func TestTask(t *testing.T) {
commcfg := &common.Config{
Path: "F:\\projects\\workspace\\go_dreamfactory\\bin\\json",
}
if tb, err := cfg.NewTables(commcfg.Loader); err == nil {
data := tb.RdtaskCondi.GetDataMap()
tmpl := template.New("task")
tplTxt := `reqDataMap := map[int32]TaskCondi{
{{ range .Data }}
{{.Id}}:{ {{.Type}},[]int32{ {{.Data1}},{{.Data2}},{{.Data3}},{{.Data4}},{{.Data5}} },
{{ end}} }`
tpl, err := tmpl.Parse(tplTxt)
if err != nil {
t.Fatal(err)
}
//输出文件
file, err := os.OpenFile("./tpl.txt", os.O_CREATE|os.O_WRONLY, 0755)
if err != nil {
panic(err)
}
p := TaskCond{
Data: data,
}
tpl.Execute(file, p)
}
}

BIN
cmd/v2/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

304
cmd/v2/lib/assistant.go Normal file
View File

@ -0,0 +1,304 @@
package lib
import (
"context"
"errors"
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/pb"
"math"
"sync/atomic"
"time"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
)
type assistant struct {
timeout time.Duration //处理超时时间
lps uint32 //每秒请求量
duration time.Duration //持续时间
concurrency uint32 //并发量
ctx context.Context
callCount int64 //调用次数,每次启动时重置
goPool GoPool //携程池
cancelFunc context.CancelFunc //取消
caller Handler //处理器
status uint32 //状态
resultCh chan *CallResult //调用结果
obs observer.Observer
}
func NewAssistant(obs observer.Observer, pm ParamMgr) (Aiassistant, error) {
if err := pm.Check(); err != nil {
return nil, err
}
a := &assistant{
timeout: pm.Timeout,
lps: pm.Lps,
duration: pm.Duration,
caller: pm.Caller,
status: STATUS_ORIGINAL,
resultCh: pm.ResultCh,
obs: obs,
}
if err := a.init(); err != nil {
return nil, err
}
return a, nil
}
func (a *assistant) init() error {
logrus.Debug("AI助手初始化")
//并发量的计算
//并发量 ≈ 超时时间 / 发送的间隔时间
var total = int64(a.timeout)/int64(1e9/a.lps) + 1
if total > math.MaxInt32 {
total = math.MaxInt32
}
a.concurrency = uint32(total)
gp, err := NewGoPool(a.concurrency)
if err != nil {
return err
}
a.goPool = gp
logrus.WithField("并发量", a.concurrency).Debug("AI助手初始化完成 ")
return nil
}
func (a *assistant) callOne(req *RawReq) *RawResp {
atomic.AddInt64(&a.callCount, 1)
logrus.WithField("count", &a.callCount).WithField("Len", len(req.Req)).Debug("调用协议")
if req == nil {
return &RawResp{ID: -1, Err: errors.New("无效的请求")}
}
var rawResp RawResp
start := time.Now().UnixNano()
resp, err := a.caller.Call(req.Req)
end := time.Now().UnixNano()
elapsedTime := time.Duration(end - start)
if err != nil {
errMsg := fmt.Sprintf("调用失败: %v", err)
rawResp = RawResp{
ID: req.ID,
Err: errors.New(errMsg),
Elapse: elapsedTime,
}
} else {
rawResp = RawResp{
ID: req.ID,
Resp: resp,
Elapse: elapsedTime,
}
}
return &rawResp
}
// 异步调用接口
func (a *assistant) asyncCall() {
a.goPool.Take()
go func() {
defer func() {
if p := recover(); p != nil {
err, ok := interface{}(p).(error)
var errMsg string
if ok {
errMsg = fmt.Sprintf("Async Call Panic! (error: %s)", err)
} else {
errMsg = fmt.Sprintf("Async Call Panic! (clue: %#v)", p)
}
logrus.Errorln(errMsg)
result := &CallResult{
Id: -1,
Code: RES_CODE_FATAL_CALL,
Message: errMsg,
}
a.sendResult(result)
}
a.goPool.Return()
}()
req := a.caller.BuildReq()
if req.ID == 0 {
return
}
//调用状态 0未调用 1调用结束 2调用超时
var callStatus uint32
// 超时处理
timer := time.AfterFunc(a.timeout, func() {
if !atomic.CompareAndSwapUint32(&callStatus, 0, 2) {
return
}
result := &CallResult{
Id: req.ID,
Req: req,
Code: RES_CODE_CALL_TIMEOUT,
Message: fmt.Sprintf("超时,期望小于 %v", a.timeout),
Elapse: a.timeout,
}
a.sendResult(result)
})
resp := a.callOne(&req)
logrus.WithField("耗时", resp.Elapse).Debug("实际耗时")
if !atomic.CompareAndSwapUint32(&callStatus, 0, 1) {
return
}
timer.Stop()
var result *CallResult
if resp.Err != nil {
result = &CallResult{
Id: req.ID,
Req: req,
Code: RES_CODE_ERROR_CALL,
Message: resp.Err.Error(),
Elapse: resp.Elapse,
}
} else {
result = a.caller.Check(req, *resp)
result.Elapse = resp.Elapse
}
a.sendResult(result)
}()
}
// 停止发送
func (a *assistant) prepareStop(ctxErr error) {
logrus.WithField("cause", ctxErr).Info("准备停止")
atomic.CompareAndSwapUint32(&a.status, STATUS_STARTED, STATUS_STOPPING)
logrus.Debug("关闭结果通道")
close(a.resultCh)
atomic.StoreUint32(&a.status, STATUS_STOPPED)
}
// 发送请求即调用接口
func (a *assistant) handleReq(throttle <-chan time.Time) {
for {
select {
case <-a.ctx.Done():
a.prepareStop(a.ctx.Err())
return
default:
}
a.asyncCall()
if a.lps > 0 {
select {
case <-throttle:
case <-a.ctx.Done():
a.prepareStop(a.ctx.Err())
return
}
}
}
}
func (a *assistant) sendResult(result *CallResult) bool {
if atomic.LoadUint32(&a.status) != STATUS_STARTED {
a.printResult(result, "已停止")
return false
}
select {
case a.resultCh <- result:
return true
default:
a.printResult(result, "结果通道已满")
return false
}
}
func (a *assistant) printResult(result *CallResult, cause string) {
resultMsg := fmt.Sprintf(
"Id:%d,Code=%d,Msg=%s,Elapse=%v",
result.Id, result.Code, result.Message, result.Elapse)
logrus.Warnf("result:%s (cause:%s)", resultMsg, cause)
}
// 启动AI助手
func (a *assistant) Start() bool {
logrus.Debug("AI助手启动")
// 节流 周期性向目标发送
var throttle <-chan time.Time
if a.lps > 0 {
//间隔时间
interval := time.Duration(1e9 / a.lps)
logrus.Debugf("启动节流控制 间隔: %v", interval)
throttle = time.Tick(interval)
}
// 初始化上下文和设置取消函数
a.ctx, a.cancelFunc = context.WithTimeout(context.Background(), a.duration)
// 重置调用次数
a.callCount = 0
// 设置状态为已启动
atomic.StoreUint32(&a.status, STATUS_STARTED)
go func() {
logrus.Debug("请求处理中...")
a.handleReq(throttle)
logrus.Infof("停止 调用次数:%d", a.callCount)
}()
return true
}
// 手动停止
func (a *assistant) Stop() error {
return nil
}
func (a *assistant) ShowResult() {
statistics := &Statistics{}
max := statistics.MaxElapse
min := statistics.MinElapse
for r := range a.resultCh {
if r.Code != RES_CODE_SUCCESS {
logrus.WithField("result", r.Code).Debug("失败的结果")
continue
}
msg := &pb.UserMessage{}
// logrus.Debugf("结果字节长度 %d", len(r.Resp.Resp))
if err := proto.Unmarshal(r.Resp.Resp, msg); err != nil {
logrus.Error("结果解析失败")
continue
}
// 协议名
statistics.Route = fmt.Sprintf("%s.%s", msg.MainType, msg.SubType)
// 总耗时
statistics.ElapseTotal += float64(r.Elapse.Nanoseconds())
if float64(r.Elapse.Nanoseconds()) > max {
max = float64(r.Elapse.Nanoseconds())
} else {
min = float64(r.Elapse.Nanoseconds())
}
logrus.WithFields(logrus.Fields{"mainType": msg.MainType, "subType": msg.SubType, "耗时": r.Elapse}).Info("结果")
}
if a.callCount == 1 {
min = max
}
// 调用次数
statistics.CallCount = a.callCount
statistics.ElapseTotal = common.FormatFloatCommon(statistics.ElapseTotal / 1e6)
statistics.MaxElapse = common.FormatFloatCommon(max / 1e6)
statistics.MinElapse = common.FormatFloatCommon(min / 1e6)
//平均耗时=总耗时/调用次数
statistics.AvgElapse = common.FormatFloatCommon(statistics.ElapseTotal / float64(statistics.CallCount))
a.obs.Notify(observer.EVENT_RESULT, statistics)
}

40
cmd/v2/lib/base.go Normal file
View File

@ -0,0 +1,40 @@
package lib
import "time"
type RawReq struct {
ID int64
Req []byte
}
type RawResp struct {
ID int64
Resp []byte
Err error
Elapse time.Duration
}
type ResCode int
const (
RES_CODE_SUCCESS ResCode = 0 //成功
RES_CODE_CALL_TIMEOUT = 1001 //调用超时
RES_CODE_ERROR_CALL = 2001 // 调用错误
RES_CODE_ERROR_RESPONSE = 2002 // 响应错误
RES_CODE_ERROR_CALEE = 2003 // 被测软件内部错误
RES_CODE_FATAL_CALL = 3001 // 调用过程中发生了致命错误!
)
//助手执行状态
const (
// 原始状态
STATUS_ORIGINAL uint32 = 0
// 正在启动
STATUS_STARTING uint32 = 1
//已启动
STATUS_STARTED uint32 = 2
//正在停止
STATUS_STOPPING uint32 = 3
// 已停止
STATUS_STOPPED uint32 = 4
)

View File

@ -0,0 +1,27 @@
package common
type (
WindowAspect int64
WindowMode int64
WindowAction int64
)
const (
MainType = "mainType"
SubType = "subType"
Params = "params"
)
const (
WindowAspect_Normal WindowAspect = iota
WindowAspect_FullScreen
)
const (
DEFAULT_RESOURCE_PATH = "resources"
)
const (
CONFIG_SERVICE_NAME = "name"
CONFIG_SERVICE_URL = "url"
)

View File

@ -0,0 +1,241 @@
package common
import (
"fmt"
"go_dreamfactory/utils"
"io/ioutil"
"sort"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
type ItemList struct {
SelItemIds []string //选择的ID 多选
SelItemId string //选择的ID 单选
ItemTotal int //总数
TitleLabel *widget.Label
CachedList List
ItemList *widget.List
SearchItem []Item //用于暂存搜索结果
}
func NewItemList() *ItemList {
return &ItemList{
TitleLabel: &widget.Label{},
CachedList: NewList(""),
}
}
// 重置
func (f *ItemList) Reset() {
f.ItemTotal = 0
f.SelItemIds = []string{}
f.SelItemId = ""
f.CachedList = NewList("")
f.ItemList.Refresh()
}
func (f *ItemList) CreateList() *widget.List {
f.ItemList = widget.NewList(
func() int {
return len(f.CachedList.Items)
},
func() fyne.CanvasObject {
return widget.NewLabel("Template")
},
func(id widget.ListItemID, item fyne.CanvasObject) {
c, _ := item.(*widget.Label)
c.Text = f.CachedList.Items[id].Text
c.Refresh()
},
)
f.ItemList.OnSelected = func(id widget.ListItemID) {
selId := f.CachedList.Items[id].Id
f.SelItemId = selId
return
}
// f.ItemList.OnUnselected = func(id widget.ListItemID) {
// selId := f.CachedList.Items[id].Id
// utils.DeleteString(f.SelItemIds, selId)
// }
return f.ItemList
}
// 创建默认的列表
func (f *ItemList) CreateDefaultCheckList() *widget.List {
f.ItemList = widget.NewList(
func() int {
return len(f.CachedList.Items)
},
func() fyne.CanvasObject {
return widget.NewCheck("Template", func(bool) {})
},
func(id widget.ListItemID, item fyne.CanvasObject) {
c, _ := item.(*widget.Check)
_id := f.CachedList.Items[id].Id
c.Text = f.CachedList.Items[id].Text
c.Checked = f.CachedList.Items[id].Checked
c.OnChanged = func(b bool) {
if b {
f.SelItemIds = append(f.SelItemIds, _id)
} else {
f.SelItemIds = utils.DeleteString(f.SelItemIds, _id)
}
f.TitleLabel.SetText(fmt.Sprintf("(%d/%d)", len(f.SelItemIds), f.ItemTotal))
f.CachedList.Items[id].Checked = b
// sort.Sort(f.cachedList)
f.ItemList.Refresh()
}
c.Refresh()
},
)
return f.ItemList
}
// 创建下载列表
func (f *ItemList) CreateDownloadList() *widget.List {
f.ItemList = widget.NewList(
func() int {
return len(f.CachedList.Items)
},
func() fyne.CanvasObject {
chk := widget.NewCheck("Template", func(bool) {})
lb := widget.NewLabel("Template")
items := container.NewHBox(chk, &layout.Spacer{}, lb)
return items
},
func(id widget.ListItemID, item fyne.CanvasObject) {
c, _ := item.(*fyne.Container)
chk := c.Objects[0].(*widget.Check)
data := f.CachedList.Items[id]
chk.Text = data.Text
chk.Checked = data.Checked
chk.OnChanged = func(b bool) {
if b {
f.SelItemIds = append(f.SelItemIds, chk.Text)
} else {
f.SelItemIds = utils.DeleteString(f.SelItemIds, chk.Text)
}
f.CachedList.Items[id].Checked = b
// sort.Sort(f.cachedList)
f.ItemList.Refresh()
}
lb := c.Objects[2].(*widget.Label)
lb.Text = ConvertFileSize(data.Size)
c.Refresh()
},
)
return f.ItemList
}
func (f *ItemList) AddItem(item Item) {
f.CachedList.Items = append(f.CachedList.Items, item)
// sort.Sort(f.cachedList)
f.ItemList.Refresh()
}
func (f *ItemList) AddItemWithText(val string) {
val = strings.TrimSpace(val)
if len(val) == 0 {
return
}
newItem := Item{
Id: val,
Text: val,
Quantity: 1,
Checked: false, //默认不选中
}
f.CachedList.Items = append(f.CachedList.Items, newItem)
sort.Sort(f.CachedList)
f.ItemList.Refresh()
}
func (f *ItemList) LoadItem(dirPath string) {
f.Reset()
files, err := ioutil.ReadDir(dirPath)
if err != nil {
logrus.Error(err)
return
}
for _, file := range files {
if !file.IsDir() {
f.AddItemWithText(file.Name())
// f.selItemIds = append(f.selItemIds, file.Name())
f.ItemTotal++
// logrus.Debugf("%v", file.Name())
}
}
f.SearchItem = f.CachedList.Items
}
// 改变列表项目
func (f *ItemList) ChangeItem(tmpDir, projectDir string) {
f.ItemTotal = 0
f.SelItemIds = []string{}
f.CachedList = NewList("")
tmpFiles, err := ioutil.ReadDir(tmpDir)
if err != nil {
logrus.Error(err)
return
}
defer func() {
if len(tmpFiles) == 0 || len(f.CachedList.Items) == 0 {
ShowTip("没有任何变更的项目")
}
}()
projectFiles, err := ioutil.ReadDir(projectDir)
if err != nil {
logrus.Error(err)
return
}
pfMap := make(map[string]int64)
for _, v := range projectFiles {
if !v.IsDir() {
pfMap[v.Name()] = v.ModTime().Unix()
}
}
for _, file := range tmpFiles {
if !file.IsDir() {
if pfTime, ok := pfMap[file.Name()]; ok {
if file.ModTime().Unix() <= pfTime {
continue
}
}
f.AddItemWithText(file.Name())
// f.SelItemIds = append(f.SelItemIds, file.Name())
f.ItemTotal++
// logrus.Debugf("%v", file.Name())
}
}
}
// 刷新文件数
func (f *ItemList) ChangeFileCount() {
f.TitleLabel.SetText(fmt.Sprintf("(%d/%d)", len(f.SelItemIds), f.ItemTotal))
}
func (f *ItemList) DeleteItem(name string) {
for i, v := range f.CachedList.Items {
if v.Text == name {
f.SelItemIds = utils.DeleteString(f.SelItemIds, v.Text)
f.CachedList.Items = append(f.CachedList.Items[:i], f.CachedList.Items[i+1:]...)
if f.ItemTotal > 0 {
f.ItemTotal--
}
}
}
}

190
cmd/v2/lib/common/lang.go Normal file
View File

@ -0,0 +1,190 @@
package common
// en
// const (
// // app
// APP_NAME = "protocol"
// app_testcase
// APP_TESTCASE_INTRO = "select on left"
// APP_TESTCASE_TITLE = "protocol test"
// APP_TESTCASE_TITLE = "path"
// APP_TESTCASE_BTN_CLEARLOG = "Clear Log"
//form
// APP_TESTCASE_FORM_LABEL_HEROOBJID = "HeroObjID"
// APP_TESTCASE_FORM_LABEL_RACECARD = "RaceObjID"
// APP_TESTCASE_FORM_LABEL_CARD = "Hero"
// APP_TESTCASE_FORM_LABEL_EXPCARDS = "ExpCardID"
// APP_TESTCASE_FORM_LABEL_OID = "HeroObjOID"
// APP_TESTCASE_FORM_LABEL_NUM = "Amount"
// APP_TESTCASE_FORM_TASKTAG = "TaskTag"
// APP_TESTCASE_FORM_TASK_OID = "TaskID"
// APP_TESTCASE_FORM_NICKNAME = "NickName"
// APP_TESTCASE_OPTIONS = "Option"
// AAP_TESTCASE_FORM_TASK_DAY = "Day"
// AAP_TESTCASE_FORM_TASK_WEEK = "Week"
// AAP_TESTCASE_FORM_TASK_ACHIEVE = "Achieve"
// // toolbar
// TOOLBAR_WELCOME = "welcome"
// TOOLBAR_TESTER = "tester"
// //button
// BUTTON_LOGIN = "login"
// BUTTON_REGISTE = "registe"
// BUTTON_RANDOM = "random"
// BUTTON_OK = "ok"
// BUTTON_CANCEL = "cancel"
// //label
// LABEL_CHOOSE = "welcome,The service you choose is "
// LABEL_NICKNAME = "nickname"
// LABEL_ACCOUNT = "account"
// LABEL_WELCOME = "welcome "
// // form title
// FORM_TITLE_CREATEROLE = "create role"
// FORM_TITLE_LOGIN = "login"
// // info
// INFO_WAIT = "Please wait"
// // menu
// MENU_FILE = "file"
// //toy
// // datetiem
// DATETIME = "datetime"
// //userinfo
// USERINFO_TITLE = "用户信息"
// USERINFO_UID = "UID"
// USERINFO_ACC = "Acc"
// USERINFO_NAME = "Name"
// USERINFO_LV = "Lv"
// USERINFO_GOLD = "Gold"
// USERINFO_EXP = "Exp"
// USERINFO_VIP = "VIP"
// USERINFO_AVATAR = "Ava"
// USERINFO_BTN_COPY = "COPY"
// )
// zh
const (
APP_NAME = "机器人"
// app 子标题 [0.0.1 build-1] 应用名称
APP_WIN_TITLE = "%s [%s build-%d] %s"
WELCOME_MSG = "欢迎使用工具箱"
//about
APP_ABOUT_TITLE = "关于"
APP_ABOUT_CONFIRM = "好的"
APP_ABOUT_INFO = `GUI 梦工厂工具`
// app_testcase
APP_TESTCASE_TITLE = "接口测试"
APP_TESTCASE_LABEL_PATH = "路径"
APP_TESTCASE_BTN_CLEARLOG = "清除"
APP_TESTCASE_INTRO = "选择左侧的测试接口"
// toolbar
TOOLBAR_MONITOR = "推送"
TOOLBAR_TESTER = "接口测试"
TOOLBAR_GEN = "Luban"
TOOLBAR_SEC = "密码器"
TOOLBAR_WEL = "欢迎"
TOOLBAR_TERM = "同步配置"
TOOLBAR_PB = "protobuf"
TOOLBAR_AUTO = "自动化"
TOOLBAR_PING = "端口扫描"
TOOLBAR_MGODB = "MongoDB"
TOOLBAR_VALID = "配置校验"
TOOLBAR_PERF_TIP = "开始"
TOOLBAR_PERF_CONF = "配置"
TOOLBAR_PERF_LOGIN = "登陆"
TOOLBAR_PERF_CREATE = "创角"
TOOLBAR_PERF_CHOOSE = "选项"
TOOLBAR_PERF_PB = "协议"
TOOLBAR_PERF_RES = "结果"
//monitor
APP_MONITOR_TITLE_ID = "编号"
APP_MONITOR_TITLE_DATE = "时间"
APP_MONITOR_TITLE_DATA = "数据"
APP_MONITOR_BTN_VIEW = "查看"
//form
APP_TESTCASE_FORM_LABEL_HEROOBJID = "卡片ID"
APP_TESTCASE_FORM_LABEL_RACECARD = "种族卡片"
APP_TESTCASE_FORM_LABEL_CARD = "卡片"
APP_TESTCASE_FORM_LABEL_EXPCARDS = "经验卡"
APP_TESTCASE_FORM_LABEL_OID = "卡片OID"
APP_TESTCASE_FORM_LABEL_NUM = "数量"
APP_TESTCASE_FORM_TASKTAG = "任务类型"
APP_TESTCASE_FORM_TASK_OID = "任务OID"
APP_TESTCASE_FORM_NICKNAME = "昵称"
APP_TESTCASE_FORM_SIGN = "签名"
APP_TESTCASE_OPTIONS = "选填"
AAP_TESTCASE_FORM_TASK_DAY = "日常任务"
APP_TESTCASE_FORM_TASK_WEEK = "周常任务"
APP_TESTCASE_FORM_TASK_ACHIEVE = "成就"
APP_TESTCASE_FORM_BINGO = "命令"
APP_TESTCASE_BTN_PUSH = "推送"
APP_TESTCASE_FORM_LABEL_FID = "好友ID"
APP_TESTCASE_FORM_MAILID = "邮件ID"
APP_TESTCASE_FORM_ITEMTYPE = "道具类型"
APP_TESTCASE_FORM_ITEM_GEZIID = "格子ID"
APP_TESTCASE_FORM_ITEM_GOODSID = "物品ID"
APP_TESTCASE_FORM_ITEM_IDX = "下标"
//button
BUTTON_LOGIN = "登录"
BUTTON_REGISTE = "注册"
BUTTON_RANDOM = "随机"
BUTTON_OK = "确定"
BUTTON_CANCEL = "取消"
//label
LABEL_CHOOSE = "欢迎,你即将登录的服务是[%s]%s"
LABEL_NICKNAME = "昵称"
LABEL_ACCOUNT = "账号"
LABEL_WELCOME = "欢迎 "
// form title
FORM_TITLE_CREATEROLE = "创角"
FORM_TITLE_LOGIN = "登录"
// info
INFO_WAIT = "请稍后"
// menu
MENU_FILE = "文件"
// toy
// datetiem
DATETIME = "时钟"
// userinfo
USERINFO_BTN_COPY = "复制UID"
USERINFO_BTN_REFRESH = "刷新"
USERINFO_PANEL_TITLE = "用户信息"
USERINFO_UID = "UID"
USERINFO_SERVER = "区服"
USERINFO_ACC = "账号"
USERINFO_NAME = "昵称"
USERINFO_TITLE = "头衔"
USERINFO_LV = "等级"
USERINFO_GOLD = "金币"
USERINFO_EXP = "经验"
USERINFO_DIAMOND = "钻石"
USERINFO_VIP = "贵宾"
USERINFO_AVATAR = "头像"
USERINFO_FRIENDPOINT = "友情点"
USERINFO_SIGN = "签名"
USERINFO_MODINAME = "免费改名次数"
USERINFO_ACTIVE_DAY = "日活"
USERINFO_ACTIVE_WEEK = "周活"
USERINFO_CREATETM = "创建"
USERINFO_UPDATETM = "更新"
)

75
cmd/v2/lib/common/list.go Normal file
View File

@ -0,0 +1,75 @@
package common
import "strings"
var replacer *strings.Replacer
func init() {
trimChars := "0123456789-()/½⅓¼⅕⅙⅐⅛⅑⅔⅖¾⅗⅘"
tcs := make([]string, len(trimChars)*2)
for i, c := range trimChars {
tcs[i*2] = string(c)
tcs[i*2+1] = ""
}
replacer = strings.NewReplacer(tcs...)
}
type List struct {
Name string `json:"name"`
Group string `json:"group"`
Items []Item `json:"items"`
// ID is the Mealie API reference for the list.
ID int `json:"id"`
}
var sortableText map[string]string = map[string]string{}
func (l List) Len() int { return len(l.Items) }
func (l List) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }
func (l List) Less(i, j int) bool {
var ok bool
var it, jt string
li := l.Items[i]
lit := li.Text
if it, ok = sortableText[lit]; !ok {
it = replacer.Replace(strings.ToUpper(lit))
it = strings.TrimLeft(it, " ")
sortableText[lit] = it
}
lj := l.Items[j]
ljt := lj.Text
if jt, ok = sortableText[ljt]; !ok {
jt = replacer.Replace(strings.ToUpper(ljt))
jt = strings.TrimLeft(jt, " ")
sortableText[ljt] = jt
}
if !li.Checked {
if lj.Checked {
return true
}
return it < jt
}
if !lj.Checked {
return false
}
return it < jt
}
type Item struct {
Id string `json:"id"`
Title string `json:"title"`
Text string `json:"text"`
Quantity int `json:"quantity"`
Checked bool `json:"checked"`
Size int64 `json:"size"`
Data interface{} `json:"data"`
}
func NewList(name string) List {
return List{
ID: -1,
Name: name,
Items: []Item{},
}
}

10
cmd/v2/lib/common/log.go Normal file
View File

@ -0,0 +1,10 @@
package common
type LogData struct {
Level string `json:"level"`
MainType string `json:"mainType"`
SubType string `json:"subType"`
Time string `json:"time"`
Msg string `json:"msg"`
Params interface{} `json:"params"`
}

View File

@ -0,0 +1,57 @@
package common
import (
"context"
"fmt"
os_storage "go_dreamfactory/cmd/v2/lib/storage"
"github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type MogoTool struct {
conf *os_storage.Config
storage os_storage.Storage
db *mongo.Database
client *mongo.Client
}
func NewMogoTool() *MogoTool {
this := &MogoTool{}
this.storage, _ = os_storage.NewOSStorage()
var err error
this.conf, err = this.storage.LoadConfig()
if err != nil {
logrus.Error(err)
}
return this
}
func (this *MogoTool) OpenConn() {
if this.conf.MgoDB == nil {
return
}
var option *options.ClientOptions
if (this.conf.MgoDB.User == "" && this.conf.MgoDB.Password == "") &&
this.conf.MgoDB.Host != "" && this.conf.MgoDB.Port != 0 && this.conf.MgoDB.Database != "" {
option = options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%d", this.conf.MgoDB.Host, this.conf.MgoDB.Port))
} else {
option = options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%s@%s:%d", this.conf.MgoDB.User, this.conf.MgoDB.Password,
this.conf.MgoDB.Host, this.conf.MgoDB.Port))
}
client, err := mongo.Connect(context.TODO(), option)
if err != nil {
logrus.Error(err)
return
}
if err2 := client.Ping(context.TODO(), nil); err2 != nil {
logrus.Error("Mongo连接失败", err2)
return
}
this.db = client.Database(this.conf.MgoDB.Database)
logrus.Info("mongo connect successful")
}

25
cmd/v2/lib/common/sec.go Normal file
View File

@ -0,0 +1,25 @@
package common
import (
"fmt"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/utils"
"time"
jsoniter "github.com/json-iterator/go"
"github.com/nacos-group/nacos-sdk-go/util"
)
func BuildSecStr(sid, account string) string {
now := time.Now().Unix()
jsonByte, _ := jsoniter.Marshal(&model.LoginParam{
Account: account,
ServerId: sid,
TimeStamp: now,
})
jsonBase64 := utils.Base64Encode(jsonByte)
// log.Printf("client base64:%s", jsonBase64)
clientMd5key := util.Md5(jsonBase64)
// logrus.Debugf("client md5:%s", clientMd5key)
return fmt.Sprintf("CE:%s%s", clientMd5key, jsonBase64)
}

40
cmd/v2/lib/common/tip.go Normal file
View File

@ -0,0 +1,40 @@
package common
import (
"image/color"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/widget"
)
// 显示Tip
func ShowTip(content string) {
drv := fyne.CurrentApp().Driver()
if drv, ok := drv.(desktop.Driver); ok {
w := drv.CreateSplashWindow()
w.SetContent(widget.NewLabelWithStyle(content, fyne.TextAlignCenter, fyne.TextStyle{}))
w.Show()
go func() {
time.Sleep(time.Millisecond * 1500)
w.Close()
}()
}
}
func ShowCanvasTip(content string) {
drv := fyne.CurrentApp().Driver()
if drv, ok := drv.(desktop.Driver); ok {
w := drv.CreateSplashWindow()
w.SetContent(canvas.NewText(content, color.RGBA{255, 0, 0, 255}))
w.Show()
go func() {
time.Sleep(time.Millisecond * 1500)
w.Close()
}()
}
}

261
cmd/v2/lib/common/utils.go Normal file
View File

@ -0,0 +1,261 @@
package common
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"go_dreamfactory/lego/core"
"io"
"io/ioutil"
"math"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
func MF(s1 core.M_Modules, s2 string) string {
return fmt.Sprintf("%s.%s", s1, s2)
}
func FormatJson(data string) (string, error) {
var out bytes.Buffer
err := json.Indent(&out, []byte(data), "", " ")
return out.String(), err
}
func SubStr(str string, start int, length int) (result string) {
s := []rune(str)
total := len(s)
if total == 0 {
return
}
// 允许从尾部开始计算
if start < 0 {
start = total + start
if start < 0 {
return
}
}
if start > total {
return
}
// 到末尾
if length < 0 {
length = total
}
end := start + length
if end > total {
result = string(s[start:])
} else {
result = string(s[start:end])
}
return
}
// 是否升级
func IsUpgrade(newVersion, oldVersion string) bool {
nvArr := strings.SplitN(newVersion, ".", 3)
if len(nvArr) != 3 {
logrus.Error("new version format err")
return false
}
ovArr := strings.SplitN(oldVersion, ".", 3)
if len(ovArr) != 3 {
logrus.Error("old version format err")
return false
}
nvNum := cast.ToInt(nvArr[0])*100 + cast.ToInt(nvArr[1])*10 + cast.ToInt(nvArr[2])
ovNum := cast.ToInt(ovArr[0])*100 + cast.ToInt(ovArr[1])*10 + cast.ToInt(ovArr[2])
if nvNum > ovNum {
return true
}
return false
}
func Copy(src, dst string) (len int64, err error) {
dstWriter, err := os.Create(dst)
if err != nil {
logrus.Error(err)
return
}
srcReader, err := os.Open(src)
if err != nil {
logrus.Error(err)
return
}
// Copy()函数其实是调用了
// io包中私有函数copyBuffer() 默认缓冲区是32K
// 与Copy()函数功能一致的是CopyBuffer()可以设置缓冲区
len, err = io.Copy(dstWriter, srcReader)
if err != nil {
logrus.Error(err)
return
}
return
}
func DeleteFile(filePath string) error {
return os.Remove(filePath)
}
func RemoveContents(dir string) error {
d, err := os.Open(dir)
if err != nil {
return err
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
return err
}
for _, name := range names {
err = os.RemoveAll(filepath.Join(dir, name))
if err != nil {
return err
}
}
return nil
}
func ConvertFileSize(size int64) string {
if size == 0 {
return "0"
}
kb := float64(size) / float64(1024)
if kb < 1024 {
f := math.Ceil(kb)
return strconv.Itoa(int(f)) + " KB"
}
mb := kb / float64(1024)
if mb < 1024 {
f := math.Ceil(mb)
return strconv.Itoa(int(f)) + " MB"
}
gb := mb / float64(1024)
if gb < 1024 {
f := math.Ceil(mb)
return strconv.Itoa(int(f)) + " G"
}
t := gb / float64(1024)
if t < 1024 {
f := math.Ceil(mb)
return strconv.Itoa(int(f)) + " T"
}
if t >= 1024 {
return "VeryBig"
}
return "0"
}
type Config struct {
Path string
}
func (this *Config) SetPath(path string) {
this.Path = path
}
// "F:\\projects\\workspace\\go_dreamfactory\\bin\\json"
func (this *Config) Loader(file string) ([]map[string]interface{}, error) {
if this.Path == "" {
return nil, errors.New("no setting config path")
}
path := filepath.Join(this.Path, file+".json")
if bytes, err := ioutil.ReadFile(path); err != nil {
return nil, err
} else {
jsonData := make([]map[string]interface{}, 0)
if err = json.Unmarshal(bytes, &jsonData); err != nil {
return nil, err
}
return jsonData, nil
}
}
func Json2Pb(jsonString string, pb proto.Message) error {
m := pb.ProtoReflect().Interface()
if err := protojson.Unmarshal([]byte(jsonString), m); err != nil {
return err
}
return nil
}
func Pb2Json(pb proto.Message) string {
return protojson.Format(pb.ProtoReflect().Interface())
}
// 保留两位小数 通用
func FormatFloatCommon(num float64) float64 {
value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", num), 64)
return value
}
// 保留两位小数,舍弃尾数,无进位运算
// 主要逻辑就是先乘trunc之后再除回去就达到了保留N位小数的效果
func FormatFloat(num float64, decimal int) (float64, error) {
// 默认乘1
d := float64(1)
if decimal > 0 {
// 10的N次方
d = math.Pow10(decimal)
}
// math.trunc作用就是返回浮点数的整数部分
// 再除回去小数点后无效的0也就不存在了
res := strconv.FormatFloat(math.Trunc(num*d)/d, 'f', -1, 64)
return strconv.ParseFloat(res, 64)
}
// 强制舍弃尾数
func FormatFloatFloor(num float64, decimal int) (float64, error) {
// 默认乘1
d := float64(1)
if decimal > 0 {
// 10的N次方
d = math.Pow10(decimal)
}
// math.trunc作用就是返回浮点数的整数部分
// 再除回去小数点后无效的0也就不存在了
res := strconv.FormatFloat(math.Floor(num*d)/d, 'f', -1, 64)
return strconv.ParseFloat(res, 64)
}
// 舍弃的尾数不为0强制进位
func FormatFloatCeil(num float64, decimal int) (float64, error) {
// 默认乘1
d := float64(1)
if decimal > 0 {
// 10的N次方
d = math.Pow10(decimal)
}
// math.trunc作用就是返回浮点数的整数部分
// 再除回去小数点后无效的0也就不存在了
res := strconv.FormatFloat(math.Ceil(num*d)/d, 'f', -1, 64)
return strconv.ParseFloat(res, 64)
}
func LastChars(str string, count int) string {
if len(str) <= count {
return str
}
return str[len(str)-count:]
}

View File

@ -0,0 +1,65 @@
package common
import (
"fmt"
"go_dreamfactory/pb"
"reflect"
"strconv"
"testing"
"time"
)
func TestSubStr(t *testing.T) {
str := "type.googleapis.com/UserResChangePush"
r := SubStr(str, 20, len(str))
fmt.Println(r)
}
func TestConver(t *testing.T) {
s := ConvertFileSize(25325466)
fmt.Println(s)
}
func TestIsUpgrade(t *testing.T) {
IsUpgrade("1.2.2", "1.0.21")
}
func TestJsonToPb(t *testing.T) {
jsonStr := `{"account":"zhaocy","sid":"dfz"}`
login := &pb.UserLoginReq{}
Json2Pb(jsonStr, login)
fmt.Println(login.Account, login.Sid)
fmt.Println(Pb2Json(login))
t2 := reflect.TypeOf(login)
fmt.Println("Name:", t2.Elem().Name())
}
func TestSpli(t *testing.T) {
now := time.Now()
fmt.Println(now.UnixNano())
fmt.Println(now.UnixNano() / 1e6) //将纳秒转换为毫秒
}
func TestDiv(t *testing.T) {
numF := float64(32766600)
// 保留两位小数, 通用
value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", numF/1e6), 64)
fmt.Println(reflect.TypeOf(value), value)
fmt.Println(FormatFloatCommon(numF / 1e6))
num, _ := FormatFloat(numF, 2)
fmt.Println(reflect.TypeOf(num), num)
// 舍弃的尾数不为0强制进位
num, _ = FormatFloatCeil(0.2205, 2)
fmt.Println(reflect.TypeOf(num), num)
// 强制舍弃尾数
num, _ = FormatFloatFloor(0.2295, 2)
fmt.Println(reflect.TypeOf(num), num)
}
func TestSub(t *testing.T){
fmt.Println(LastChars("23af2", 6))
}

7
cmd/v2/lib/core.go Normal file
View File

@ -0,0 +1,7 @@
package lib
type Aiassistant interface {
Start() bool
Stop() error
ShowResult()
}

60
cmd/v2/lib/gopool.go Normal file
View File

@ -0,0 +1,60 @@
package lib
import "errors"
type GoPool interface {
Take()
Return()
Total() uint32
Remainder() uint32
}
var _ GoPool = (*myGoPool)(nil)
type myGoPool struct {
total uint32 //总数
poolCh chan struct{} //池子容量
active bool //是否激活
}
func NewGoPool(total uint32) (GoPool, error) {
gp := myGoPool{}
if !gp.init(total) {
return nil, errors.New("携程池初始化失败")
}
return &gp, nil
}
func (gp *myGoPool) init(total uint32) bool {
if gp.active {
return false
}
if total == 0 {
return false
}
ch := make(chan struct{}, total)
n := int(total)
for i := 0; i < n; i++ {
ch <- struct{}{}
}
gp.poolCh = ch
gp.total = total
gp.active = true
return true
}
func (gp *myGoPool) Take() {
<-gp.poolCh
}
func (gp *myGoPool) Return() {
gp.poolCh <- struct{}{}
}
func (gp *myGoPool) Total() uint32 { return gp.total }
func (gp *myGoPool) Remainder() uint32 {
return uint32(len(gp.poolCh))
}

38
cmd/v2/lib/handler.go Normal file
View File

@ -0,0 +1,38 @@
package lib
import (
"sync"
"time"
)
// 处理器接口
type Handler interface {
SetReq(req []byte, flag bool)
// 处理请求
BuildReq() RawReq
//调用
Call(req []byte) ([]byte, error)
// 检查响应
Check(req RawReq, rsp RawResp) *CallResult
GetConnMap() sync.Map
}
// 调用结果
type CallResult struct {
Id int64
Req RawReq
Resp RawResp
Code ResCode
Message string
Elapse time.Duration
}
type Statistics struct {
ElapseTotal float64 //总耗时
MaxElapse float64 //最大耗时
MinElapse float64 //最小耗时
AvgElapse float64 //平均耗时
CallCount int64 //调用次数
Route string //协议名称
}

57
cmd/v2/lib/param.go Normal file
View File

@ -0,0 +1,57 @@
package lib
import (
"bytes"
"errors"
"fmt"
"strings"
"time"
"github.com/sirupsen/logrus"
)
type ParamMgr struct {
Timeout time.Duration
Lps uint32
Duration time.Duration
Caller Handler //调用器
ResultCh chan *CallResult //调用结果
}
func (param *ParamMgr) Check() error {
var errMsgs []string
if param.Caller == nil {
errMsgs = append(errMsgs, "无效的调用器")
}
if param.Timeout == 0 {
errMsgs = append(errMsgs, "参数Timeout无效")
}
if param.Duration == 0 {
errMsgs = append(errMsgs, "参数Duration无效")
}
if param.Lps == 0 {
errMsgs = append(errMsgs, "参数lps无效")
}
if param.ResultCh == nil {
errMsgs = append(errMsgs, "结果通过未实例化")
}
var buf bytes.Buffer
buf.WriteString("校验参数")
if errMsgs != nil {
errMsg := strings.Join(errMsgs, " ")
buf.WriteString(fmt.Sprintf("没有通过 %s", errMsg))
logrus.Infoln(buf.String())
return errors.New(errMsg)
}
buf.WriteString(fmt.Sprintf("通过. (timeoutMS=%s, lps=%d, durationS=%s)",
param.Timeout, param.Lps, param.Duration))
logrus.Debugln(buf.String())
return nil
}

867
cmd/v2/lib/pb/protocol.go Normal file
View File

@ -0,0 +1,867 @@
package pb
import (
// "errors"
// "fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/pb"
// "go_dreamfactory/comm"
// "go_dreamfactory/modules/friend"
// "go_dreamfactory/modules/growtask"
// "go_dreamfactory/modules/hero"
// "go_dreamfactory/modules/library"
// "go_dreamfactory/modules/linestory"
// "go_dreamfactory/modules/mline"
// "go_dreamfactory/modules/sociaty"
// "go_dreamfactory/modules/sys"
// "go_dreamfactory/modules/task"
// "go_dreamfactory/modules/user"
// "go_dreamfactory/modules/worldtask"
// "go_dreamfactory/pb"
// "strings"
// "github.com/sirupsen/logrus"
// "google.golang.org/protobuf/proto"
)
// 2 添加导航菜单
var (
CaseIndex = map[string][]string{
"": {
string("gm"),
// string(comm.ModuleSys),
// string(comm.ModuleUser),
// string(comm.ModuleHero),
// string(comm.ModuleTask),
// string(comm.ModuleFriend),
// string(comm.ModuleMail),
// string(comm.ModuleItems),
// string(comm.ModuleEquipment),
// string(comm.ModuleShop),
// string(comm.ModuleMline),
// string(comm.ModulePagoda),
// string(comm.ModuleRtask),
// string(comm.ModuleLinestory),
// string(comm.ModuleGourmet),
// string(comm.ModuleSociaty),
// string(comm.ModuleTroll),
// string(comm.ModuleGrowtask),
string("worldtask"),
// string(comm.ModuleReddot),
string("smithy"),
// string(comm.ModuleDispatch),
// string(comm.ModuleReputation),
// string(comm.ModuleOldtimes),
},
"gm": {
common.MF("gm", "cmd"),
// common.MF(comm.ModuleGM, "gi"),
},
"sys": {
// common.MF(comm.ModuleSys, "funclist"),
},
// "reddot": {common.MF(comm.ModuleReddot, "get")},
"user": {
// common.MF(comm.ModuleUser, user.UserSubTypeModifyAvatar),
// common.MF(comm.ModuleUser, user.UserSubTypeModifyName),
// common.MF(comm.ModuleUser, user.UserSubTypeModifyFigure),
// common.MF(comm.ModuleUser, user.UserSubTYpeModifyBgp),
// common.MF(comm.ModuleUser, user.UserSubTypeModifySign),
// common.MF(comm.ModuleUser, user.UserSubTypeBattlerecord),
},
"items": {
// common.MF(comm.ModuleItems, "getlist"),
// common.MF(comm.ModuleItems, "sellitem"),
// common.MF(comm.ModuleItems, "useitem"),
},
"mail": {
// common.MF(comm.ModuleMail, "getlist"),
// common.MF(comm.ModuleMail, "readmail"),
// common.MF(comm.ModuleMail, "delmail"),
// common.MF(comm.ModuleMail, "getusermailattachment"),
// common.MF(comm.ModuleMail, "getallmailattachment"),
},
"hero": {
// common.MF(comm.ModuleHero, hero.HeroSubTypeList),
// common.MF(comm.ModuleHero, hero.StrengthenUplv),
// common.MF(comm.ModuleHero, hero.StrengthenUpStar),
// common.MF(comm.ModuleHero, hero.DrawCard),
// common.MF(comm.ModuleHero, "info"),
},
"equipment": {
// common.MF(comm.ModuleEquipment, "getlist"),
// common.MF(comm.ModuleEquipment, "equip"),
// common.MF(comm.ModuleEquipment, "upgrade"),
},
"task": {
// common.MF(comm.ModuleTask, task.TaskSubTypeList),
// common.MF(comm.ModuleTask, task.TaskSubTypeActiveList),
// common.MF(comm.ModuleTask, task.TaskSubTypeActiveReceive),
// common.MF(comm.ModuleTask, task.TaskSubTypeGetrecord),
},
"friend": {
// common.MF(comm.ModuleFriend, friend.FriendSubTypeList),
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAgree),
// common.MF(comm.ModuleFriend, friend.FriendSubTypeBlacklist),
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAddBlack),
// common.MF(comm.ModuleFriend, friend.FriendSubTypeDelBlack),
// common.MF(comm.ModuleFriend, friend.FriendSubTypeRandList),
// common.MF(comm.ModuleFriend, friend.FriendSubTypeGetreward),
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAssistHero),
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAssistlist),
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAssistHeroList),
},
"shop": {
// common.MF(comm.ModuleShop, "getlist"),
// common.MF(comm.ModuleShop, "buy"),
},
"mainline": {
// common.MF(comm.ModuleMline, mline.MlineGetListResp),
// common.MF(comm.ModuleMline, mline.MlineChallengeResp),
// common.MF(comm.ModuleMline, mline.MlineGetRewardResp),
},
"pagoda": {
// common.MF(comm.ModulePagoda, "getlist"),
},
"linestory": {
// common.MF(comm.ModuleLinestory, linestory.LinestorySubTypeChapter),
// common.MF(comm.ModuleLibrary, library.LibraryFetterstoryTaskResp),
},
"gourmet": {
// common.MF(comm.ModuleGourmet, "getranduser"),
},
"sociaty": {
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeList),
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeCreate),
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeMine),
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeTasklist),
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeRank),
// common.MF(comm.ModuleSociaty, "boss"),
},
"troll": {
// common.MF(comm.ModuleTroll, "getlist"),
},
"growtask": {
// common.MF(comm.ModuleGrowtask, growtask.GrowtaskSubTypeList),
},
"worldtask": {
// common.MF(comm.ModuleWorldtask, worldtask.WorldtaskSubtypeMine),
// common.MF(comm.ModuleWorldtask, "check"),
common.MF("worldtask", "test"),
},
"smithy": {
common.MF("smithy", "tasklist"),
},
"dispatch": {
// common.MF(comm.ModuleDispatch, "dispatch"),
// common.MF(comm.ModulePractice, "practice"),
},
"reputation": {
// common.MF(comm.ModuleReputation, "reputation"),
},
"oldtimes": {
// common.MF(comm.ModuleOldtimes, "oldtimes"),
},
}
)
// 3 注册测试用例
var (
CaseNav = map[string]*model.TestCase{
// gm
"gm": {
NavLabel: "GM",
MainType: string("gm"),
Enabled: true,
},
common.MF("gm", "cmd"): {
Desc: "eg. bingo:item,10001,1 bingo:attr,gold,1000000",
MainType: string("gm"),
SubType: "cmd",
Req: &pb.GMCmdReq{},
Rsp: &pb.GMCmdResp{},
Enabled: true,
},
// common.MF(comm.ModuleGM, "gi"): {
// NavLabel: "实例配置",
// Desc: "全局实例配置",
// MainType: string(comm.ModuleGM),
// SubType: "gi",
// Enabled: true,
// },
// "reddot": {
// NavLabel: "红点",
// MainType: string(comm.ModuleReddot),
// Enabled: true,
// },
// common.MF(comm.ModuleReddot, "get"): {
// Desc: "红点",
// NavLabel: "红点",
// MainType: string(comm.ModuleReddot),
// SubType: "get",
// Enabled: true,
// },
// "sys": {
// NavLabel: "系统",
// MainType: string(comm.ModuleSys),
// Enabled: true,
// },
// common.MF(comm.ModuleSys, "funclist"): {
// NavLabel: "功能列表",
// Desc: "返回未开启的功能列表",
// MainType: string(comm.ModuleSys),
// // SubType: sys.SysSubTypeFunc,
// Enabled: true,
// },
// // user
// "user": {
// NavLabel: "用户",
// MainType: "user",
// Enabled: true,
// },
// common.MF(comm.ModuleUser, user.UserSubTypeModifyAvatar): {
// Desc: "用户头像修改",
// NavLabel: "修改头像",
// MainType: string(comm.ModuleUser),
// SubType: user.UserSubTypeModifyAvatar,
// Enabled: true,
// },
// "user.modifyname": {
// Desc: "用户昵称修改",
// NavLabel: "修改昵称",
// MainType: "user",
// SubType: "modifyname",
// Req: &pb.UserModifynameReq{},
// Rsp: &pb.UserModifynameResp{},
// Print: func(rsp proto.Message) string {
// logrus.WithFields(logrus.Fields{"main": "user", "stype": "modifyname"}).Debug("print")
// var formatStr strings.Builder
// if in, ok := rsp.(*pb.UserMessage); ok {
// out := &pb.UserModifynameResp{}
// if !comm.ProtoUnmarshal(in, out) {
// return errors.New("unmarshal err").Error()
// }
// formatStr.WriteString(fmt.Sprintf("Uid:%s count:%d", out.Uid, out.Count))
// }
// return formatStr.String()
// },
// Enabled: true,
// },
// common.MF(comm.ModuleUser, user.UserSubTypeModifyFigure): {
// Desc: "用户形象修改",
// NavLabel: "修改形象",
// MainType: string(comm.ModuleUser),
// SubType: user.UserSubTypeModifyFigure,
// Enabled: true,
// },
// common.MF(comm.ModuleUser, user.UserSubTYpeModifyBgp): {
// Desc: "背景修改",
// NavLabel: "修改背景",
// MainType: string(comm.ModuleUser),
// SubType: user.UserSubTYpeModifyBgp,
// Enabled: true,
// },
// common.MF(comm.ModuleUser, user.UserSubTypeModifySign): {
// NavLabel: "修改签名",
// Desc: "用户签名修改",
// MainType: string(comm.ModuleUser),
// SubType: user.UserSubTypeModifySign,
// Enabled: true,
// },
// common.MF(comm.ModuleUser, user.UserSubTypeBattlerecord): {
// NavLabel: "战斗记录",
// Desc: "玩家在心魔塔、狩猎、维京远征的战斗记录",
// MainType: string(comm.ModuleUser),
// SubType: user.UserSubTypeBattlerecord,
// Enabled: true,
// },
//items
// string(comm.ModuleItems): {
// NavLabel: "道具",
// MainType: string(comm.ModuleItems),
// Enabled: true,
// },
// common.MF(comm.ModuleItems, "getlist"): {
// NavLabel: "道具列表",
// Desc: "道具列表",
// MainType: string(comm.ModuleItems),
// SubType: "getlist",
// Enabled: true,
// },
// common.MF(comm.ModuleItems, "sellitem"): {
// NavLabel: "道具售卖",
// Desc: "道具售卖",
// MainType: string(comm.ModuleItems),
// SubType: "sellitem",
// Enabled: true,
// },
// common.MF(comm.ModuleItems, "useitem"): {
// NavLabel: "道具使用",
// Desc: "道具使用",
// MainType: string(comm.ModuleItems),
// SubType: "useitem",
// Enabled: true,
// },
//mail
// string(comm.ModuleMail): {
// NavLabel: "邮箱",
// MainType: string(comm.ModuleMail),
// Enabled: true,
// },
// common.MF(comm.ModuleMail, "getlist"): {
// NavLabel: "我的邮件",
// Desc: "我的邮件",
// MainType: string(comm.ModuleMail),
// SubType: "getlist",
// Enabled: true,
// },
// common.MF(comm.ModuleMail, "readmail"): {
// NavLabel: "读邮件",
// Desc: "读取邮件",
// MainType: string(comm.ModuleMail),
// SubType: "readmail",
// Enabled: true,
// },
// common.MF(comm.ModuleMail, "delmail"): {
// NavLabel: "删除邮件",
// Desc: "删除邮件",
// MainType: string(comm.ModuleMail),
// SubType: "delmail",
// Enabled: true,
// },
// common.MF(comm.ModuleMail, "getusermailattachment"): {
// NavLabel: "领取附件",
// Desc: "领取附件",
// MainType: string(comm.ModuleMail),
// SubType: "getusermailattachment",
// Enabled: true,
// },
// common.MF(comm.ModuleMail, "getallmailattachment"): {
// NavLabel: "一键领取",
// Desc: "领取所有附件",
// MainType: string(comm.ModuleMail),
// SubType: "getallmailattachment",
// Enabled: true,
// },
// task
// "task": {
// NavLabel: "任务",
// MainType: "task",
// Enabled: true,
// },
// "task.list": {
// NavLabel: "任务列表",
// Desc: "用户任务列表",
// MainType: "task",
// SubType: "list",
// Req: &pb.TaskListReq{},
// Rsp: &pb.TaskListResp{},
// Enabled: true,
// Print: func(rsp proto.Message) string {
// logrus.WithFields(logrus.Fields{"main": "task", "stype": "list"}).Debug("print")
// var formatStr strings.Builder
// if in, ok := rsp.(*pb.UserMessage); ok {
// out := &pb.TaskListResp{}
// if !comm.ProtoUnmarshal(in, out) {
// return errors.New("unmarshal err").Error()
// }
// for i, v := range out.List {
// formatStr.WriteString(fmt.Sprintf("%d- %v\n", (i + 1), v))
// }
// }
// return formatStr.String()
// },
// },
// common.MF(comm.ModuleTask, task.TaskSubTypeGetrecord): {
// NavLabel: "任务数据",
// Desc: "任务数据",
// MainType: string(comm.ModuleTask),
// SubType: task.TaskSubTypeGetrecord,
// Enabled: true,
// },
// common.MF(comm.ModuleTask, task.TaskSubTypeActiveList): {
// NavLabel: "活跃度列表",
// Desc: "用户活跃度列表",
// MainType: string(comm.ModuleTask),
// SubType: task.TaskSubTypeActiveList,
// Req: &pb.TaskActiveListReq{},
// Rsp: &pb.TaskActiveListResp{},
// Enabled: true,
// },
// common.MF(comm.ModuleTask, task.TaskSubTypeActiveReceive): {
// NavLabel: "活跃度领取",
// Desc: "用户活跃度领取",
// MainType: string(comm.ModuleTask),
// SubType: task.TaskSubTypeActiveReceive,
// Req: &pb.TaskActiveReceiveReq{},
// Rsp: &pb.TaskActiveReceiveResp{},
// Enabled: true,
// },
// hero
// string(comm.ModuleHero): {
// NavLabel: "英雄",
// MainType: string(comm.ModuleHero),
// Enabled: true,
// // },
// common.MF(comm.ModuleHero, hero.HeroSubTypeList): {
// NavLabel: "英雄列表",
// Desc: "英雄列表",
// MainType: string(comm.ModuleHero),
// SubType: hero.HeroSubTypeList,
// Print: func(rsp proto.Message) string {
// logrus.WithFields(logrus.Fields{"main": comm.ModuleHero, "stype": hero.HeroSubTypeList}).Debug("print")
// var formatStr strings.Builder
// if in, ok := rsp.(*pb.UserMessage); ok {
// out := &pb.HeroListResp{}
// if !comm.ProtoUnmarshal(in, out) {
// return errors.New("unmarshal err").Error()
// }
// for i, v := range out.List {
// formatStr.WriteString(fmt.Sprintf("%d- %v\n", (i + 1), v))
// }
// }
// return formatStr.String()
// },
// Enabled: true,
// },
// common.MF(comm.ModuleHero, hero.StrengthenUplv): {
// NavLabel: "英雄升级",
// Desc: "英雄等级升级",
// MainType: string(comm.ModuleHero),
// SubType: hero.StrengthenUplv,
// Req: &pb.HeroStrengthenUplvReq{},
// Rsp: &pb.HeroStrengthenUplvResp{},
// Enabled: true,
// },
// common.MF(comm.ModuleHero, hero.StrengthenUpStar): {
// NavLabel: "英雄升星",
// Desc: "英雄星级升级",
// MainType: string(comm.ModuleHero),
// SubType: hero.StrengthenUpStar,
// Req: &pb.HeroStrengthenUpStarReq{},
// Rsp: &pb.HeroStrengthenUpStarResp{},
// Enabled: true,
// },
// common.MF(comm.ModuleHero, hero.DrawCard): {
// NavLabel: "招募",
// Desc: "抽卡招募",
// MainType: string(comm.ModuleHero),
// SubType: hero.DrawCard,
// Enabled: true,
// },
// common.MF(comm.ModuleHero, "info"): {
// NavLabel: "英雄信息",
// Desc: "英雄信息",
// MainType: string(comm.ModuleHero),
// SubType: "info",
// Enabled: true,
// },
//equipment
// string(comm.ModuleEquipment): {
// NavLabel: "装备",
// MainType: string(comm.ModuleEquipment),
// Enabled: true,
// },
// common.MF(comm.ModuleEquipment, "getlist"): {
// NavLabel: "装备列表",
// Desc: "装备列表",
// MainType: string(comm.ModuleEquipment),
// SubType: "getlist",
// Enabled: true,
// },
// common.MF(comm.ModuleEquipment, "equip"): {
// NavLabel: "穿卸装备",
// Desc: "穿/卸装备",
// MainType: string(comm.ModuleEquipment),
// SubType: "equip",
// Enabled: true,
// },
// common.MF(comm.ModuleEquipment, "upgrade"): {
// NavLabel: "升级装备",
// Desc: "升级装备",
// MainType: string(comm.ModuleEquipment),
// SubType: "upgrade",
// Enabled: true,
// },
//friend
// string(comm.ModuleFriend): {
// NavLabel: "好友",
// MainType: string(comm.ModuleFriend),
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeRandList): {
// NavLabel: "在线玩家",
// Desc: "在线玩家列表",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeRandList,
// Enabled: true
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeList): {
// NavLabel: "我的好友",
// Desc: "我的好友列表",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeList,
// Req: &pb.FriendListReq{},
// Rsp: &pb.FriendListResp{},
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeApply): {
// NavLabel: "好友申请",
// Desc: "好友申请",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeApply,
// Req: &pb.FriendApplyReq{},
// Rsp: &pb.FriendApplyResp{},
// Enabled: false,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeBlacklist): {
// NavLabel: "黑名单",
// Desc: "黑名单列表",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeBlacklist,
// Req: &pb.FriendBlackListReq{},
// Rsp: &pb.FriendBlackListResp{},
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAddBlack): {
// NavLabel: "加黑名单",
// Desc: "添加黑名单",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeAddBlack,
// Req: &pb.FriendAddBlackReq{},
// Rsp: &pb.FriendAddBlackResp{},
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeDelBlack): {
// NavLabel: "删黑名单",
// Desc: "删除黑名单",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeDelBlack,
// Req: &pb.FriendDelBlackReq{},
// Rsp: &pb.FriendDelBlackResp{},
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAgree): {
// NavLabel: "好友审批",
// Desc: "好友申请审批",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeAgree,
// Req: &pb.FriendAgreeReq{},
// Rsp: &pb.FriendAgreeResp{},
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeSearch): {
// NavLabel: "好友搜索",
// Desc: "搜索好友",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeSearch,
// Req: &pb.FriendSearchReq{},
// Rsp: &pb.FriendSearchResp{},
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAssistHero): {
// NavLabel: "助战英雄",
// Desc: "助战英雄",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeAssistHero,
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAssistlist): {
// NavLabel: "助战列表",
// Desc: "助战英雄列表",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeAssistlist,
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeGetreward): {
// NavLabel: "助战奖励",
// Desc: "助战奖励",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeGetreward,
// Enabled: true,
// },
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAssistHeroList): {
// NavLabel: "助战英雄列表",
// Desc: "我的好友助战英雄列表",
// MainType: string(comm.ModuleFriend),
// SubType: friend.FriendSubTypeAssistHeroList,
// Enabled: true,
// },
// //shop
// string(comm.ModuleShop): {
// NavLabel: "商店",
// MainType: string(comm.ModuleShop),
// Enabled: true,
// },
// common.MF(comm.ModuleShop, "getlist"): {
// NavLabel: "商店列表",
// Desc: "商店列表",
// MainType: string(comm.ModuleShop),
// SubType: "getlist",
// Enabled: true,
// },
// common.MF(comm.ModuleShop, "buy"): {
// NavLabel: "购买商品",
// Desc: "购买商品",
// MainType: string(comm.ModuleShop),
// SubType: "buy",
// Enabled: true,
// },
// mainline
// string(comm.ModuleMline): {
// NavLabel: "主线关卡",
// MainType: string(comm.ModuleShop),
// Enabled: true,
// },
// common.MF(comm.ModuleMline, mline.MlineGetListResp): {
// NavLabel: "主线关卡",
// Desc: "主线关卡",
// MainType: string(comm.ModuleMline),
// SubType: mline.MlineGetListResp,
// Enabled: true,
// },
// common.MF(comm.ModuleMline, mline.MlineChallengeResp): {
// NavLabel: "关卡挑战",
// Desc: "关卡挑战",
// MainType: string(comm.ModuleMline),
// SubType: mline.MlineChallengeResp,
// Enabled: true,
// },
// common.MF(comm.ModuleMline, mline.MlineGetRewardResp): {
// NavLabel: "关卡奖励",
// Desc: "获取关卡奖励",
// MainType: string(comm.ModuleMline),
// SubType: mline.MlineGetRewardResp,
// Enabled: true,
// },
// //pagoda
// string(comm.ModulePagoda): {
// NavLabel: "魔塔",
// MainType: string(comm.ModulePagoda),
// Enabled: true,
// },
// common.MF(comm.ModulePagoda, "getlist"): {
// NavLabel: "魔塔列表",
// Desc: "魔塔列表",
// MainType: string(comm.ModulePagoda),
// SubType: "getlist",
// Enabled: true,
// },
// rtask
// string(comm.ModuleRtask): {
// NavLabel: "随机任务",
// MainType: string(comm.ModuleRtask),
// Enabled: true,
// },
// common.MF(comm.ModuleRtask, "rtest"): {
// NavLabel: "测试条件",
// Desc: "测试任务触发",
// MainType: string(comm.ModuleRtask),
// SubType: "rtest",
// Enabled: true,
// },
//linestory
// string(comm.ModuleLinestory): {
// NavLabel: "支线剧情任务",
// MainType: string(comm.ModuleLinestory),
// Enabled: true,
// },
// common.MF(comm.ModuleLinestory, linestory.LinestorySubTypeChapter): {
// NavLabel: "支线任务",
// Desc: "我的剧情任务",
// MainType: string(comm.ModuleLinestory),
// SubType: linestory.LinestorySubTypeChapter,
// Enabled: true,
// },
// common.MF(comm.ModuleLibrary, library.LibraryFetterstoryTaskResp): {
// NavLabel: "羁绊任务",
// Desc: "我的剧情任务",
// MainType: string(comm.ModuleLibrary),
// SubType: library.LibraryFetterstoryTaskResp,
// Enabled: true,
// },
// // gourmet
// string(comm.ModuleGourmet): {
// NavLabel: "美食馆",
// MainType: string(comm.ModuleGourmet),
// Enabled: true,
// },
// common.MF(comm.ModuleGourmet, "getranduser"): {
// NavLabel: "随机玩家",
// Desc: "随机玩家",
// MainType: string(comm.ModuleGourmet),
// SubType: "getranduser",
// Enabled: true,
// },
// sociaty
// string(comm.ModuleSociaty): {
// NavLabel: "公会",
// MainType: string(comm.ModuleSociaty),
// Enabled: true,
// },
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeCreate): {
// NavLabel: "公会创建",
// Desc: "公会创建",
// MainType: string(comm.ModuleSociaty),
// SubType: sociaty.SociatySubTypeCreate,
// Enabled: true,
// },
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeList): {
// NavLabel: "公会推荐",
// Desc: "公会列表",
// MainType: string(comm.ModuleSociaty),
// SubType: sociaty.SociatySubTypeList,
// Enabled: true,
// Print: func(rsp proto.Message) string {
// return "不打印"
// },
// },
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeMine): {
// NavLabel: "我的公会",
// Desc: "我的公会",
// MainType: string(comm.ModuleSociaty),
// SubType: sociaty.SociatySubTypeMine,
// Enabled: true,
// },
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeTasklist): {
// NavLabel: "任务列表",
// Desc: "任务列表",
// MainType: string(comm.ModuleSociaty),
// SubType: sociaty.SociatySubTypeTasklist,
// Enabled: true,
// },
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeRank): {
// NavLabel: "排行榜",
// Desc: "排行榜",
// MainType: string(comm.ModuleSociaty),
// SubType: sociaty.SociatySubTypeRank,
// Enabled: true,
// },
// common.MF(comm.ModuleSociaty, "boss"): {
// NavLabel: "BOSS",
// Desc: "BOSS",
// MainType: string(comm.ModuleSociaty),
// SubType: "boss",
// Enabled: true,
// },
//troll
// string(comm.ModuleTroll): {
// NavLabel: "巨兽",
// MainType: string(comm.ModuleTroll),
// Enabled: true,
// },
// common.MF(comm.ModuleTroll, "getlist"): {
// NavLabel: "列表",
// Desc: "巨兽列表",
// MainType: string(comm.ModuleTroll),
// SubType: "getlist",
// Enabled: true,
// },
// growtask
// string(comm.ModuleGrowtask): {
// NavLabel: "成长任务",
// MainType: string(comm.ModuleGrowtask),
// Enabled: true,
// },
// common.MF(comm.ModuleGrowtask, growtask.GrowtaskSubTypeList): {
// NavLabel: "列表",
// Desc: "成长任务列表",
// MainType: string(comm.ModuleGrowtask),
// SubType: growtask.GrowtaskSubTypeList,
// Enabled: true,
// },
// worldtask
// string(comm.ModuleWorldtask): {
// NavLabel: "世界任务",
// MainType: string(comm.ModuleWorldtask),
// Enabled: true,
// },
// common.MF(comm.ModuleWorldtask, worldtask.WorldtaskSubtypeMine): {
// NavLabel: "我的任务",
// Desc: "世界任务剧情",
// MainType: string(comm.ModuleWorldtask),
// SubType: worldtask.WorldtaskSubtypeMine,
// Enabled: true,
// },
// common.MF(comm.ModuleWorldtask, "check"): {
// NavLabel: "模拟",
// Desc: "世界任务自动执行模拟",
// MainType: string(comm.ModuleWorldtask),
// SubType: "check",
// Enabled: true,
// },
common.MF("worldtask", "test"): {
NavLabel: "触发条件",
Desc: "触发条件",
MainType: "worldtask",
SubType: "test",
Enabled: true,
},
// smithy
string("smithy"): {
NavLabel: "铁匠铺",
MainType: string("smithy"),
Enabled: true,
},
common.MF("smithy", "tasklist"): {
NavLabel: "任务",
Desc: "当前铁匠铺中的顾客",
MainType: string("smithy"),
SubType: "tasklist",
Enabled: true,
},
// dispatch
// string(comm.ModuleDispatch): {
// NavLabel: "武馆派遣",
// MainType: string(comm.ModuleDispatch),
// Enabled: true,
// },
// common.MF(comm.ModuleDispatch, "dispatch"): {
// NavLabel: "派遣",
// Desc: "派遣",
// MainType: string(comm.ModuleDispatch),
// SubType: "dispatch",
// Enabled: true,
// },
// common.MF(comm.ModulePractice, "practice"): {
// NavLabel: "练功",
// Desc: "练功",
// MainType: string(comm.ModulePractice),
// SubType: "practice",
// Enabled: true,
// },
// reputation
// string(comm.ModuleReputation): {
// NavLabel: "声望",
// MainType: string(comm.ModuleReputation),
// Enabled: true,
// },
// common.MF(comm.ModuleReputation, "reputation"): {
// NavLabel: "声望管理",
// Desc: "声望管理",
// MainType: string(comm.ModuleReputation),
// SubType: "reputation",
// Enabled: true,
// },
// oldtimes
// string(comm.ModuleOldtimes): {
// NavLabel: "旧时光",
// MainType: string(comm.ModuleOldtimes),
// Enabled: true,
// },
// common.MF(comm.ModuleOldtimes, "oldtimes"): {
// NavLabel: "关卡",
// Desc: "关卡编辑器",
// MainType: string(comm.ModuleOldtimes),
// SubType: "oldtimes",
// Enabled: true,
// },
}
)

View File

@ -0,0 +1,32 @@
package pb
import (
// "go_dreamfactory/cmd/v2/lib/common"
// "go_dreamfactory/cmd/v2/model"
// "go_dreamfactory/comm"
// "go_dreamfactory/pb"
)
var (
// TaskCase = map[string]*model.TestCase{
// common.MF(comm.ModuleAcademy, "receive"): {
// NavLabel: "联盟学院 领取奖励",
// Desc: "新手训练营",
// MainType: string(comm.ModuleAcademy),
// SubType: "receive",
// Enabled: true,
// Req: &pb.AcademyReceiveReq{},
// Rsp: &pb.AcademyReceiveResp{},
// },
// }
//worldtask
// WorldtaskCase = map[string]*model.TestCase{
// common.MF(comm.ModuleWorldtask, "accept"): {
// NavLabel: "接取任务",
// MainType: string(comm.ModuleWorldtask),
// SubType: "accept",
// Enabled: true,
// },
// }
)

56
cmd/v2/lib/quen.go Normal file
View File

@ -0,0 +1,56 @@
package lib
import (
"fmt"
"sync"
)
type Queue[T any] struct {
data []T
lock sync.RWMutex
}
func NewQueue[T any]() *Queue[T] {
return &Queue[T]{data: make([]T, 0)}
}
func (this *Queue[T]) Add(obj T) {
this.lock.Lock()
defer this.lock.Unlock()
this.data = append(this.data, obj)
}
func (this *Queue[T]) List() []T {
this.lock.RLock()
defer this.lock.RUnlock()
list := make([]T, 0, len(this.data))
for _, v := range this.data {
list = append(list, v)
}
return list
}
func (this *Queue[T]) Get(index int) (T, bool) {
this.lock.RLock()
defer this.lock.RUnlock()
var ret T
if index < 0 || index >= len(this.data) {
return ret, false
}
return this.data[index], true
}
func (this *Queue[T]) Pop() (T, error) {
this.lock.Lock()
defer this.lock.Unlock()
var ret T
if len(this.data) == 0 {
return ret, fmt.Errorf("not found")
}
pop := this.data[0]
this.data = this.data[1:]
return pop, nil
}

View File

@ -0,0 +1,110 @@
package storage
import "go_dreamfactory/pb"
// 默认配置
func NewDefaultConfig() *Config {
return &Config{
Pressure: PressureConfig{
TimeoutMs: 50,
Concurrency: 10,
DurationS: 5,
},
UserCount: 100,
UpgradeUrl: "http://10.0.0.9:8080/",
Servers: []*ServerConfig{
{
SId: "df01",
Url: "ws://119.3.89.14:9891/gateway",
Name: "外网",
},
{
SId: "df01",
Url: "ws://10.0.0.9:7891/gateway",
Name: "测试服",
},
},
}
}
type Config struct {
Pressure PressureConfig `json:"Pressure,omitempty"`
UserCount int32 `json:"UserCount,omitempty"` //用户数
SId string `json:"sid,omitempty"` //区服ID
WsAddr string `json:"wsAddr,omitempty"` //websocket addr
IntervalS int32 `json:"intervalS,omitempty"` //间隔时间s
LubanConf *LubanConfig `json:"lubanConf,omitempty"` //luban工具配置
PbConf *ProtobufConfig `json:"pbConf,omitempty"` //Pb配置
SyncConf *SyncConfig `json:"syncConf,omitempty"` //同步配置
UpgradeUrl string `json:"upgradeUrl,omitempty"` //升级服务
Servers []*ServerConfig `json:"servers,omitempty"` //区服配置
MgoDB *MgoDB `json:"mgoDB,omitempty"` //MongoDB配置
ServiceDBInfo *pb.ServiceDBInfo `json:"serviceDBInfo,omitempty"` //
JsonDir string `json:"jsonDir,omitempty"` //json配置目录
PingConf *PingConf `json:"pingConf,omitempty"` //ping配置
MultiMgo map[string]*MgoDB `json:"multiMgo,omitempty"` //MongoDBMap
}
type PingConf struct {
Host string
Ports string
}
type MgoDB struct {
Name string `json:"name,omitempty"` //
Host string `json:"host,omitempty"`
Port int32 `json:"port,omitempty"`
User string `json:"user,omitempty"`
Password string `json:"password,omitempty"`
Database string `json:"database,omitempty"`
}
// 压测配置
type PressureConfig struct {
TimeoutMs int32 `json:"timeout,omitempty"` //超时时间 毫秒
Concurrency int32 `json:"concurrency,omitempty"` //并发量
DurationS int32 `json:"duration,omitempty"` //持续时间 秒
}
// luban
type LubanConfig struct {
ServerAddr string
ProjectDir string
WorkDir string
Client string
GenType string
TmpDir string
InputDir string //输入目录
OutputCodeDir string //输出Code目录
OutputJsonDir string //输出json目录
}
type ProtobufConfig struct {
ProtoDir string
OutputDir string
}
type SyncConfig struct {
Ip string
Port string
UserName string
Password string
LocalDir string
RemoteDir string
ServerIp string
WorkDir string
LubanCli string
DataDir string
JsonDir string
SaveDir string //保存目录
LogDir string //远程日志目录
Editor string //编辑器
}
type ServerConfig struct {
SId string
Name string
Url string
}

View File

@ -0,0 +1,29 @@
package storage
import "path/filepath"
const (
storageRootName = "storage"
configFileName = "config.json"
ID = "zhaocy.df"
Version = "zhaocy/df/v1"
)
type Storage interface {
Root() string
ConfigStorage
}
type ConfigStorage interface {
LoadConfig() (*Config, error)
StoreConfig(s *Config) error
}
func storageRootPath(s Storage) string {
return filepath.Join(s.Root(), storageRootName)
}
func configPath(s Storage) string {
return filepath.Join(storageRootPath(s), configFileName)
}

View File

@ -0,0 +1,117 @@
package storage
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)
var _ Storage = (*OSStorage)(nil)
type OSStorage struct {
root string
}
func NewOSStorage() (Storage, error) {
urd, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("不能获取默认的根目录 : %w", err)
}
return NewOSStorageRooted(urd)
}
func NewOSStorageRooted(root string) (Storage, error) {
if !filepath.IsAbs(root) {
return nil, fmt.Errorf("root必须是绝对路径:%s", root)
}
storageRoot := filepath.Join(root, ".df")
s := &OSStorage{root: storageRoot}
mighted, err := s.migrateDeprecatedRootStorage()
if mighted {
if err != nil {
return nil, fmt.Errorf("找到不推荐使用的存储,但无法移动到新位置:%w", err)
}
return s, nil
}
err = s.mkdirIfNotExists(storageRootPath(s))
return s, err
}
func (s *OSStorage) Root() string {
return s.root
}
func (s *OSStorage) LoadConfig() (*Config, error) {
configFile := configPath(s)
f, err := os.Open(configFile)
if os.IsNotExist(err) {
return NewDefaultConfig(), nil
}
if err != nil {
return NewDefaultConfig(), fmt.Errorf("没有读到 URI:%w", err)
}
defer f.Close()
config := &Config{}
err = json.NewDecoder(f).Decode(config)
if err != nil {
return NewDefaultConfig(), err
}
return config, nil
}
func (s *OSStorage) StoreConfig(config *Config) error {
configFile := configPath(s)
w, err := s.createFile(configFile)
if err != nil {
return err
}
defer w.Close()
return json.NewEncoder(w).Encode(config)
}
func (s *OSStorage) createFile(name string) (*os.File, error) {
return os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
}
func (s *OSStorage) isExist(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
func (s *OSStorage) mkdirIfNotExists(dir string) error {
if s.isExist(dir) {
return nil
}
return os.MkdirAll(dir, 0700)
}
// 合并弃用的根目录
func (s *OSStorage) migrateDeprecatedRootStorage() (bool, error) {
oldRoot, err := os.UserConfigDir()
if err != nil {
return false, err
}
src := filepath.Join(oldRoot, "fyne", ID, "vvv")
_, err = os.Stat(src)
if os.IsNotExist(err) {
return false, err
}
dest := storageRootPath(s)
err = os.Rename(src, dest)
if err != nil {
return false, err
}
return true, nil
}

201
cmd/v2/main.go Normal file
View File

@ -0,0 +1,201 @@
package main
import (
"errors"
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/cmd/v2/theme"
"go_dreamfactory/cmd/v2/ui"
"image/color"
"io"
"log"
"os"
"os/exec"
"runtime"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
)
var (
connService service.ConnService
pttService service.PttService
obs = observer.NewObserver()
logger *logrus.Logger
)
func init() {
_ = os.Setenv("FYNE_SCALE", "0.9")
var err error
// initialize logger
if err = setupLogger(); err != nil {
fmt.Println(err)
os.Exit(1)
}
// if err = setupConfig(); err != nil {
// fmt.Println(err)
// os.Exit(1)
// }
if err = setupWsConn(); err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = setupPtt(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func main() {
// create a new ui
app := app.NewWithID("df-toolkit")
app.SetIcon(theme.ResourceAppPng)
appUI, err := ui.NewUI(app, connService, pttService, obs)
if err != nil {
w := fyne.CurrentApp().NewWindow("错误")
w.SetContent(canvas.NewText(err.Error(), color.RGBA{255, 0, 0, 255}))
w.ShowAndRun()
}
// logLifecycle(app)
//创建enter
w := fyne.CurrentApp().NewWindow("传送门")
w.SetIcon(theme.ResourceAppPng)
// 检查版本
if app.Metadata().Build > 1 {
checkVersion(app, w)
}
w.SetContent(container.NewGridWithColumns(2,
widget.NewButton("工具", func() {
toolWindow := ui.NewToolWindow(appUI, w)
toolWindow.CreateWindow(common.APP_NAME, 1499, 800, true)
w.Hide()
}),
widget.NewButton("登服", func() {
mainWindow := ui.NewMainWindow(appUI, w)
mainWindow.CreateWindow(common.APP_NAME, 1499, 800, true)
w.Hide()
}),
// widget.NewButton("自动化测试", func() {
// perfWindow := ui.NewPerfWindow(appUI, w)
// perfWindow.CreateWindow(common.APP_NAME, 800, 600, true)
// w.Hide()
// }),
))
w.SetFixedSize(true)
w.Resize(fyne.NewSize(400, 200))
w.CenterOnScreen()
w.SetCloseIntercept(func() {
app.Quit()
})
logrus.WithField("version", app.Metadata().Version).Debug("app starting")
w.Show()
appUI.Run()
}
func logLifecycle(a fyne.App) {
a.Lifecycle().SetOnStarted(func() {
log.Println("Lifecycle: Started")
})
a.Lifecycle().SetOnStopped(func() {
log.Println("Lifecycle: Stopped")
})
a.Lifecycle().SetOnEnteredForeground(func() {
log.Println("Lifecycle: Entered Foreground")
})
a.Lifecycle().SetOnExitedForeground(func() {
log.Println("Lifecycle: Exited Foreground")
})
}
func setupPtt() (err error) {
pttService = service.NewPttService(connService, obs)
return
}
func setupWsConn() (err error) {
connService = service.NewConnService(obs)
return
}
func setupLogger() (err error) {
logrus.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
// DisableColors: true,
// FullTimestamp: true,
})
logrus.SetLevel(logrus.DebugLevel)
logrus.SetOutput(os.Stdout)
//设置output,默认为stderr,可以为任何io.Writer比如文件*os.File
file, err := os.OpenFile("robot.log", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
writers := []io.Writer{
file,
os.Stdout}
//同时写文件和屏幕
fileAndStdoutWriter := io.MultiWriter(writers...)
if err == nil {
logrus.SetOutput(fileAndStdoutWriter)
} else {
logrus.Fatal("failed to log to file.")
}
return nil
}
// check version
func checkVersion(app fyne.App, parent fyne.Window) {
b, err := connService.HttpConnect("http://10.0.0.9:8080/" + "version")
if err != nil {
dialog.ShowError(errors.New("版本检查:"+err.Error()), parent)
return
}
oldVer := app.Metadata().Version
parent.SetTitle(fmt.Sprintf("传送门 v%v", oldVer))
ver := gjson.Get(string(b), "version").String()
if strings.HasSuffix(ver, ".zip") {
ver = strings.TrimSuffix(ver, ".zip")
}
logrus.WithField("version", ver).Debug("server version")
if common.IsUpgrade(ver, oldVer) {
chkDialog := dialog.NewConfirm("版本检查", "检查到新版本:"+ver+" 请升级后使用", func(b bool) {
var commands = map[string]string{
"windows": "explorer",
"darwin": "open",
"linux": "xdg-open",
}
//open browser
open := func(uri string) error {
// runtime.GOOS获取当前平台
run, ok := commands[runtime.GOOS]
if !ok {
logrus.Errorf("don't know how to open things on %s platform", runtime.GOOS)
}
cmd := exec.Command(run, uri)
return cmd.Run()
}
_ = open("http://10.0.0.9:8080/")
defer parent.Close()
}, parent)
chkDialog.SetConfirmText(common.BUTTON_OK)
chkDialog.Show()
}
}

13
cmd/v2/model/gen.go Normal file
View File

@ -0,0 +1,13 @@
package model
type GenTool struct {
ServerAddr string //服务地址
ProjectDir string //项目目录
TmpDir string //临时目录
Client string //Client 文件路径
WorkDir string //工作目录
InputDir string //输入目录
OutputCodeDir string //输出Code目录
OutputJsonDir string //输出json目录
GenType string //生成类型
}

24
cmd/v2/model/item.go Normal file
View File

@ -0,0 +1,24 @@
package model
type ItemModel struct {
Id string
Label string
}
type ItemModelList struct {
DataList []*ItemModel
}
func NewItemModelList() *ItemModelList {
return &ItemModelList{
DataList: make([]*ItemModel, 0),
}
}
func (s *ItemModelList) AsInterfaceArray() []interface{} {
rs := make([]interface{}, len(s.DataList))
for i, v := range s.DataList {
rs[i] = v
}
return rs
}

7
cmd/v2/model/login.go Normal file
View File

@ -0,0 +1,7 @@
package model
type LoginParam struct {
Account string `json:"account"`
ServerId string `json:"serverId"`
TimeStamp int64 `json:"timestamp"`
}

6
cmd/v2/model/pbconf.go Normal file
View File

@ -0,0 +1,6 @@
package model
type PbConfModel struct {
ProtoDir string //项目目录
OutputDir string //输出目录
}

29
cmd/v2/model/push.go Normal file
View File

@ -0,0 +1,29 @@
package model
import (
"go_dreamfactory/pb"
)
type PushModel struct {
MethodName string
DataTime string
Msg *pb.UserMessage
}
type PushModelList struct {
DataList []*PushModel
}
func NewPushModelList() *PushModelList {
return &PushModelList{
DataList: make([]*PushModel, 0),
}
}
func (s *PushModelList) AsInterfaceArray() []interface{} {
rs := make([]interface{}, len(s.DataList), len(s.DataList))
for i := range s.DataList {
rs[i] = s.DataList[i]
}
return rs
}

21
cmd/v2/model/ssh.go Normal file
View File

@ -0,0 +1,21 @@
package model
type SSHModel struct {
Ip string
Port string
UserName string
Password string
LocalDir string
RemoteDir string
/////
ServerIp string
WorkDir string
LubanCli string
DataDir string
JsonDir string
//
SaveDir string //保存目录
LogDir string //远程日志目录
Editor string //编辑器
}

17
cmd/v2/model/testcase.go Normal file
View File

@ -0,0 +1,17 @@
package model
import "google.golang.org/protobuf/proto"
type TestCase struct {
Id string //用例ID 如果没有指定,会自动赋值uuid
NavLabel string //菜单名称
Desc string //用例描述
MainType string //协议类型 L1
SubType string //协议类型 L2
Req proto.Message //请求类型
Rsp proto.Message //响应类型
Enabled bool //是否启用
// View MyCaseView //视图
Print func(rsp proto.Message) string //定义打印
Sort int32 //顺序号
}

36
cmd/v2/readme.md Normal file
View File

@ -0,0 +1,36 @@
## install tool
<!-- https://sourceforge.net/projects/mingw/files/Installer/mingw-get-setup.exe/download -->
go install fyne.io/fyne/v2/cmd/fyne@latest
## install font
### fyne CLI 打包
fyne bundle msyh.ttc >> bundled.go
-- 打包粗体
fyne bundle -append msyhbd.ttc >> bundled.go
(不要使用powershell 或vscode自带终端)
## icon
1.
将静态资源编译为 go 文件
fyne bundle fav.png >> bundled.go
如果是要追加资源
fyne bundle -append image2.jpg >> bundled.go
2.
a := app.New()
a.SetIcon(resourceFavPng)
## package
fyne package --name robotGUI-1.0.0 -os windows -icon Icon.png
## 开发协议参数表单
1.
2.
3.

14
cmd/v2/robot.log Normal file
View File

@ -0,0 +1,14 @@
{"level":"debug","msg":"app starting","time":"2023-06-09 21:54:05","version":"0.0.1"}
{"level":"debug","msg":"choose server","server":"01区","sid":"dfz01","time":"2023-06-09 21:54:08"}
{"account":"6911","level":"debug","msg":"submit login","time":"2023-06-09 21:54:12"}
{"level":"info","mainType":"user","msg":"登录","params":{"account":"6911","sid":"dfz01","area":0,"channel":0,"vcode":0,"vname":""},"subType":"login","time":"2023-06-09 21:54:12"}
{"idx":2,"lbl":"昵称","level":"debug","msg":"更新Prop","time":"2023-06-09 21:54:12","val":"Piratemountain"}
{"id":"worldtask","level":"warning","msg":"Missing tutorial panel","time":"2023-06-09 21:54:14"}
{"id":"worldtask","level":"warning","msg":"Missing tutorial panel","time":"2023-06-09 21:54:14"}
{"id":"worldtask","level":"warning","msg":"Missing tutorial panel","time":"2023-06-09 21:54:14"}
{"id":"worldtask","level":"warning","msg":"Missing tutorial panel","time":"2023-06-09 21:54:14"}
{"id":"worldtask","level":"warning","msg":"Missing tutorial panel","time":"2023-06-09 21:54:14"}
{"id":"worldtask","level":"warning","msg":"Missing tutorial panel","time":"2023-06-09 21:54:15"}
{"level":"debug","mainType":"smithy","msg":"select","subType":"tasklist","time":"2023-06-09 21:54:15"}
{"level":"error","msg":"readMessage err:read tcp 10.0.0.238:14482-\u003e10.0.0.238:7891: wsarecv: An existing connection was forcibly closed by the remote host.","time":"2023-06-09 21:54:31"}
{"level":"debug","msg":"即将与服务器断开链接","time":"2023-06-09 21:54:32"}

View File

@ -0,0 +1,62 @@
package service
import (
"go_dreamfactory/cmd/v2/lib"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/comm"
"go_dreamfactory/pb"
"testing"
"time"
"github.com/Pallinder/go-randomdata"
"google.golang.org/protobuf/proto"
)
func loginReq() ([]byte, error) {
head := &pb.UserMessage{MainType: "user", SubType: "login"}
sid := "dfz"
account := randomdata.SillyName()
head.Sec = common.BuildSecStr(sid, account)
if comm.ProtoMarshal(&pb.UserLoginReq{
Sid: sid,
Account: account,
}, head) {
data, err := proto.Marshal(head)
if err != nil {
return nil, err
}
return data, nil
}
return nil, nil
}
func TestStart(t *testing.T) {
wsAddr := "ws://10.0.0.238:7891/gateway"
h, err := NewWsCli(wsAddr, 2*time.Second)
if err != nil {
panic(err)
}
b, err := loginReq()
if err != nil {
t.Fatal(err)
}
h.SetReq(b, false)
param := lib.ParamMgr{
Caller: h,
Timeout: 50 * time.Millisecond,
Lps: uint32(1),
Duration: 5 * time.Second,
ResultCh: make(chan *lib.CallResult, 50),
}
a, err := lib.NewAssistant(nil, param)
if err != nil {
t.Fatalf("AI助手初始化错误: %v", err)
t.FailNow()
}
a.Start()
a.ShowResult()
}

View File

@ -0,0 +1,184 @@
package service
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/comm"
"go_dreamfactory/pb"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
)
var (
conn *ConnServiceImpl
)
type ConnService interface {
WsConnect(wsUrl string) error
HttpConnect(url string) ([]byte, error)
SendMsg(msg *pb.UserMessage, rsp proto.Message) (err error)
ReceiveMsg() (errdata *pb.ErrorData, msg *pb.UserMessage)
ListenerPush()
Close()
}
type ConnServiceImpl struct {
ws *websocket.Conn
obs observer.Observer
}
func NewConnService(obs observer.Observer) ConnService {
conn = &ConnServiceImpl{obs: obs}
return conn
}
func GetConnService() *ConnServiceImpl {
return conn
}
// connect ...
func (c *ConnServiceImpl) WsConnect(wsUrl string) error {
dialer := &websocket.Dialer{
HandshakeTimeout: 5 * time.Second,
}
ws, _, err := dialer.Dial(wsUrl, nil)
if err != nil {
logrus.Errorf("websocket conn err:%v", err)
return err
}
c.ws = ws
return nil
}
func (c *ConnServiceImpl) HttpConnect(url string) ([]byte, error) {
res, err := http.Get(url)
if err != nil {
return nil, err
}
defer res.Body.Close()
return ioutil.ReadAll(res.Body)
}
// listener push
func (c *ConnServiceImpl) ListenerPush() {
defer func() {
if err := recover(); err != nil {
logrus.Errorf("listenerPush err: %v", err)
}
}()
go func() {
for {
// time.Sleep(time.Millisecond * 200)
msg := &pb.UserMessage{}
_, data, err := c.ws.ReadMessage()
if err != nil {
logrus.Errorf("readMessage err:%v", err)
return
}
if err = proto.Unmarshal(data, msg); err != nil {
return
}
// logrus.WithFields(
// logrus.Fields{"MainType": msg.MainType, "SubType": msg.SubType},
// ).Debug(msg.Data.TypeUrl)
methodStr := msg.Data.TypeUrl
methodName := common.SubStr(methodStr, 20, len(methodStr))
p := &model.PushModel{
MethodName: methodName,
DataTime: time.Now().Format(time.RFC3339),
Msg: msg,
}
// logrus.WithFields(
// logrus.Fields{"MainType": msg.MainType, "SubType": msg.SubType},
// ).Debug(methodName)
renderRespPanel := func(p *model.PushModel) {
c.obs.Notify(observer.EVENT_REQ_RSP, p.Msg)
}
if strings.HasSuffix(methodName, "Push") {
c.obs.Notify(observer.EVENT_APP_MONI, p)
c.obs.Notify(observer.EVENT_APP_MONI_EX, msg)
c.obs.Notify(observer.EVENT_USER_CHANGE, p)
if methodName == "NotifyErrorNotifyPush" {
renderRespPanel(p)
}
} else {
//render appTestcase respPanel
renderRespPanel(p)
}
}
}()
}
// SendMsg ....
func (c *ConnServiceImpl) SendMsg(msg *pb.UserMessage, rsp proto.Message) (err error) {
defer func() {
if err := recover(); err != nil {
logrus.Errorf("sendMsg %v", err)
}
}()
// msg.Sec = r.BuildSecStr()
if comm.ProtoMarshal(rsp, msg) {
if data, err := proto.Marshal(msg); err != nil {
return err
} else {
return c.ws.WriteMessage(websocket.BinaryMessage, data)
}
}
return
}
// ReceiveMsg ....
func (c *ConnServiceImpl) ReceiveMsg() (errdata *pb.ErrorData, msg *pb.UserMessage) {
msg = &pb.UserMessage{}
_, data, err := c.ws.ReadMessage()
if err != nil {
logrus.Errorf("readMessage err:%v", err)
return
}
if err = proto.Unmarshal(data, msg); err != nil {
return
}
if errdata = c.handleNotify(msg); errdata != nil {
return
}
return
}
// err notify
func (c *ConnServiceImpl) handleNotify(msg *pb.UserMessage) (errdata *pb.ErrorData) {
if msg.MainType == "notify" && msg.SubType == "errornotify" {
rsp := &pb.NotifyErrorNotifyPush{}
if !comm.ProtoUnmarshal(msg, rsp) {
errdata = &pb.ErrorData{
Code: pb.ErrorCode_PbError,
}
return
}
errdata = &pb.ErrorData{
Code: rsp.Code,
}
return
}
return
}
func (c *ConnServiceImpl) Close() {
if err := c.ws.Close(); err != nil {
logrus.Errorf("close ws conn err: %v", err)
}
}

View File

@ -0,0 +1,22 @@
package observer
const (
EVENT_PING = "ping"
//用户详情
EVENT_USERINFO Event = "userinfo"
//协议推送
EVENT_APP_MONI Event = "push"
EVENT_APP_MONI_EX Event = "push_ex"
// 用户数据变化
EVENT_USER_CHANGE Event = "chanage"
//测试用例响应事件
EVENT_REQ_RSP Event = "response"
// 清空 ui数据事件
EVENT_UI_CLEAN Event = "uiclean"
// 请求响应计时
EVENT_RST = "ctime"
// 测试结果
EVENT_RESULT = "result"
EVENT_FINISH = "finish"
)

View File

@ -0,0 +1,56 @@
package observer
import "sync"
type (
Event string
OnNotify func(data interface{}, args ...interface{})
Listener struct {
OnNotify OnNotify
}
Observer interface {
AddListener(event Event, listener Listener)
Remove(event Event)
Notify(event Event, data interface{}, args ...interface{})
}
ObserverImpl struct {
listeners map[Event][]Listener
}
)
var (
obs *ObserverImpl
singletonObserver sync.Once
)
func NewObserver() *ObserverImpl {
singletonObserver.Do(func() {
obs = &ObserverImpl{}
})
return obs
}
func (o *ObserverImpl) AddListener(event Event, listener Listener) {
if o.listeners == nil {
o.listeners = map[Event][]Listener{}
}
o.listeners[event] = append(o.listeners[event], listener)
}
func (o *ObserverImpl) Remove(event Event) {
delete(o.listeners, event)
}
func (o *ObserverImpl) Notify(event Event, data interface{}, args ...interface{}) {
if listeners, ok := o.listeners[event]; !ok {
return
} else {
for _, listener := range listeners {
go listener.OnNotify(data, args...)
}
}
}

View File

@ -0,0 +1,124 @@
package service
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service/observer"
// "go_dreamfactory/modules/user"
"go_dreamfactory/pb"
"time"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
)
var (
ptt PttService
)
type PttService interface {
Login(sid, account string) (errdata *pb.ErrorData)
CreateRole(nickName string, gender, figure int32) (errdata *pb.ErrorData)
GetUser() *UserInfo
SetUser(dbUser *pb.DBUser, dbUserExpand *pb.DBUserExpand)
SendToClient(mainType, subType string, rsp proto.Message) error
Ping(sid, account string)
}
type PttServiceImpl struct {
connService ConnService
user *UserInfo
obs observer.Observer
}
type UserInfo struct {
DbUser *pb.DBUser
DbUserExpand *pb.DBUserExpand
}
func NewPttService(connService ConnService, obs observer.Observer) PttService {
ptt = &PttServiceImpl{
connService: connService,
obs: obs,
}
return ptt
}
func GetPttService() PttService {
return ptt
}
func (p *PttServiceImpl) GetUser() *UserInfo {
return p.user
}
func (p *PttServiceImpl) SetUser(dbUser *pb.DBUser, dbUserExpand *pb.DBUserExpand) {
p.user = &UserInfo{DbUser: dbUser, DbUserExpand: dbUserExpand}
}
func (p *PttServiceImpl) SendToClient(mainType, subType string, rsp proto.Message) (err error) {
msg := &pb.UserMessage{MainType: mainType, SubType: subType}
msg.Sec = common.BuildSecStr(p.user.DbUser.Sid, p.user.DbUser.Binduid)
now := time.Now()
if err = p.connService.SendMsg(msg, rsp); err != nil {
logrus.WithField("发送错误:", err).Error(err)
return err
}
p.obs.Notify(observer.EVENT_RST, now)
return
}
// Login
func (p *PttServiceImpl) Login(sid, account string) (errdata *pb.ErrorData) {
head := &pb.UserMessage{MainType: string("user"), SubType: "login"}
head.Sec = common.BuildSecStr(sid, account)
req := &pb.UserLoginReq{
Account: account,
Sid: sid,
}
if err := p.connService.SendMsg(head, req); err != nil {
errdata = &pb.ErrorData{
Code: pb.ErrorCode_SystemError,
}
logrus.WithField("err", err).Error("Login")
return
}
logrus.WithFields(logrus.Fields{common.MainType: string("user"), common.SubType: "login",
common.Params: req}).Info("登录")
return
}
func (p *PttServiceImpl) Ping(sid, account string) {
defer func() {
if err := recover(); err != nil {
logrus.Errorf("Ping sid:%v account:%v err:%v", sid, account, err)
}
}()
head := &pb.UserMessage{MainType: string("gateway"), SubType: "heartbeat"}
head.Sec = common.BuildSecStr(sid, account)
if err := p.connService.SendMsg(head, &pb.GatewayHeartbeatReq{}); err != nil {
p.obs.Notify(observer.EVENT_PING, err)
return
}
// logrus.Debug("Ping")
return
}
// create role
func (p *PttServiceImpl) CreateRole(nickName string, gender, figure int32) (errdata *pb.ErrorData) {
head := &pb.UserMessage{MainType: string("user"), SubType: "create"}
head.Sec = common.BuildSecStr(p.user.DbUser.Sid, p.user.DbUser.Binduid)
if err := p.connService.SendMsg(head, &pb.UserCreateReq{
NickName: nickName,
Figure: figure,
Gender: gender,
}); err != nil {
errdata = &pb.ErrorData{
Code: pb.ErrorCode_SystemError,
}
logrus.WithField("err", err).Error("CreateRole")
return
}
return
}

View File

@ -0,0 +1,378 @@
package service
import (
"fmt"
"io"
"io/fs"
"io/ioutil"
"log"
"net"
"os"
"path"
"path/filepath"
"runtime"
"sync"
"time"
"github.com/pkg/errors"
"github.com/pkg/sftp"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
)
var sftpCli *sftp.Client
var sftpOne sync.Once
type SSHService struct {
Client *ssh.Client
LastResult string //执行结果
}
func NewSSHService() *SSHService {
ss := &SSHService{}
return ss
}
func (ss *SSHService) Connect(user, password, host, key string, port int, cipherList []string) error {
var (
auth []ssh.AuthMethod //认证方式
addr string
clientConfig *ssh.ClientConfig
config ssh.Config
err error
)
auth = make([]ssh.AuthMethod, 0)
if key == "" {
// 密码认证
auth = append(auth, ssh.Password(password))
} else {
// 秘钥认证
pemBytes, err := ioutil.ReadFile(key)
if err != nil {
return err
}
var signer ssh.Signer
if password == "" {
signer, err = ssh.ParsePrivateKey(pemBytes)
} else {
signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(password))
}
if err != nil {
return err
}
// 加载秘钥
auth = append(auth, ssh.PublicKeys(signer))
}
// 设置ssh 的配置参数
if len(cipherList) == 0 {
config = ssh.Config{
// 连接所允许的加密算法, go的SSH包允许的算法
Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"},
}
} else {
config = ssh.Config{
Ciphers: cipherList,
}
}
clientConfig = &ssh.ClientConfig{
User: user,
Auth: auth,
Timeout: time.Second * 30,
Config: config,
// 默认密钥不受信任时Go 的 ssh 包会在 HostKeyCallback 里把连接干掉1.8 之后加的应该)。但是我们使用用户名密码连接的时候,这个太正常了,所以让他 return nil 就好了
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
addr = fmt.Sprintf("%s:%d", host, port)
if ss.Client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
return err
}
sftpCli, err = sftp.NewClient(ss.Client)
return nil
}
func (ss *SSHService) getSftp() (*sftp.Client, error) {
if ss.Client == nil {
return nil, errors.New("ssh client is nil")
}
var err error
sftpOne.Do(func() {
if sftpCli, err = sftp.NewClient(ss.Client); err != nil {
return
}
})
return sftpCli, nil
}
func (ss *SSHService) Close() {
if ss.Client != nil {
if err := ss.Client.Close(); err != nil {
logrus.WithField("err", err).Error("close ssh err")
}
}
// if sftpCli != nil {
// if err := sftpCli.Close(); err != nil {
// logrus.WithField("err", err).Error("close sftp err")
// }
// }
}
func (ss *SSHService) RunShell(shell string) {
var (
session *ssh.Session
err error
)
//获取session这个session是用来远程执行操作的
if session, err = ss.Client.NewSession(); err != nil {
logrus.Errorf("error newsession:%v", err)
}
// 使用 session.Shell() 模拟终端时,所建立的终端参数
modes := ssh.TerminalModes{
ssh.ECHO: 0, //disable echoing
ssh.TTY_OP_ISPEED: 14400, //input speed=14.4kbaud
ssh.TTY_OP_OSPEED: 14400,
}
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
logrus.Error(err)
}
//执行shell
if output, err := session.CombinedOutput(shell); err != nil {
logrus.Errorf("error CombinedOutput:%v", err)
} else {
ss.LastResult = string(output)
}
}
//单个copy
func (ss *SSHService) ScpCopy(localFilePath, remoteDir string) error {
var (
err error
)
fi, err := os.Stat(localFilePath)
if err != nil {
return err
}
if fi.IsDir() {
return errors.New(localFilePath + " is not file")
}
sftpCli, err = ss.getSftp()
if err != nil {
return fmt.Errorf("new sftp client error: %w", err)
}
// defer sftpCli.Close()
srcFile, err := os.Open(localFilePath)
if err != nil {
log.Println("scpCopy:", err)
return err
}
defer srcFile.Close()
var remoteFileName string
sysTyep := runtime.GOOS
if sysTyep == "windows" {
remoteFileName = path.Base(filepath.ToSlash(localFilePath))
} else {
remoteFileName = path.Base(localFilePath)
}
dstFile, err := sftpCli.Create(path.Join(remoteDir, remoteFileName))
if err != nil {
log.Println("scpCopy:", err)
return err
}
defer dstFile.Close()
fileByte, err := ioutil.ReadAll(srcFile)
if nil != err {
return fmt.Errorf("read local file failed: %w", err)
}
if _, err := dstFile.Write(fileByte); err != nil {
return err
}
return nil
}
//批量copy
func (ss *SSHService) BatchScpCoy(cfs []CopyFiles, remoteDir string) error {
var err error
sftpCli, err = ss.getSftp()
if err != nil {
return fmt.Errorf("new sftp client error: %w", err)
}
for _, f := range cfs {
srcFile, err := os.Open(path.Join(f.Dir, f.FileName))
if err != nil {
return err
}
defer srcFile.Close()
dstFile, err := sftpCli.Create(path.Join(remoteDir, f.FileName))
if err != nil {
logrus.Error("scpCopy:", err)
return err
}
defer dstFile.Close()
fileByte, err := ioutil.ReadAll(srcFile)
if nil != err {
return fmt.Errorf("read local file failed: %w", err)
}
if _, err := dstFile.Write(fileByte); err != nil {
return err
}
}
return nil
}
//Deprecated Scp
func (ss *SSHService) Scp(targetDir, srcFileName string) (int, error) {
sftpClient, err := sftp.NewClient(ss.Client)
if err != nil {
return 0, fmt.Errorf("new sftp client error: %w", err)
}
defer sftpClient.Close()
// source, err := sftpClient.Open(srcFileName)
// if err != nil {
// return 0, fmt.Errorf("sftp client open src file error: %w", err)
// }
// defer source.Close()
srcFile, err := os.Open(srcFileName)
if err != nil {
return 0, fmt.Errorf("open local file error: %w", err)
}
defer srcFile.Close()
var remoteFileName = path.Base(srcFileName)
dstFile, err := sftpClient.Create(path.Join(targetDir, remoteFileName))
if err != nil {
fmt.Errorf("scpCopy:%v", err)
return 0, err
}
// n, err := io.Copy(target, source)
if err != nil {
return 0, fmt.Errorf("copy file error: %w", err)
}
defer dstFile.Close()
buf := make([]byte, 1024)
n := 0
for {
n, _ = srcFile.Read(buf)
if n == 0 {
break
}
dstFile.Write(buf[0:n])
}
return n, nil
}
//Download
func (ss *SSHService) ScpDownload(localDir, remoteFilePath string) error {
var err error
sftpCli, err = ss.getSftp()
if err != nil {
return fmt.Errorf("new sftp client error: %w", err)
}
remoteFile, err := sftpCli.Open(remoteFilePath)
if err != nil {
log.Println("scpCopy:", err)
return err
}
defer remoteFile.Close()
fileName := path.Base(remoteFile.Name())
if err := os.MkdirAll(localDir, fs.ModePerm); err != nil {
logrus.Error(err)
return err
}
target, err := os.OpenFile(localDir+fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fs.ModePerm)
if err != nil {
return fmt.Errorf("open local file error: %w", err)
}
defer target.Close()
_, err = io.Copy(target, remoteFile)
if err != nil {
return fmt.Errorf("write file error: %w", err)
}
return nil
}
func (ss *SSHService) GetRemoteDir(remoteDir string) (files []File, err error) {
sftpCli, err = ss.getSftp()
if err != nil {
return nil, fmt.Errorf("new sftp client error: %w", err)
}
remoteFiles, err := sftpCli.ReadDir(remoteDir)
if err != nil {
log.Println("read remote Dir:", err)
return nil, err
}
for _, f := range remoteFiles {
fi := File{
FileName: f.Name(),
FilePath: filepath.Join(remoteDir, f.Name()),
Size: f.Size(),
}
files = append(files, fi)
// logrus.WithFields(logrus.Fields{"name": f.Name(), "size": f.Size()}).Debug("远程日志文件")
}
return
}
func (ss *SSHService) BatchScpDownload(localDir, remoteDir string) error {
var err error
sftpCli, err = ss.getSftp()
if err != nil {
return fmt.Errorf("new sftp client error: %w", err)
}
remoteFiles, err := sftpCli.ReadDir(remoteDir)
if err != nil {
log.Println("read remote Dir:", err)
return err
}
for _, f := range remoteFiles {
if err := ss.ScpDownload(localDir, filepath.Join(remoteDir, f.Name())); err != nil {
return err
}
}
return nil
}
type CopyFiles struct {
Dir string
FileName string
}
type File struct {
FileName string
FilePath string
Size int64
}

View File

@ -0,0 +1,87 @@
package service
import (
"fmt"
"log"
"path"
"path/filepath"
"testing"
)
const (
username = "root"
password = "Legu.cc()123"
ip = "10.0.0.9"
port = 22
cmd = "ls"
cmds = "cd /home/liwei/go_dreamfactory&&git pull&&exit" //exit结尾,否则程序不会退出
)
func TestRunShell(t *testing.T) {
ciphers := []string{}
ssh := &SSHService{}
err := ssh.Connect(username, password, ip, "", port, ciphers)
if err != nil {
t.Fatal(err)
}
// ssh.RunShell("cd /opt")
ssh.RunShell(cmds)
}
func TestScp(t *testing.T) {
ciphers := []string{}
ssh := &SSHService{}
err := ssh.Connect(username, password, ip, "", port, ciphers)
if err != nil {
t.Fatal(err)
}
path := filepath.Join(`E:/svn/dreamworks/client/dreamworks/ExcelFile/JSON/`, "game_vikingchallenge.json")
fmt.Println(path)
if err := ssh.ScpCopy(path, "/opt/json/"); err != nil {
fmt.Println(err)
}
}
func TestPath(t *testing.T) {
Dir := `E:/svn/dreamworks/client/dreamworks/ExcelFile/JSON/`
mypath := filepath.Join(Dir, "a.json")
s := filepath.ToSlash(mypath)
fmt.Println(path.Base(s))
}
func TestMath(t *testing.T) {
a := float64(10) / float64(3)
fmt.Println(a)
}
func TestScpDown(t *testing.T) {
ciphers := []string{}
ssh := &SSHService{}
err := ssh.Connect(username, password, ip, "", port, ciphers)
if err != nil {
t.Fatal(err)
}
sftpCli, err = ssh.getSftp()
if err != nil {
fmt.Errorf("new sftp client error: %w", err)
}
remoteFiles, err := sftpCli.ReadDir("/home/liwei/go_dreamfactory/bin/log")
if err != nil {
log.Println("scpCopy:", err)
}
for _, v := range remoteFiles {
fmt.Println(v.Name())
if err := ssh.ScpDownload("G:/log/", "/home/liwei/go_dreamfactory/bin/log/"+v.Name()); err != nil {
fmt.Println(err)
}
}
}

229
cmd/v2/service/wsCli.go Normal file
View File

@ -0,0 +1,229 @@
package service
import (
"encoding/base64"
"go_dreamfactory/cmd/v2/lib"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/comm"
"go_dreamfactory/lego/sys/log"
"go_dreamfactory/pb"
"strings"
"sync"
"time"
"github.com/gorilla/websocket"
"github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"google.golang.org/protobuf/proto"
)
type WsCli struct {
addr string
conn *websocket.Conn
// reqData []byte //请求数据
uid string
reqDatas *lib.Queue[[]byte]
conns sync.Map
flag bool
reqData []byte
}
func NewWsCli(addr string, timeout time.Duration) (lib.Handler, error) {
cli := &WsCli{
addr: addr,
reqDatas: lib.NewQueue[[]byte](),
}
// if err := cli.connect(timeout); err != nil {
// return nil, err
// }
return cli, nil
}
func (cli *WsCli) GetConnMap() sync.Map {
return cli.conns
}
func (cli *WsCli) connect(timeout time.Duration) error {
if cli.conn == nil {
dialer := &websocket.Dialer{
HandshakeTimeout: 2 * time.Second,
}
conn, _, err := dialer.Dial(cli.addr, nil)
if err != nil {
logrus.Errorf("websocket conn err:%v", err)
return err
}
cli.conn = conn
}
// ping
// go func() {
// timer := time.NewTimer(2 * time.Second)
// for {
// timer.Reset(2 * time.Second)
// <-timer.C
// if err := cli.ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
// break
// }
// }
// }()
return nil
}
// 检查相应
func (cli *WsCli) checkResp(data []byte) bool {
msg := &pb.UserMessage{}
if err := proto.Unmarshal(data, msg); err != nil {
logrus.Error("结果解析失败")
return false
}
if msg.MainType == "user" && msg.SubType == "login" {
rsp := &pb.UserLoginResp{}
if !comm.ProtoUnmarshal(msg, rsp) {
logrus.Error("unmarshal err")
return false
}
if rsp.Data != nil {
if rsp.Data.Uid != "" {
logrus.WithField("uid", rsp.Data.Uid).Debug("登录响应")
cli.uid = rsp.Data.Uid
return true
}
}
return true
} else {
if cli.uid != "" {
return true
}
}
return false
}
// 检查推送(包括错误推送)
func (cli *WsCli) checkPush(data []byte) bool {
msg := &pb.UserMessage{}
if err := proto.Unmarshal(data, msg); err != nil {
logrus.Error("结果解析失败")
return false
}
methodStr := msg.Data.TypeUrl
methodName := common.SubStr(methodStr, 20, len(methodStr))
if strings.HasSuffix(methodName, "Push") {
if methodName == "NotifyErrorNotifyPush" {
push := &pb.NotifyErrorNotifyPush{}
if !comm.ProtoUnmarshal(msg, push) {
logrus.Error("unmarsh err")
return false
}
logrus.WithField("methodName", methodName).WithField("code", push.Code).Debug("收到错误码")
} else {
logrus.WithField("methodName", methodName).Debug("收到推送")
}
return true
}
return false
}
func (cli *WsCli) SetReq(data []byte, flag bool) {
cli.flag = flag
if flag {
cli.reqData = data
}
cli.reqDatas.Add(data)
}
func (cli *WsCli) BuildReq() lib.RawReq {
id := time.Now().UnixNano()
var (
data []byte
err error
)
data, err = cli.reqDatas.Pop()
if err != nil {
if cli.flag {
data = cli.reqData
} else {
return lib.RawReq{}
}
}
rawReq := lib.RawReq{ID: id, Req: data}
return rawReq
}
func (cli *WsCli) Call(req []byte) ([]byte, error) {
msg := &pb.UserMessage{}
if err := proto.Unmarshal(req, msg); err != nil {
logrus.Error("结果解析失败")
return nil, err
}
base64Str := msg.Sec
dec, err := base64.StdEncoding.DecodeString(base64Str[35:])
if err != nil {
log.Errorf("base64 decode err %v", err)
return nil, err
}
jsonRet := gjson.Parse(string(dec))
account := jsonRet.Get("account").String()
dialer := &websocket.Dialer{
HandshakeTimeout: 2 * time.Second,
}
var conn *websocket.Conn
if c, ok := cli.conns.Load(account); ok {
conn = c.(*websocket.Conn)
} else {
conn, _, err = dialer.Dial(cli.addr, nil)
if err != nil {
logrus.Errorf("websocket conn err:%v", err)
return nil, err
}
}
if msg.MainType == "user" && msg.SubType == "login" {
cli.conns.Store(account, conn)
}
// 向连接写数据
conn.WriteMessage(websocket.BinaryMessage, req)
// 读数据
var res []byte
for {
_, data, err := conn.ReadMessage()
if err != nil {
logrus.Errorf("readMessage err:%v", err)
break
}
// if cli.checkResp(data) {
// return data, nil
// } else {
if !cli.checkPush(data) {
return data, nil
}
// }
}
return res, nil
}
func (cli *WsCli) Check(req lib.RawReq, resp lib.RawResp) *lib.CallResult {
var result lib.CallResult
result.Id = resp.ID
result.Req = req
result.Resp = resp
msg := &pb.UserMessage{}
if err := proto.Unmarshal(resp.Resp, msg); err != nil {
logrus.Error("结果解析失败")
return &result
}
// logrus.WithFields(logrus.Fields{"msg": msg}).Debug("检查结果")
return &result
}

12
cmd/v2/theme/bundled.go Normal file

File diff suppressed because one or more lines are too long

33
cmd/v2/theme/icon.go Normal file

File diff suppressed because one or more lines are too long

BIN
cmd/v2/theme/icon/excel.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

12
cmd/v2/theme/image.go Normal file

File diff suppressed because one or more lines are too long

29
cmd/v2/theme/mytheme.go Normal file
View File

@ -0,0 +1,29 @@
package theme
import (
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
)
type MyTheme struct{}
var _ fyne.Theme = (*MyTheme)(nil)
// return bundled font resource
// ResourceSourceHanSansTtf 即是 bundle.go 文件中 var 的变量名
func (m MyTheme) Font(s fyne.TextStyle) fyne.Resource {
return resourceMsyhTtc
}
func (*MyTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
return theme.DarkTheme().Color(n, v)
}
func (*MyTheme) Icon(n fyne.ThemeIconName) fyne.Resource {
return theme.DarkTheme().Icon(n)
}
func (*MyTheme) Size(n fyne.ThemeSizeName) float32 {
return theme.DarkTheme().Size(n)
}

698
cmd/v2/tpl.txt Normal file
View File

@ -0,0 +1,698 @@
reqDataMap := map[int32]TaskCondi{
101:{ 1,[]int32{ 25001,0,0,0,0 },
102:{ 2,[]int32{ 3,0,0,0,0 },
103:{ 3,[]int32{ 2,0,0,0,0 },
104:{ 4,[]int32{ 14007,10,0,0,0 },
105:{ 5,[]int32{ 25001,2,0,0,0 },
106:{ 6,[]int32{ 25004,5,0,0,0 },
107:{ 7,[]int32{ 1,0,0,0,0 },
108:{ 8,[]int32{ 1,0,0,0,0 },
109:{ 9,[]int32{ 1,0,0,0,0 },
110:{ 10,[]int32{ 2,0,0,0,0 },
111:{ 11,[]int32{ 1,0,0,0,0 },
112:{ 12,[]int32{ 1,0,0,0,0 },
113:{ 13,[]int32{ 2,0,0,0,0 },
114:{ 14,[]int32{ 10,0,0,0,0 },
115:{ 15,[]int32{ 10,0,0,0,0 },
116:{ 16,[]int32{ 5,3,0,0,0 },
117:{ 17,[]int32{ 1,3,0,0,0 },
118:{ 18,[]int32{ 1,0,0,0,0 },
119:{ 19,[]int32{ 2,0,0,0,0 },
120:{ 20,[]int32{ 1,0,0,0,0 },
121:{ 21,[]int32{ 2,0,0,0,0 },
122:{ 22,[]int32{ 25001,0,0,0,0 },
123:{ 23,[]int32{ 3,5,20,0,0 },
124:{ 24,[]int32{ 4,0,0,0,0 },
125:{ 25,[]int32{ 3,25001,6,0,0 },
126:{ 26,[]int32{ 1,0,0,0,0 },
127:{ 27,[]int32{ 1,0,0,0,0 },
128:{ 28,[]int32{ 1,0,0,0,0 },
129:{ 29,[]int32{ 2,22,25004,0,0 },
130:{ 30,[]int32{ 5,3,0,0,0 },
131:{ 31,[]int32{ 4,3,0,0,0 },
132:{ 32,[]int32{ 3,3,20,0,0 },
133:{ 33,[]int32{ 3,3,23,0,0 },
134:{ 34,[]int32{ 5,2,0,0,0 },
135:{ 35,[]int32{ 2,25001,0,0,0 },
136:{ 36,[]int32{ 1,3,3,1,2 },
137:{ 37,[]int32{ 1,3,0,0,0 },
138:{ 38,[]int32{ 1,0,0,0,0 },
139:{ 39,[]int32{ 2,0,0,0,0 },
140:{ 40,[]int32{ 2,2,0,0,0 },
141:{ 41,[]int32{ 2,2,1,0,0 },
142:{ 42,[]int32{ 2,2,2,0,0 },
143:{ 43,[]int32{ 2,2,0,0,0 },
144:{ 44,[]int32{ 3,0,0,0,0 },
145:{ 45,[]int32{ 2,1,1,2,0 },
146:{ 46,[]int32{ 1,4,1,0,0 },
147:{ 47,[]int32{ 2,0,0,0,0 },
148:{ 48,[]int32{ 1,1,2,0,0 },
149:{ 49,[]int32{ 1,1,2,0,0 },
150:{ 50,[]int32{ 1,1,0,0,0 },
151:{ 51,[]int32{ 1,1,0,0,0 },
152:{ 52,[]int32{ 1,14007,0,0,0 },
153:{ 53,[]int32{ 2,0,0,0,0 },
154:{ 54,[]int32{ 1,0,0,0,0 },
155:{ 55,[]int32{ 2,3,0,0,0 },
156:{ 56,[]int32{ 1,1,2,0,0 },
157:{ 57,[]int32{ 1,0,0,0,0 },
158:{ 58,[]int32{ 1,0,0,0,0 },
159:{ 59,[]int32{ 1,0,0,0,0 },
160:{ 60,[]int32{ 1,0,0,0,0 },
161:{ 61,[]int32{ 109,0,0,0,0 },
162:{ 62,[]int32{ 2,0,0,0,0 },
163:{ 63,[]int32{ 1,0,0,0,0 },
164:{ 64,[]int32{ 2,0,0,0,0 },
165:{ 65,[]int32{ 2,10001,0,0,0 },
166:{ 66,[]int32{ 2,1,0,0,0 },
167:{ 67,[]int32{ 1000,1,0,0,0 },
168:{ 68,[]int32{ 1000,1,0,0,0 },
169:{ 69,[]int32{ 1,0,0,0,0 },
170:{ 1,[]int32{ 44006,0,0,0,0 },
171:{ 70,[]int32{ 1,101,0,0,0 },
172:{ 18,[]int32{ 10,0,0,0,0 },
173:{ 71,[]int32{ 1,0,0,0,0 },
174:{ 70,[]int32{ 1,101,0,0,0 },
175:{ 70,[]int32{ 1,102,0,0,0 },
176:{ 70,[]int32{ 1,103,0,0,0 },
177:{ 70,[]int32{ 1,104,0,0,0 },
178:{ 70,[]int32{ 1,105,0,0,0 },
179:{ 70,[]int32{ 1,106,0,0,0 },
180:{ 70,[]int32{ 1,107,0,0,0 },
181:{ 70,[]int32{ 1,108,0,0,0 },
182:{ 70,[]int32{ 1,109,0,0,0 },
183:{ 140,[]int32{ 1,10001,0,0,0 },
184:{ 140,[]int32{ 1,10002,0,0,0 },
185:{ 140,[]int32{ 1,10003,0,0,0 },
186:{ 140,[]int32{ 1,10004,0,0,0 },
187:{ 109,[]int32{ 1,0,0,0,0 },
188:{ 104,[]int32{ 100,0,0,0,0 },
189:{ 70,[]int32{ 1,110,0,0,0 },
190:{ 70,[]int32{ 1,111,1,1,1 },
191:{ 70,[]int32{ 1,112,2,2,2 },
192:{ 70,[]int32{ 1,113,3,3,3 },
193:{ 70,[]int32{ 1,114,4,4,4 },
194:{ 138,[]int32{ 30,0,0,0,0 },
195:{ 140,[]int32{ 1,10005,0,0,0 },
196:{ 140,[]int32{ 1,10006,0,0,0 },
197:{ 140,[]int32{ 1,10007,0,0,0 },
198:{ 140,[]int32{ 1,10008,0,0,0 },
199:{ 140,[]int32{ 1,10009,0,0,0 },
200:{ 140,[]int32{ 1,10010,0,0,0 },
10001:{ 24,[]int32{ 1,0,0,0,0 },
10002:{ 24,[]int32{ 5,0,0,0,0 },
10003:{ 24,[]int32{ 10,0,0,0,0 },
10004:{ 24,[]int32{ 20,0,0,0,0 },
10005:{ 24,[]int32{ 50,0,0,0,0 },
10006:{ 24,[]int32{ 100,0,0,0,0 },
10007:{ 24,[]int32{ 200,0,0,0,0 },
10008:{ 24,[]int32{ 300,0,0,0,0 },
10009:{ 24,[]int32{ 500,0,0,0,0 },
10010:{ 24,[]int32{ 1000,0,0,0,0 },
10101:{ 7,[]int32{ 1,0,0,0,0 },
10201:{ 18,[]int32{ 1,0,0,0,0 },
10202:{ 18,[]int32{ 5,0,0,0,0 },
10203:{ 18,[]int32{ 10,0,0,0,0 },
10204:{ 18,[]int32{ 20,0,0,0,0 },
10205:{ 18,[]int32{ 50,0,0,0,0 },
10206:{ 18,[]int32{ 100,0,0,0,0 },
10207:{ 18,[]int32{ 200,0,0,0,0 },
10208:{ 18,[]int32{ 300,0,0,0,0 },
10209:{ 18,[]int32{ 500,0,0,0,0 },
10210:{ 18,[]int32{ 1000,0,0,0,0 },
10301:{ 18,[]int32{ 1,5,0,0,0 },
10302:{ 50,[]int32{ 5,5,0,0,0 },
10303:{ 50,[]int32{ 10,5,0,0,0 },
10304:{ 50,[]int32{ 20,5,0,0,0 },
10305:{ 50,[]int32{ 50,5,0,0,0 },
10306:{ 50,[]int32{ 100,5,0,0,0 },
10307:{ 50,[]int32{ 200,5,0,0,0 },
10308:{ 50,[]int32{ 300,5,0,0,0 },
10309:{ 50,[]int32{ 500,5,0,0,0 },
10310:{ 50,[]int32{ 1000,5,0,0,0 },
10401:{ 50,[]int32{ 1,6,0,0,0 },
10402:{ 50,[]int32{ 5,6,0,0,0 },
10403:{ 50,[]int32{ 10,6,0,0,0 },
10404:{ 50,[]int32{ 20,6,0,0,0 },
10405:{ 50,[]int32{ 50,6,0,0,0 },
10406:{ 50,[]int32{ 100,6,0,0,0 },
10407:{ 50,[]int32{ 200,6,0,0,0 },
10408:{ 50,[]int32{ 300,6,0,0,0 },
10409:{ 50,[]int32{ 500,6,0,0,0 },
10410:{ 50,[]int32{ 1000,6,0,0,0 },
10501:{ 58,[]int32{ 1,0,0,0,0 },
10502:{ 58,[]int32{ 5,0,0,0,0 },
10503:{ 58,[]int32{ 10,0,0,0,0 },
10504:{ 58,[]int32{ 20,0,0,0,0 },
10505:{ 58,[]int32{ 30,0,0,0,0 },
10506:{ 58,[]int32{ 50,0,0,0,0 },
10507:{ 58,[]int32{ 70,0,0,0,0 },
10508:{ 58,[]int32{ 100,0,0,0,0 },
10601:{ 62,[]int32{ 1,0,0,0,0 },
10602:{ 62,[]int32{ 5,0,0,0,0 },
10603:{ 62,[]int32{ 99,0,0,0,0 },
10701:{ 64,[]int32{ 1,0,0,0,0 },
10702:{ 64,[]int32{ 5,0,0,0,0 },
10703:{ 64,[]int32{ 10,0,0,0,0 },
10704:{ 64,[]int32{ 20,0,0,0,0 },
10705:{ 64,[]int32{ 50,0,0,0,0 },
10706:{ 64,[]int32{ 100,0,0,0,0 },
10707:{ 64,[]int32{ 200,0,0,0,0 },
10708:{ 64,[]int32{ 300,0,0,0,0 },
10709:{ 64,[]int32{ 500,0,0,0,0 },
10710:{ 64,[]int32{ 1000,0,0,0,0 },
10801:{ 8,[]int32{ 1,0,0,0,0 },
10802:{ 8,[]int32{ 5,0,0,0,0 },
10803:{ 8,[]int32{ 10,0,0,0,0 },
10804:{ 8,[]int32{ 20,0,0,0,0 },
10805:{ 8,[]int32{ 50,0,0,0,0 },
10806:{ 8,[]int32{ 100,0,0,0,0 },
10807:{ 8,[]int32{ 200,0,0,0,0 },
10808:{ 8,[]int32{ 300,0,0,0,0 },
10809:{ 8,[]int32{ 500,0,0,0,0 },
10810:{ 18,[]int32{ 1000,0,0,0,0 },
2008001:{ 8,[]int32{ 1,0,0,0,0 },
2012001:{ 12,[]int32{ 1,0,0,0,0 },
2018001:{ 18,[]int32{ 1,0,0,0,0 },
2024001:{ 24,[]int32{ 1,0,0,0,0 },
2047001:{ 47,[]int32{ 1,0,0,0,0 },
2062001:{ 62,[]int32{ 1,0,0,0,0 },
2064001:{ 64,[]int32{ 1,0,0,0,0 },
2130001:{ 130,[]int32{ 3,0,0,0,0 },
3008001:{ 8,[]int32{ 10,0,0,0,0 },
3012001:{ 12,[]int32{ 10,0,0,0,0 },
3018001:{ 18,[]int32{ 5,0,0,0,0 },
3024001:{ 24,[]int32{ 5,0,0,0,0 },
3047001:{ 47,[]int32{ 7,0,0,0,0 },
3061001:{ 62,[]int32{ 7,0,0,0,0 },
3064001:{ 64,[]int32{ 7,0,0,0,0 },
3088001:{ 88,[]int32{ 3,0,0,0,0 },
3104001:{ 104,[]int32{ 1000,0,0,0,0 },
3105001:{ 105,[]int32{ 10,0,0,0,0 },
3130001:{ 130,[]int32{ 30,0,0,0,0 },
4008001:{ 8,[]int32{ 3,0,0,0,0 },
4008002:{ 8,[]int32{ 7,0,0,0,0 },
4008003:{ 8,[]int32{ 30,0,0,0,0 },
4008004:{ 8,[]int32{ 90,0,0,0,0 },
4008005:{ 8,[]int32{ 180,0,0,0,0 },
4008006:{ 8,[]int32{ 365,0,0,0,0 },
4008007:{ 9,[]int32{ 3,0,0,0,0 },
4008008:{ 9,[]int32{ 7,0,0,0,0 },
4008009:{ 9,[]int32{ 14,0,0,0,0 },
4008010:{ 9,[]int32{ 30,0,0,0,0 },
4010001:{ 10,[]int32{ 1,0,0,0,0 },
4010002:{ 10,[]int32{ 5,0,0,0,0 },
4010003:{ 10,[]int32{ 10,0,0,0,0 },
4010004:{ 10,[]int32{ 20,0,0,0,0 },
4010005:{ 10,[]int32{ 30,0,0,0,0 },
4010006:{ 10,[]int32{ 50,0,0,0,0 },
4011001:{ 11,[]int32{ 5,0,0,0,0 },
4011002:{ 11,[]int32{ 10,0,0,0,0 },
4011003:{ 11,[]int32{ 30,0,0,0,0 },
4011004:{ 11,[]int32{ 50,0,0,0,0 },
4011005:{ 11,[]int32{ 100,0,0,0,0 },
4011006:{ 11,[]int32{ 300,0,0,0,0 },
4011007:{ 11,[]int32{ 500,0,0,0,0 },
4011008:{ 11,[]int32{ 1000,0,0,0,0 },
4011009:{ 11,[]int32{ 3000,0,0,0,0 },
4011010:{ 11,[]int32{ 5000,0,0,0,0 },
4011011:{ 11,[]int32{ 10000,0,0,0,0 },
4012001:{ 12,[]int32{ 1,0,0,0,0 },
4012002:{ 12,[]int32{ 5,0,0,0,0 },
4012003:{ 12,[]int32{ 10,0,0,0,0 },
4012004:{ 12,[]int32{ 30,0,0,0,0 },
4012005:{ 12,[]int32{ 50,0,0,0,0 },
4012006:{ 12,[]int32{ 100,0,0,0,0 },
4012007:{ 12,[]int32{ 300,0,0,0,0 },
4012008:{ 12,[]int32{ 500,0,0,0,0 },
4012009:{ 12,[]int32{ 1000,0,0,0,0 },
4016001:{ 16,[]int32{ 1,0,0,0,0 },
4016002:{ 16,[]int32{ 2,0,0,0,0 },
4016003:{ 16,[]int32{ 3,0,0,0,0 },
4016004:{ 16,[]int32{ 5,0,0,0,0 },
4016005:{ 16,[]int32{ 10,0,0,0,0 },
4016006:{ 16,[]int32{ 15,0,0,0,0 },
4016007:{ 16,[]int32{ 20,0,0,0,0 },
4016008:{ 16,[]int32{ 30,0,0,0,0 },
4016009:{ 16,[]int32{ 50,0,0,0,0 },
4018001:{ 18,[]int32{ 10,0,0,0,0 },
4018002:{ 18,[]int32{ 20,0,0,0,0 },
4018003:{ 18,[]int32{ 30,0,0,0,0 },
4018004:{ 18,[]int32{ 50,0,0,0,0 },
4018005:{ 18,[]int32{ 100,0,0,0,0 },
4018006:{ 18,[]int32{ 200,0,0,0,0 },
4018007:{ 18,[]int32{ 300,0,0,0,0 },
4018008:{ 18,[]int32{ 500,0,0,0,0 },
4018009:{ 18,[]int32{ 1000,0,0,0,0 },
4018010:{ 18,[]int32{ 2000,0,0,0,0 },
4018011:{ 18,[]int32{ 5000,0,0,0,0 },
4018012:{ 18,[]int32{ 10000,0,0,0,0 },
4020001:{ 20,[]int32{ 10,0,0,0,0 },
4020002:{ 20,[]int32{ 20,0,0,0,0 },
4020003:{ 20,[]int32{ 30,0,0,0,0 },
4020004:{ 20,[]int32{ 40,0,0,0,0 },
4020005:{ 20,[]int32{ 50,0,0,0,0 },
4020006:{ 20,[]int32{ 60,0,0,0,0 },
4043001:{ 43,[]int32{ 1,0,0,0,0 },
4043002:{ 43,[]int32{ 2,0,0,0,0 },
4043003:{ 43,[]int32{ 4,0,0,0,0 },
4043004:{ 43,[]int32{ 6,0,0,0,0 },
4043005:{ 43,[]int32{ 12,0,0,0,0 },
4043006:{ 43,[]int32{ 18,0,0,0,0 },
4043007:{ 43,[]int32{ 24,0,0,0,0 },
4043008:{ 43,[]int32{ 30,0,0,0,0 },
4043009:{ 43,[]int32{ 60,0,0,0,0 },
4043010:{ 43,[]int32{ 120,0,0,0,0 },
4047001:{ 47,[]int32{ 10,0,0,0,0 },
4047002:{ 47,[]int32{ 20,0,0,0,0 },
4047003:{ 47,[]int32{ 30,0,0,0,0 },
4047004:{ 47,[]int32{ 50,0,0,0,0 },
4047005:{ 47,[]int32{ 100,0,0,0,0 },
4047006:{ 47,[]int32{ 200,0,0,0,0 },
4047007:{ 47,[]int32{ 300,0,0,0,0 },
4047008:{ 47,[]int32{ 500,0,0,0,0 },
4047009:{ 47,[]int32{ 1000,0,0,0,0 },
4047010:{ 47,[]int32{ 2000,0,0,0,0 },
4047011:{ 47,[]int32{ 3000,0,0,0,0 },
4047012:{ 47,[]int32{ 5000,0,0,0,0 },
4047013:{ 47,[]int32{ 10000,0,0,0,0 },
4064001:{ 64,[]int32{ 5,0,0,0,0 },
4064002:{ 64,[]int32{ 10,0,0,0,0 },
4064003:{ 64,[]int32{ 20,0,0,0,0 },
4064004:{ 64,[]int32{ 50,0,0,0,0 },
4064005:{ 64,[]int32{ 100,0,0,0,0 },
4064006:{ 64,[]int32{ 200,0,0,0,0 },
4064007:{ 64,[]int32{ 500,0,0,0,0 },
4068001:{ 68,[]int32{ 50000,0,0,0,0 },
4068002:{ 68,[]int32{ 500000,0,0,0,0 },
4068003:{ 68,[]int32{ 5000000,0,0,0,0 },
4068004:{ 68,[]int32{ 50000000,0,0,0,0 },
4084001:{ 84,[]int32{ 10,0,0,0,0 },
4084002:{ 84,[]int32{ 20,0,0,0,0 },
4084003:{ 84,[]int32{ 50,0,0,0,0 },
4084004:{ 84,[]int32{ 100,0,0,0,0 },
4085001:{ 85,[]int32{ 5,0,0,0,0 },
4085002:{ 85,[]int32{ 10,0,0,0,0 },
4085003:{ 85,[]int32{ 20,0,0,0,0 },
4085004:{ 85,[]int32{ 30,0,0,0,0 },
4085005:{ 85,[]int32{ 50,0,0,0,0 },
4096001:{ 96,[]int32{ 10,0,0,0,0 },
4096002:{ 96,[]int32{ 20,0,0,0,0 },
4096003:{ 96,[]int32{ 50,0,0,0,0 },
4096004:{ 96,[]int32{ 100,0,0,0,0 },
4096005:{ 96,[]int32{ 200,0,0,0,0 },
4096006:{ 96,[]int32{ 300,0,0,0,0 },
4096007:{ 96,[]int32{ 500,0,0,0,0 },
4096008:{ 96,[]int32{ 1000,0,0,0,0 },
4096009:{ 96,[]int32{ 2000,0,0,0,0 },
4096010:{ 96,[]int32{ 3000,0,0,0,0 },
4096011:{ 96,[]int32{ 5000,0,0,0,0 },
4096012:{ 96,[]int32{ 10000,0,0,0,0 },
4104001:{ 104,[]int32{ 500,0,0,0,0 },
4104002:{ 104,[]int32{ 1000,0,0,0,0 },
4104003:{ 104,[]int32{ 3000,0,0,0,0 },
4104004:{ 104,[]int32{ 5000,0,0,0,0 },
4104005:{ 104,[]int32{ 10000,0,0,0,0 },
4104006:{ 104,[]int32{ 20000,0,0,0,0 },
4104007:{ 104,[]int32{ 30000,0,0,0,0 },
4104008:{ 104,[]int32{ 50000,0,0,0,0 },
4104009:{ 104,[]int32{ 100000,0,0,0,0 },
4104010:{ 104,[]int32{ 200000,0,0,0,0 },
4104011:{ 104,[]int32{ 500000,0,0,0,0 },
4128001:{ 128,[]int32{ 1100,0,0,0,0 },
4128002:{ 128,[]int32{ 1200,0,0,0,0 },
4128003:{ 128,[]int32{ 1300,0,0,0,0 },
4128004:{ 128,[]int32{ 1400,0,0,0,0 },
4128005:{ 128,[]int32{ 1500,0,0,0,0 },
4128006:{ 128,[]int32{ 1600,0,0,0,0 },
4128007:{ 128,[]int32{ 1700,0,0,0,0 },
4128008:{ 128,[]int32{ 1800,0,0,0,0 },
4128009:{ 128,[]int32{ 1900,0,0,0,0 },
4128010:{ 128,[]int32{ 2000,0,0,0,0 },
4131001:{ 131,[]int32{ 5,0,0,0,0 },
4131002:{ 131,[]int32{ 10,0,0,0,0 },
4131003:{ 131,[]int32{ 20,0,0,0,0 },
4131004:{ 131,[]int32{ 30,0,0,0,0 },
4131005:{ 131,[]int32{ 50,0,0,0,0 },
4131006:{ 131,[]int32{ 100,0,0,0,0 },
4131007:{ 131,[]int32{ 200,0,0,0,0 },
4131008:{ 131,[]int32{ 300,0,0,0,0 },
4131009:{ 131,[]int32{ 500,0,0,0,0 },
4131010:{ 131,[]int32{ 1000,0,0,0,0 },
5011001:{ 11,[]int32{ 3,0,0,0,0 },
5012001:{ 12,[]int32{ 1,0,0,0,0 },
5018001:{ 18,[]int32{ 1,0,0,0,0 },
5024001:{ 24,[]int32{ 1,0,0,0,0 },
5047001:{ 47,[]int32{ 6,0,0,0,0 },
5063001:{ 63,[]int32{ 100,0,0,0,0 },
5068001:{ 68,[]int32{ 200000,0,0,0,0 },
5104001:{ 104,[]int32{ 100,0,0,0,0 },
}

35
cmd/v2/ui/about.go Normal file
View File

@ -0,0 +1,35 @@
package ui
import (
"go_dreamfactory/cmd/v2/lib/common"
"net/url"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
)
type about struct {
aboutDialog dialog.Dialog
}
func newAbout() *about {
var a about
ver := toolWin.app.Metadata().Version
link, _ := url.Parse("http://10.0.0.9:8080")
wlink := widget.NewHyperlinkWithStyle("更新包", link, fyne.TextAlignLeading, fyne.TextStyle{})
content := widget.NewCard("", "", widget.NewRichTextFromMarkdown(
`
梦工厂项目辅助工具GUI
@v`+ver,
))
a.aboutDialog = dialog.NewCustom(common.APP_ABOUT_TITLE, common.APP_ABOUT_CONFIRM,
container.NewVBox(content, wlink), toolWin.w)
return &a
}
func showAbout() {
newAbout().aboutDialog.Show()
}

84
cmd/v2/ui/app_auto.go Normal file
View File

@ -0,0 +1,84 @@
package ui
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"io/ioutil"
"path/filepath"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/theme"
"github.com/sirupsen/logrus"
)
//半自动化 依据采集的数据
type appAuto struct {
appAdapter
obs observer.Observer
caseList func()
itemList *common.ItemList
}
// 读文件
func (app *appAuto) loadData() error {
bytes, err := ioutil.ReadFile(filepath.Join(".", "robot.log"))
if err != nil {
return err
}
logrus.Debug(bytes)
return nil
}
func (app *appAuto) LazyInit(service service.PttService, obs observer.Observer) error {
app.obs = obs
app.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_AUTO, theme.StorageIcon(), nil)
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
// 初始化列表
app.itemList = common.NewItemList()
app.itemList.ItemList = app.itemList.CreateList()
// 读取采集的数据文件
app.caseList = func() {
app.loadData()
}
defer func() {
app.caseList()
}()
// layout
// panel := container.NewVSplit(container.NewBorder(app.monitorHeader, nil, nil, nil, app.monitorList), resPanel)
// panel.Offset = 0.8
content.Objects = append(content.Objects)
app.tabItem.Content = content
return nil
}
func (a *appAuto) OpenDefault() string {
return common.TOOLBAR_AUTO
}
func (a *appAuto) GetAppName() string {
return "自动化"
}
func (a appAuto) OnClose() bool {
return false
}
func (this *appAuto) ShortCut() fyne.Shortcut {
return &desktop.CustomShortcut{KeyName: fyne.Key3, Modifier: desktop.AltModifier}
}
func (this *appAuto) Icon() fyne.Resource {
return theme.StorageIcon()
}

112
cmd/v2/ui/app_container.go Normal file
View File

@ -0,0 +1,112 @@
package ui
import (
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2/container"
)
type appContainer struct {
appStatusMap map[string]appStatus
container.DocTabs
obs observer.Observer
ai []appInterface
service service.PttService
}
func newAppContainer(ai []appInterface, service service.PttService, obs observer.Observer) *appContainer {
at := &appContainer{ai: ai, service: service, obs: obs}
at.appStatusMap = make(map[string]appStatus)
at.CloseIntercept = at.closeHandle
at.SetTabLocation(container.TabLocationTop)
at.ExtendBaseWidget(at)
at.OnSelected = func(item *container.TabItem) {
item.Content.Refresh()
}
return at
}
type appStatus struct {
lazyInit bool
}
func (at *appContainer) closeHandle(tab *container.TabItem) {
for _, app := range at.ai {
if app.GetAppName() == tab.Text {
if app.OnClose() {
at.Remove(tab)
}
}
}
}
func (at *appContainer) openApp(app appInterface) error {
for _, appItem := range at.Items {
if appItem.Text == app.GetAppName() {
at.Select(appItem)
return nil
}
}
err := at.initApp(app)
if err != nil {
return err
}
tab := app.GetTabItem()
at.Append(tab)
at.Select(tab)
return nil
}
func (at *appContainer) openWelcome() (string, error) {
var firstTab *container.TabItem
app := &appWelcome{}
if err := at.initApp(app); err != nil {
return app.GetAppName(), err
}
tab := app.GetTabItem()
at.Append(tab)
if firstTab == nil {
firstTab = tab
}
if firstTab != nil {
at.Select(firstTab)
}
return "", nil
}
// open default app
func (at *appContainer) openDefaultApp(appName string) (string, error) {
var firstTab *container.TabItem
for _, app := range at.ai {
if app.OpenDefault() == appName {
if err := at.initApp(app); err != nil {
return app.GetAppName(), err
}
tab := app.GetTabItem()
at.Append(tab)
if firstTab == nil {
firstTab = tab
}
}
}
if firstTab != nil {
at.Select(firstTab)
}
return "", nil
}
func (at *appContainer) initApp(app appInterface) error {
st, ok := at.appStatusMap[app.GetAppName()]
if !ok || !st.lazyInit {
err := app.LazyInit(at.service, at.obs)
if err != nil {
return err
}
at.appStatusMap[app.GetAppName()] = appStatus{lazyInit: true}
}
return nil
}

View File

@ -0,0 +1,72 @@
package ui
import (
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
)
type appInterface interface {
LazyInit(service service.PttService, obs observer.Observer) error
GetTabItem() *container.TabItem
GetAppName() string
OpenDefault() string
GetUI() interface{}
OnClose() bool
Icon() fyne.Resource
}
var (
appRegister = []appInterface{
&appMonitor{},
&appTester{},
// &appAuto{},
}
toolRegister = []appInterface{
// &appWelcome{},
&appGen{},
&appPbGen{},
&appLock{},
&appTerm{},
&appPing{},
&appMgo{},
&appConfigValid{},
}
)
type appAdapter struct {
tabItem *container.TabItem
ai []appInterface
}
func (a appAdapter) LazyInit() error {
panic("implement LazyInit")
}
func (a appAdapter) GetAppName() string {
panic("implement GetAppName()")
}
func (a appAdapter) GetUI() interface{} {
panic("implement GetUI()")
}
func (a appAdapter) OpenDefault() string {
return ""
}
func (a appAdapter) GetTabItem() *container.TabItem {
return a.tabItem
}
func (a appAdapter) OnClose() bool {
return true
}
func (a appAdapter) Icon() fyne.Resource {
return theme.FyneLogo()
}

110
cmd/v2/ui/app_monitor.go Normal file
View File

@ -0,0 +1,110 @@
package ui
import (
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/types/known/anypb"
)
type appMonitor struct {
appAdapter
obs observer.Observer
logPanel *widget.Entry
monitorList *common.ItemList
id int64
}
func (app *appMonitor) LazyInit(service service.PttService, obs observer.Observer) error {
app.obs = obs
app.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_MONITOR, theme.MediaVideoIcon(), nil)
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
// panel for output log
app.logPanel = widget.NewMultiLineEntry()
app.logPanel.Wrapping = fyne.TextWrapWord
//clear button
clearBtn := widget.NewButtonWithIcon(common.APP_TESTCASE_BTN_CLEARLOG, theme.DeleteIcon(), func() {
app.logPanel.SetText("")
app.monitorList.Reset()
})
resPanel := container.NewBorder(container.NewHBox(clearBtn, layout.NewSpacer()), nil, nil, nil, app.logPanel)
app.monitorList = common.NewItemList()
app.monitorList.ItemList = app.monitorList.CreateList()
app.monitorList.ItemList.OnSelected = func(id widget.ListItemID) {
o := app.monitorList.CachedList.Items[id]
pd, ok := o.Data.(*model.PushModel)
if !ok {
return
}
if res, err := anypb.New(pd.Msg.Data); err == nil {
app.logPanel.Text = res.String()
app.logPanel.Refresh()
} else {
logrus.WithField("err", err).Error("view detail")
}
}
// layout
panel := container.NewVSplit(container.NewBorder(nil, nil, nil, nil,
app.monitorList.ItemList), resPanel)
panel.Offset = 0.8
content.Objects = append(content.Objects, panel)
app.tabItem.Content = content
app.monitorlistener()
return nil
}
func (a *appMonitor) OpenDefault() string {
return common.TOOLBAR_MONITOR
}
func (a *appMonitor) GetAppName() string {
return common.TOOLBAR_MONITOR
}
func (a appMonitor) OnClose() bool {
return false
}
// monitor list data
func (app *appMonitor) monitorlistener() {
app.obs.AddListener(observer.EVENT_APP_MONI, observer.Listener{
OnNotify: func(d interface{}, args ...interface{}) {
app.id++
data := d.(*model.PushModel)
protocol := fmt.Sprintf("【%s.%s】", data.Msg.MainType, data.Msg.SubType)
app.monitorList.AddItem(common.Item{
Text: fmt.Sprintf("%d - %-50v %-50v %-50v",
app.id, protocol, data.MethodName, data.DataTime),
Data: data,
})
app.monitorList.ItemList.ScrollToBottom()
},
})
}
func (this *appMonitor) ShortCut() fyne.Shortcut {
return &desktop.CustomShortcut{KeyName: fyne.Key3, Modifier: desktop.AltModifier}
}
func (this *appMonitor) Icon() fyne.Resource {
return theme.MediaVideoIcon()
}

187
cmd/v2/ui/app_testcase.go Normal file
View File

@ -0,0 +1,187 @@
package ui
import (
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
pb2 "go_dreamfactory/cmd/v2/lib/pb"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/pb"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
type appTester struct {
appAdapter
progress *widget.ProgressBarInfinite
}
func (a *appTester) GetAppName() string {
return common.TOOLBAR_TESTER
}
func (a *appTester) disEnabled(t *model.TestCase) bool {
return !t.Enabled
}
func (a *appTester) LazyInit(service service.PttService, obs observer.Observer) error {
a.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_TESTER, theme.DocumentIcon(), nil)
a.progress = widget.NewProgressBarInfinite()
a.progress.Hide()
content := container.NewMax()
title := widget.NewLabel(common.APP_TESTCASE_TITLE)
intro := widget.NewLabel("")
intro.Wrapping = fyne.TextWrapWord
caseContent := container.NewBorder(
container.NewVBox(container.NewHBox(title), widget.NewSeparator(), intro), nil, nil, nil, content)
setNav := func(t *model.TestCase) {
defer a.progress.Hide()
viewKey := fmt.Sprintf("%s.%s", t.MainType, t.SubType)
title.SetText(fmt.Sprintf("> %s", viewKey))
intro.SetText(t.Desc)
content.Objects = []fyne.CanvasObject{}
if view, ok := ViewRegister[viewKey]; ok {
timeLbl := widget.NewLabel("time")
resLog := widget.NewMultiLineEntry()
view.Init(service, obs, globalWin.w, resLog)
obs.AddListener(observer.EVENT_REQ_RSP, observer.Listener{
OnNotify: func(data interface{}, args ...interface{}) {
if data == nil {
return
}
msg := data.(*pb.UserMessage)
if t.MainType == msg.MainType && t.SubType == msg.SubType {
if t.Print != nil {
resLog.SetText(t.Print(msg))
} else {
resLog.SetText(msg.Data.String())
}
}
},
})
obs.AddListener(observer.EVENT_RST, observer.Listener{
OnNotify: func(data interface{}, args ...interface{}) {
tm := data.(time.Time)
cost := time.Since(tm)
// logrus.WithFields(logrus.Fields{"Main": t.MainType, "Sub": t.SubType, "cost": cost}).Debug("耗时")
timeLbl.Text = fmt.Sprintf("耗时:%v", cost)
timeLbl.Refresh()
},
})
resLog.Wrapping = fyne.TextWrapBreak
formReq := view.CreateView(t)
// required! Refresh updates the widget
formReq.Refresh()
formCard := widget.NewCard("", "", formReq)
clearBtn := widget.NewButtonWithIcon(common.APP_TESTCASE_BTN_CLEARLOG, theme.DeleteIcon(), func() {
resLog.SetText("")
})
resPanel := container.NewBorder(container.NewHBox(clearBtn, layout.NewSpacer(), timeLbl), nil, nil, nil, resLog)
panel := container.NewVSplit(
formCard,
resPanel,
)
panel.Offset = 0.8
content.Objects = append(content.Objects, panel)
} else {
logrus.WithFields(logrus.Fields{"mainType": t.MainType, "subType": t.SubType}).Warn("no view")
}
content.Refresh()
}
split := container.NewHSplit(a.makeNav(setNav), caseContent)
split.Offset = 0.2
a.tabItem.Content = container.NewBorder(nil, a.progress, nil, nil, split)
return nil
}
func (a *appTester) makeNav(setNav func(testCase *model.TestCase)) fyne.CanvasObject {
tree := &widget.Tree{
ChildUIDs: func(uid string) (c []widget.TreeNodeID) {
return pb2.CaseIndex[uid]
},
IsBranch: func(uid string) bool {
children, ok := pb2.CaseIndex[uid]
return ok && len(children) > 0
},
CreateNode: func(branch bool) fyne.CanvasObject {
return widget.NewLabel("(empty)")
},
UpdateNode: func(uid string, branch bool, obj fyne.CanvasObject) {
t, ok := pb2.CaseNav[uid]
if !ok {
logrus.WithField("id", uid).Warnf("Missing tutorial panel")
return
}
if a.disEnabled(t) {
obj.(*widget.Label).TextStyle = fyne.TextStyle{Italic: true}
if branch { //parent
obj.(*widget.Label).SetText(fmt.Sprintf("%s(dis0)", t.MainType))
} else {
// obj.(*widget.Label).SetText(fmt.Sprintf("%s(dis1)", t.SubType))
}
} else {
if branch { //parent
obj.(*widget.Label).TextStyle = fyne.TextStyle{Bold: true}
if t.NavLabel != "" {
obj.(*widget.Label).SetText(t.NavLabel)
} else {
obj.(*widget.Label).SetText(t.MainType)
}
} else {
if t.NavLabel != "" {
obj.(*widget.Label).SetText(t.NavLabel)
} else {
obj.(*widget.Label).SetText(t.SubType)
}
}
}
},
OnSelected: func(uid string) {
if t, ok := pb2.CaseNav[uid]; ok {
logrus.WithFields(logrus.Fields{"mainType": t.MainType, "subType": t.SubType}).Debug("select")
if a.disEnabled(t) {
return
}
if t.SubType == "" {
return
}
a.progress.Start()
a.progress.Show()
go setNav(t)
}
},
}
return container.NewBorder(nil, nil, nil, nil, tree)
}
func (a *appTester) ShortCut() fyne.Shortcut {
return &desktop.CustomShortcut{KeyName: fyne.Key2, Modifier: desktop.AltModifier}
}
func (a *appTester) Icon() fyne.Resource {
return theme.DocumentIcon()
}
func (a *appTester) Pressure() bool {
return true
}

10
cmd/v2/ui/basewindow.go Normal file
View File

@ -0,0 +1,10 @@
package ui
import "go_dreamfactory/cmd/v2/lib/common"
type WindowDefaultOptions struct {
windowAction common.WindowAction
windowMode common.WindowMode
windowAspect common.WindowAspect
// windowVisible bool
}

View File

@ -0,0 +1,57 @@
package ui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
)
// Declare conformity with Layout interface
var _ fyne.Layout = (*CZBoxLayout)(nil)
// CZBoxLayout is a grid layout that support custom size of object
// Now only support vertical mode
type CZBoxLayout struct {
vertical bool
}
func NewVCZBoxLayout() fyne.Layout {
return &CZBoxLayout{vertical: true}
}
func (c *CZBoxLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
pos := fyne.NewPos(0, 0)
for _, child := range objects {
if !child.Visible() {
continue
}
child.Move(pos)
size := child.Size()
if c.vertical {
pos = pos.Add(fyne.NewPos(0, size.Height+theme.Padding()))
}
}
}
func (c *CZBoxLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
minSize := fyne.NewSize(0, 0)
addPadding := false
for _, child := range objects {
if !child.Visible() {
continue
}
if c.vertical {
size := child.Size()
minSize.Width = fyne.Max(size.Width, minSize.Width)
minSize.Height += size.Height
if addPadding {
minSize.Height += theme.Padding()
}
}
addPadding = true
}
return minSize
}

49
cmd/v2/ui/log_viewer.go Normal file
View File

@ -0,0 +1,49 @@
package ui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type logViewer struct {
logArea *widget.Entry
refresh *widget.Button
Win fyne.Window
}
func newLogViewer() *logViewer {
var lv logViewer
lv.logArea = widget.NewMultiLineEntry()
lv.logArea.Disable()
lv.logArea.Wrapping = fyne.TextWrapBreak
lv.initLogContent()
lv.refresh = widget.NewButtonWithIcon(string(theme.ColorNameFocus), theme.ViewRefreshIcon(), func() {
// lv.initLogContent()
})
lv.Win = fyne.CurrentApp().NewWindow("日志")
lv.Win.SetContent(container.NewBorder(
container.NewHBox(layout.NewSpacer(), lv.refresh), nil, nil, nil,
lv.logArea))
lv.Win.Resize(fyne.NewSize(800, 600))
lv.Win.CenterOnScreen()
return &lv
}
func (lv *logViewer) initLogContent() {
cont := ""
// globalLogWriter.Traversal(func(s string) {
// cont += s
// })
// lv.logArea.CursorRow = globalLogWriter.Size()
lv.logArea.SetText(cont)
}

70
cmd/v2/ui/main_menu.go Normal file
View File

@ -0,0 +1,70 @@
package ui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
"github.com/sirupsen/logrus"
)
type mainMenu struct {
*fyne.MainMenu
sysMenu *fyne.Menu
appMenus []*fyne.MenuItem
settingMenu *fyne.Menu
testingMenu *fyne.MenuItem
quite *fyne.MenuItem
helpMenu *fyne.Menu
sysLog *fyne.MenuItem
// aboutSelf *fyne.MenuItem
}
func newMainMenu() *mainMenu {
var mm mainMenu
// system
mm.appMenus = make([]*fyne.MenuItem, len(appRegister))
for i, app := range appRegister {
app := app
mm.appMenus[i] = fyne.NewMenuItem(app.GetAppName(), func() {
err := globalWin.at.openApp(app)
if err != nil {
logrus.Errorf("打开 %s, err:%v", app.GetAppName(), err)
}
})
mm.appMenus[i].Icon = app.Icon()
}
mm.quite = fyne.NewMenuItem("退出", globalWin.quiteHandle)
mm.quite.Icon = theme.LogoutIcon()
mm.quite.IsQuit = true
mm.sysMenu = fyne.NewMenu("应用", mm.appMenus...)
mm.sysMenu.Items = append(mm.sysMenu.Items, fyne.NewMenuItemSeparator(), mm.quite)
// setting
mm.testingMenu = fyne.NewMenuItem("压测配置", func() {
newTestConfigWindow().Win.Show()
})
mm.testingMenu.Icon = theme.SettingsIcon()
mm.settingMenu = fyne.NewMenu("设置",
mm.testingMenu,
)
// help
mm.sysLog = fyne.NewMenuItem("日志", func() {
newLogViewer().Win.Show()
})
mm.helpMenu = fyne.NewMenu("帮助",
mm.sysLog,
// mm.aboutSelf,
)
mm.MainMenu = fyne.NewMainMenu(
mm.sysMenu,
// mm.settingMenu,
// mm.helpMenu,
)
return &mm
}

457
cmd/v2/ui/mainwindow.go Normal file
View File

@ -0,0 +1,457 @@
package ui
import (
"errors"
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/lib/storage"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/comm"
// "go_dreamfactory/modules/user"
"go_dreamfactory/pb"
"strings"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/BabySid/gobase"
"github.com/Pallinder/go-randomdata"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
var (
globalWin *MainWindowImpl
)
type MainWindow interface {
WindowInterface
}
type MainWindowImpl struct {
UIImpl
WindowDefaultOptions
parent fyne.Window
w fyne.Window
tb *toolBar //工具条
toys *toys // side
sb *statusBar //状态栏
at *appContainer //tabs
mm *mainMenu //菜单
}
func NewMainWindow(ui *UIImpl, parent fyne.Window) MainWindow {
gobase.NewScheduler().Start()
gobase.RegisterAtExit(gobase.GlobalScheduler.Stop)
mw := &MainWindowImpl{
UIImpl: *ui,
parent: parent,
}
globalWin = mw
ui.obs.AddListener(observer.EVENT_PING, observer.Listener{
OnNotify: func(data interface{}, args ...interface{}) {
ui.obs.Remove(observer.EVENT_PING)
logrus.Debug("即将与服务器断开链接")
conf := dialog.NewConfirm("链接中断", data.(error).Error(), func(
b bool) {
ui.app.Quit()
}, globalWin.w)
conf.SetDismissText("取消")
conf.SetConfirmText("退出")
conf.Show()
},
})
return mw
}
// createWindowContainer create a container with the window content
func (ui *MainWindowImpl) createWindowContainer() {
// create main layout
// status bar
ui.sb = newStatusBar()
// tool bar
toolbar := widget.NewToolbar(
// widget.NewToolbarAction(theme.MediaVideoIcon(), func() {
// openApp(ui.at, common.TOOLBAR_MONITOR)
// }),
// widget.NewToolbarAction(theme.DocumentIcon(), func() {
// openApp(ui.at, common.TOOLBAR_TESTER)
// }),
// widget.NewToolbarAction(theme.StorageIcon(), func() {
// openApp(ui.at, common.TOOLBAR_AUTO)
// }),
// widget.NewToolbarSpacer(),
// widget.NewToolbarAction(theme.FileIcon(), func() {
// newLogViewer().Win.Show()
// }),
)
ui.tb = newToolBar(toolbar)
// Fun Toys
ui.toys = newToys(ui.obs)
// Main Menu
ui.mm = newMainMenu()
ui.w.SetMainMenu(ui.mm.MainMenu)
// main app tabs
ui.at = newAppContainer(appRegister, ui.pttService, ui.obs)
content := container.NewBorder(ui.tb.toolbar, ui.sb.widget, nil, ui.toys.widget, ui.at)
ui.w.SetContent(content)
ui.w.CenterOnScreen()
ui.w.SetCloseIntercept(ui.quiteHandle)
}
func (ui *MainWindowImpl) SetStatusMsg(msg string) {
ui.sb.setMessage(fmt.Sprintf("err:%s", msg))
}
func (ui *MainWindowImpl) quiteHandle() {
dialog.ShowConfirm("提示", "确定退出吗", func(b bool) {
if !b {
return
}
ui.app.Quit()
}, ui.w)
}
// CreateWindow ....
func (ui *MainWindowImpl) CreateWindow(_ string, width, height float32, _ bool) {
// init window
title := fmt.Sprintf(common.APP_WIN_TITLE, "登录", ui.app.Metadata().Version, ui.app.Metadata().Build, common.APP_NAME)
w := ui.app.NewWindow(title)
ui.AddWindow("main", w)
ui.w = w
if ui.windowAspect == common.WindowAspect_FullScreen {
w.SetFullScreen(true)
} else {
w.Resize(fyne.NewSize(width, height))
}
// w.SetMaster()
w.CenterOnScreen()
w.SetCloseIntercept(func() {
ui.parent.Show()
})
_ = ui.createChooseServerPopUp(w)
}
// createChooseServerPopUp
func (ui *MainWindowImpl) createChooseServerPopUp(w fyne.Window) error {
ch := make(chan string)
title := fmt.Sprintf(common.APP_WIN_TITLE, "选服", ui.app.Metadata().Version, ui.app.Metadata().Build, common.APP_NAME)
selServerWin := ui.createChooseServerWindow(title, ch)
go func() {
data := <-ch
selServerWin.Hide()
ui.NewWelcomeLabel(data)
ui.w.Show()
}()
return nil
}
func (ui *MainWindowImpl) configForm(w fyne.Window, call func()) *widget.Form {
//区服编号
sId := widget.NewEntry()
//区服地址
sAddr := widget.NewEntry()
//区服名称
sName := widget.NewEntry()
configForm := widget.NewForm()
configForm.AppendItem(widget.NewFormItem("区服编号", sId))
configForm.AppendItem(widget.NewFormItem("区服地址", sAddr))
configForm.AppendItem(widget.NewFormItem("区服名称", sName))
configForm.OnSubmit = func() {
defer func() {
w.Close()
call()
}()
ui.config.Servers = append(ui.config.Servers, &storage.ServerConfig{
Name: sName.Text,
SId: sId.Text,
Url: sAddr.Text,
})
if err := ui.storage.StoreConfig(ui.config); err != nil {
dialog.ShowError(err, w)
return
}
}
configForm.SubmitText = "保存配置"
configForm.Refresh()
return configForm
}
// createChooseServerWindow
func (ui *MainWindowImpl) createChooseServerWindow(title string, ch chan string) fyne.Window {
w := fyne.CurrentApp().NewWindow(title)
// choose server button func
makeButton := func(s *storage.ServerConfig, parent fyne.Window) *widget.Button {
btn := widget.NewButton(s.Name, func() {
d := dialog.NewInformation("", common.INFO_WAIT, parent)
d.SetDismissText(common.BUTTON_CANCEL)
d.Show()
logrus.WithFields(logrus.Fields{"server": s.Name, "sid": s.SId}).Debug("choose server")
//conn server
if err := ui.connService.WsConnect(s.Url); err != nil {
d.Hide()
dialog.ShowError(err, parent)
} else {
ch <- fmt.Sprintf("%s:%s", s.SId, s.Name)
}
})
return btn
}
sGrid := container.NewGridWithColumns(2)
if len(ui.config.Servers) == 0 {
c := storage.NewDefaultConfig()
ui.config.Servers = append(ui.config.Servers, c.Servers...)
}
if len(ui.config.Servers) != 0 {
for _, s := range ui.config.Servers {
box := makeButton(s, w)
sGrid.Add(container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.DeleteIcon(), func() {
defer func() {
w.Close()
ui.parent.Show()
}()
txt := box.Text
for i, ss := range ui.config.Servers {
if txt == ss.Name {
ui.config.Servers = append(ui.config.Servers[:i], ui.config.Servers[i+1:]...)
i--
}
}
if err := ui.storage.StoreConfig(ui.config); err != nil {
dialog.ShowError(err, w)
}
}), box))
}
sGrid.Add(widget.NewButtonWithIcon("", theme.ContentAddIcon(), func() {
addWin := fyne.CurrentApp().NewWindow("添加区服")
addWin.SetContent(ui.configForm(addWin, func() {
w.Close()
ui.parent.Show()
}))
addWin.SetFixedSize(true)
addWin.Resize(fyne.NewSize(500, 200))
addWin.Show()
addWin.CenterOnScreen()
}))
w.SetContent(sGrid)
} else {
w.SetContent(ui.configForm(w, func() {
w.Close()
ui.parent.Show()
}))
}
w.SetFixedSize(true)
w.Resize(fyne.NewSize(500, 200))
w.Show()
w.SetCloseIntercept(func() {
ui.parent.Show()
w.Close()
})
w.CenterOnScreen()
return w
}
// createLoginWin
func (ui *MainWindowImpl) createLoginWin(sid, sname string) {
account := widget.NewEntry()
// account.Text = "user8080" //default account
items := []*widget.FormItem{
widget.NewFormItem(common.LABEL_ACCOUNT, account),
}
dialog.ShowForm(common.FORM_TITLE_LOGIN, common.BUTTON_LOGIN, common.BUTTON_CANCEL, items, func(b bool) {
if !b {
logrus.Debug("cancel login")
} else {
if account.Text != "" {
logrus.WithField("account", account.Text).Debug("submit login")
ui.createWindowContainer()
appName, err := ui.at.openDefaultApp(common.TOOLBAR_MONITOR)
if err != nil {
logrus.WithField("appName", appName).Error(err)
}
// hide toolbar
ui.tb.toolbar.Hide()
// call after ui.createWindowContainer
ui.connService.ListenerPush()
if errdata := ui.pttService.Login(sid, account.Text); errdata != nil {
err := fmt.Errorf("login err: %v[%d]", errdata, int32(errdata.Code))
dialog.ShowError(err, ui.w)
} else {
ui.obs.AddListener(observer.EVENT_REQ_RSP, observer.Listener{
OnNotify: func(data interface{}, args ...interface{}) {
if data == nil {
return
}
msg := data.(*pb.UserMessage)
if msg.MainType == string("user") &&
msg.SubType == "login" {
rsp := &pb.UserLoginResp{}
if !comm.ProtoUnmarshal(msg, rsp) {
logrus.Error("unmarshal err")
return
}
//ping
go func() {
timer := time.NewTimer(10 * time.Second)
for {
if !timer.Stop() {
select {
case <-timer.C:
default:
}
}
timer.Reset(10 * time.Second)
select {
case <-timer.C:
ui.pttService.Ping(sid, account.Text)
continue
}
}
}()
// reset main window title
subTitle := fmt.Sprintf("%s[%s]", sname, sid)
ui.w.SetTitle(fmt.Sprintf(common.APP_WIN_TITLE, subTitle, ui.app.Metadata().Version, ui.app.Metadata().Build, common.APP_NAME))
ui.pttService.SetUser(rsp.Data, rsp.Ex)
// isCreateRole
if rsp.Data.Created {
// set statusbar message
ui.sb.setMessage(common.LABEL_WELCOME + rsp.Data.Binduid)
// update userinfo
ui.obs.Notify(observer.EVENT_USERINFO, ui.pttService.GetUser())
// show toolbar
ui.tb.toolbar.Show()
} else {
// create role
ui.createRoleWindowPopUp()
}
} else if msg.MainType == "notify" && msg.SubType == "errornotify" {
push := &pb.NotifyErrorNotifyPush{}
if !comm.ProtoUnmarshal(msg, push) {
logrus.Error("unmarsh err")
return
}
if push.Code == pb.ErrorCode_UserLogined {
cf := dialog.NewError(errors.New("【"+account.Text+"】账号已在其它终端登录,退出重新登录"), ui.w)
cf.SetOnClosed(func() {
ui.app.Quit()
})
cf.Show()
}
}
},
})
}
}
}
}, ui.w)
}
// createMainWindowMenu create the main window menu
func (ui *MainWindowImpl) createMainWindowMenu() *fyne.MainMenu {
menuItems := []*fyne.MenuItem{}
menu := fyne.Menu{
Label: common.MENU_FILE,
Items: menuItems,
}
return fyne.NewMainMenu(&menu)
}
// createRoleWindowPopUp
func (ui *MainWindowImpl) createRoleWindowPopUp() {
nickname := widget.NewEntry()
c := container.NewBorder(nil, nil, nil,
widget.NewButtonWithIcon("", theme.ViewRefreshIcon(), func() {
nickname.SetText(randomdata.SillyName())
}),
nickname,
)
figure := widget.NewEntry()
figure.PlaceHolder = "形象ID"
figure.Text = "1"
var gender int32
genderSel := widget.NewSelect([]string{"男", "女"}, func(s string) {
if s == "男" {
gender = 0
} else if s == "女" {
gender = 1
}
})
genderSel.Selected = "男"
items := []*widget.FormItem{
widget.NewFormItem("性别", genderSel),
widget.NewFormItem("形象", figure),
widget.NewFormItem(common.LABEL_NICKNAME, c),
}
d := dialog.NewForm(common.FORM_TITLE_CREATEROLE, common.BUTTON_OK, common.BUTTON_CANCEL, items, func(b bool) {
if nickname.Text != "" {
logrus.WithField("nickname", nickname.Text).Debug("submit crete role")
if errdata := ui.pttService.CreateRole(nickname.Text, gender, cast.ToInt32(figure.Text)); errdata != nil {
err := fmt.Errorf("login err: %v[%d]", errdata, int32(errdata.Code))
dialog.ShowError(err, ui.w)
} else {
user := ui.pttService.GetUser()
logrus.WithField("uid", user.DbUser.Uid).Debug("create role succ")
ui.obs.Notify(observer.EVENT_USERINFO, ui.pttService.GetUser())
ui.tb.toolbar.Show()
}
}
}, ui.w)
d.Resize(fyne.NewSize(300, 200))
d.Show()
}
// NewWelcomeLabel
func (ui *MainWindowImpl) NewWelcomeLabel(data string) {
p := strings.Split(data, ":")
if len(p) != 2 {
logrus.WithField("param", p).Error("choose service")
return
}
c := container.NewCenter(container.NewVBox(
widget.NewLabelWithStyle(fmt.Sprintf(common.LABEL_CHOOSE, p[0], p[1]),
fyne.TextAlignCenter,
fyne.TextStyle{Bold: true}),
container.NewCenter(container.NewHBox(
widget.NewButton(common.BUTTON_LOGIN, func() {
ui.createLoginWin(p[0], p[1])
}),
)),
))
ui.w.SetContent(c)
ui.w.SetCloseIntercept(func() {
ui.w.Close()
ui.parent.Show()
})
}

127
cmd/v2/ui/navview.go Normal file
View File

@ -0,0 +1,127 @@
package ui
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
formview "go_dreamfactory/cmd/v2/ui/views"
// "go_dreamfactory/modules/friend"
// "go_dreamfactory/modules/growtask"
// "go_dreamfactory/modules/hero"
// "go_dreamfactory/modules/library"
// "go_dreamfactory/modules/linestory"
// "go_dreamfactory/modules/mline"
// "go_dreamfactory/modules/sociaty"
// "go_dreamfactory/modules/task"
// "go_dreamfactory/modules/user"
// "go_dreamfactory/modules/worldtask"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
)
type MyCaseView interface {
Init(service service.PttService, obs observer.Observer, w fyne.Window, res *widget.Entry)
CreateView(t *model.TestCase) fyne.CanvasObject
Load()
}
// 1 注册接口测试的表单试图
var (
ViewRegister = map[string]MyCaseView{
// gm
common.MF("gm", "cmd"): &formview.BingoView{},
// common.MF(comm.ModuleGM, "gi"): &formview.GlobalConfView{},
// reddot
// common.MF(comm.ModuleReddot, "get"): &formview.ReddotView{},
//sys
// common.MF(comm.ModuleSys, "funclist"): &formview.SysFuncListView{},
//user
// common.MF(comm.ModuleUser, user.UserSubTypeModifyAvatar): &formview.UserModifyavatarView{},
// common.MF(comm.ModuleUser, user.UserSubTypeModifyName): &formview.UserModifynameView{},
// common.MF(comm.ModuleUser, user.UserSubTypeModifyFigure): &formview.UserModifyfigureView{},
// common.MF(comm.ModuleUser, user.UserSubTYpeModifyBgp): &formview.UserModifybgpView{},
// common.MF(comm.ModuleUser, user.UserSubTypeModifySign): &formview.UserSignView{},
// common.MF(comm.ModuleUser, user.UserSubTypeBattlerecord): &formview.UserBattlerecordView{},
// items
// common.MF(comm.ModuleItems, "getlist"): &formview.ItemListView{},
// common.MF(comm.ModuleItems, "sellitem"): &formview.ItemsSellView{},
// common.MF(comm.ModuleItems, "useitem"): &formview.ItemsUseView{},
//mail
// common.MF(comm.ModuleMail, "getlist"): &formview.MailListView{},
// common.MF(comm.ModuleMail, "readmail"): &formview.MailReadMailView{},
// common.MF(comm.ModuleMail, "delmail"): &formview.MailDelView{},
// common.MF(comm.ModuleMail, "getusermailattachment"): &formview.MailAttachmentView{},
// common.MF(comm.ModuleMail, "getallmailattachment"): &formview.MailAttachmentAllView{},
//task
// common.MF(comm.ModuleTask, task.TaskSubTypeList): &formview.TaskListView{},
// common.MF(comm.ModuleTask, task.TaskSubTypeActiveList): &formview.TaskActiveListView{},
// common.MF(comm.ModuleTask, task.TaskSubTypeActiveReceive): &formview.TaskActiveReceiveView{},
// common.MF(comm.ModuleTask, task.TaskSubTypeGetrecord): &formview.RtaskRecordView{},
// hero
// common.MF(comm.ModuleHero, hero.HeroSubTypeList): &formview.HeroListView{},
// common.MF(comm.ModuleHero, hero.StrengthenUplv): &formview.HeroStrengthenUplvView{},
// common.MF(comm.ModuleHero, hero.StrengthenUpStar): &formview.HeroStrengthenUpStarView{},
// common.MF(comm.ModuleHero, hero.DrawCard): &formview.HeroZhaomuView{},
// common.MF(comm.ModuleHero, "info"): &formview.HeroInfoView{},
//equip
// common.MF(comm.ModuleEquipment, "getlist"): &formview.EquipListView{},
// common.MF(comm.ModuleEquipment, "equip"): &formview.EquipUpDownView{},
// common.MF(comm.ModuleEquipment, "upgrade"): &formview.EquipUpgradeView{},
//friend
// common.MF(comm.ModuleFriend, friend.FriendSubTypeApply): &formview.FriendApplyView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAgree): &formview.FriendAgreeView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeBlacklist): &formview.FriendBlacklistView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAddBlack): &formview.FriendAddBlackView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeDelBlack): &formview.FriendDelblackView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeSearch): &formview.FriendSearchView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeRandList): &formview.FriendRandListView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeGetreward): &formview.FriendGetrewardView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAssistlist): &formview.FriendAssistListView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAssistHero): &formview.FriendAssistHeroView{},
// common.MF(comm.ModuleFriend, friend.FriendSubTypeAssistHeroList): &formview.FriendAssistHeroListView{},
// shop
// common.MF(comm.ModuleShop, "getlist"): &formview.ShopListView{},
// common.MF(comm.ModuleShop, "buy"): &formview.ShopBuyView{},
// mailine
// common.MF(comm.ModuleMline, mline.MlineGetListResp): &formview.MainlineListView{},
// common.MF(comm.ModuleMline, mline.MlineChallengeResp): &formview.MainlineChallengeView{},
// common.MF(comm.ModuleMline, mline.MlineGetRewardResp): &formview.MainlineRewardView{},
// pagoda
// common.MF(comm.ModulePagoda, "getlist"): &formview.PagodaListView{},
// rtask
common.MF("worldtask", "test"): &formview.RtaskTestView{},
// linestory
// common.MF(comm.ModuleLinestory, linestory.LinestorySubTypeChapter): &formview.LinestoryMineView{},
// common.MF(comm.ModuleLibrary, library.LibraryFetterstoryTaskResp): &formview.FetterstoryView{},
// gourmet
// common.MF(comm.ModuleGourmet, "getranduser"): &formview.GourmentGetRandView{},
// sociaty
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeList): &formview.SociatyListView{},
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeCreate): &formview.SociatyCreateView{},
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeMine): &formview.SociatyMineView{},
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeTasklist): &formview.SociatyTasklistView{},
// common.MF(comm.ModuleSociaty, sociaty.SociatySubTypeRank): &formview.SociatyRankView{},
// common.MF(comm.ModuleSociaty, "boss"): &formview.SociatyBossView{},
// troll
// common.MF(comm.ModuleTroll, "getlist"): &formview.TrollGetlistView{},
// growtask
// common.MF(comm.ModuleGrowtask, growtask.GrowtaskSubTypeList): &formview.GrowtaskListView{},
//worldtask
// common.MF(comm.ModuleWorldtask, worldtask.WorldtaskSubtypeMine): &formview.WorldtaskMineView{},
// common.MF(comm.ModuleWorldtask, "check"): &formview.CheckWorldtaskView{},
// common.MF(comm.ModuleWorldtask, "test"): &formview.RtaskTestView{},
// //smithy
common.MF("smithy", "tasklist"): &formview.SmithyView{},
//武馆派遣
// common.MF(comm.ModuleDispatch, "dispatch"): &formview.DispatchView{},
// common.MF(comm.ModulePractice, "practice"): &formview.PracticeView{},
//声望
// common.MF(comm.ModuleReputation, "reputation"): &formview.ReputationView{},
//旧时光
// common.MF(comm.ModuleOldtimes, "oldtimes"): &formview.OldtimesView{},
}
)

26
cmd/v2/ui/status_bar.go Normal file
View File

@ -0,0 +1,26 @@
package ui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
)
type statusBar struct {
msgLabel *widget.Label
widget *fyne.Container
}
func newStatusBar() *statusBar {
var sb statusBar
sb.msgLabel = widget.NewLabel("")
sb.widget = container.New(layout.NewHBoxLayout(),
sb.msgLabel,
layout.NewSpacer())
return &sb
}
func (sb *statusBar) setMessage(msg string) {
sb.msgLabel.SetText(msg)
}

View File

@ -0,0 +1,71 @@
package ui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type TestConfigWindow struct {
computer *widget.Button
Win fyne.Window
}
func newTestConfigWindow() *TestConfigWindow {
var configWin TestConfigWindow
conf := globalWin.UIImpl.config
// 压测form
timeoutEntry := widget.NewEntry()
timeoutEntry.PlaceHolder = "毫秒数"
timeoutEntry.Text = cast.ToString(conf.Pressure.TimeoutMs)
lpsEntry := widget.NewEntry()
lpsEntry.PlaceHolder = "并发数量"
lpsEntry.Text = cast.ToString(conf.Pressure.Concurrency)
durationEntry := widget.NewEntry()
durationEntry.PlaceHolder = "秒数"
durationEntry.Text = cast.ToString(conf.Pressure.DurationS)
form := widget.NewForm(
widget.NewFormItem("超时(ms)", timeoutEntry),
widget.NewFormItem("并发量", lpsEntry),
widget.NewFormItem("持续时间(s)", durationEntry),
)
form.OnSubmit = func() {
pressure := conf.Pressure
pressure.TimeoutMs = cast.ToInt32(timeoutEntry.Text)
pressure.Concurrency = 10
pressure.DurationS = 2
conf.Pressure = pressure
if err := globalWin.UIImpl.storage.StoreConfig(conf); err != nil {
logrus.Error(err)
}
}
form.SubmitText = "确定"
// 计算器
concurrencyEntry := widget.NewEntry()
computerForm := widget.NewForm(
widget.NewFormItem("并发", concurrencyEntry),
)
computerForm.OnSubmit = func() {
}
computerForm.SubmitText = "确定"
// layout
configWin.Win = fyne.CurrentApp().NewWindow("压测配置")
//压测配置
settingLayout := container.NewBorder(widget.NewLabel("压测配置"), nil, nil, nil, form)
// computer
computerLayout := container.NewBorder(widget.NewLabel("并发量计算"), nil, nil, nil, computerForm)
configWin.Win.SetContent(container.NewGridWithRows(2, settingLayout, computerLayout))
configWin.Win.Resize(fyne.NewSize(800, 600))
configWin.Win.CenterOnScreen()
return &configWin
}

51
cmd/v2/ui/tool_bar.go Normal file
View File

@ -0,0 +1,51 @@
package ui
import (
"fmt"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
type toolBar struct {
toolbar *widget.Toolbar
}
func newToolBar(items *widget.Toolbar) *toolBar {
var tb toolBar
// mainwindow toolbar
tb.toolbar = items
return &tb
}
// open app2
func openApp(ac *appContainer, name string) {
for _, app := range appRegister {
if app.GetAppName() == name {
err := ac.openApp(app)
if err != nil {
logrus.Error(fmt.Errorf("%s %v", app.GetAppName(), err))
}
}
}
}
func openApp2(ac *appContainer, name string) {
for _, app := range toolRegister {
if app.GetAppName() == name {
err := ac.openApp(app)
if err != nil {
logrus.Error(fmt.Errorf("%s %v", app.GetAppName(), err))
}
}
}
}
func closeApp3(ac *appContainer, name string) {
for _, appItem := range ac.Items {
if appItem.Text == name {
ac.Remove(appItem)
}
}
}

436
cmd/v2/ui/tool_luban.go Normal file
View File

@ -0,0 +1,436 @@
package ui
import (
"errors"
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
os_storage "go_dreamfactory/cmd/v2/lib/storage"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"os/exec"
"path/filepath"
"runtime"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
fyne_storage "fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
type appGen struct {
appAdapter
obs observer.Observer
goList *common.ItemList
jsonList *common.ItemList
}
func (this *appGen) LazyInit(ptService service.PttService, obs observer.Observer) error {
this.obs = obs
this.goList = common.NewItemList()
this.jsonList = common.NewItemList()
this.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_GEN, theme.ContentCopyIcon(), nil)
storage, _ := os_storage.NewOSStorage()
conf, err := storage.LoadConfig()
if err != nil {
return err
}
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
serverAddr := widget.NewEntry()
serverAddr.PlaceHolder = "服务器地址 例如: 10.0.1.11"
projectDir := widget.NewEntry()
projectDir.PlaceHolder = `项目目录 例如: E:\projects\workspace\go_dreamfactory\`
workDir := widget.NewEntry()
workDir.PlaceHolder = `LuBan目录 例如: E:\svn\dreamworks\client\dreamworks\ExcelFile\`
// client
client := widget.NewEntry()
client.Text = `Luban.Client\Luban.Client.exe`
client.PlaceHolder = `配置Luban Client.exe路径`
//define
define := widget.NewEntry()
define.PlaceHolder = "定义文件"
define.Text = `Defines\\__root__.xml`
// output
outputCodeDir := widget.NewEntry()
outputCodeDir.Text = `sys\configure\structs`
outputJsonDir := widget.NewEntry()
outputJsonDir.Text = `bin\json`
//input
inputDir := widget.NewEntry()
inputDir.Text = "Datas"
tmpDir := widget.NewEntry()
tmpDir.Text = filepath.Join(`c:\tmp\`)
tmpDir.PlaceHolder = "临时目录,不要和工作目录和项目目录重复"
//genType
var genTypeText string
genType := widget.NewSelect([]string{"go", "json", "all"}, func(s string) {
genTypeText = s
})
genType.PlaceHolder = "生成类型"
if conf.LubanConf != nil {
serverAddr.Text = conf.LubanConf.ServerAddr
projectDir.Text = conf.LubanConf.ProjectDir
workDir.Text = conf.LubanConf.WorkDir
client.Text = conf.LubanConf.Client
genType.Selected = conf.LubanConf.GenType
genTypeText = conf.LubanConf.GenType
tmpDir.Text = conf.LubanConf.TmpDir
}
form := widget.NewForm(
widget.NewFormItem("服务地址", serverAddr),
widget.NewFormItem("项目目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(projectDir, toolWin.w)
}), projectDir)),
widget.NewFormItem("工作目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(workDir, toolWin.w)
}), workDir)),
widget.NewFormItem("LuBan Cli", client),
widget.NewFormItem("输入目录", inputDir),
widget.NewFormItem("输出Code目录", outputCodeDir),
widget.NewFormItem("输出JSON目录", outputJsonDir),
widget.NewFormItem("临时目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(tmpDir, toolWin.w)
}), tmpDir)),
widget.NewFormItem("生成类型", genType),
)
getType := func() string {
if genTypeText == "" {
return ""
} else {
if genTypeText == "go" {
return "code_go_json"
} else if genTypeText == "json" {
return "data_json"
} else if genTypeText == "all" {
return "code_go_json,data_json"
}
}
return ""
}
saveBtn := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), func() {
lubanconf := &os_storage.LubanConfig{
ServerAddr: serverAddr.Text,
ProjectDir: projectDir.Text,
Client: client.Text,
WorkDir: workDir.Text,
InputDir: inputDir.Text,
OutputCodeDir: outputCodeDir.Text,
OutputJsonDir: outputJsonDir.Text,
GenType: genTypeText,
TmpDir: tmpDir.Text,
}
conf.LubanConf = lubanconf
if err := storage.StoreConfig(conf); err != nil {
logrus.WithField("err", err).Debug("保存配置")
}
logrus.Debug("save luban conf")
})
//go
this.goList.TitleLabel = widget.NewLabel("Go文件")
this.goList.TitleLabel.Hide()
// 复选列表
this.goList.ItemList = this.goList.CreateDefaultCheckList()
// 覆盖 -go
go_allSelBtn := &widget.Button{Icon: theme.CheckButtonCheckedIcon()}
go_allCancelBtn := &widget.Button{Icon: theme.CheckButtonIcon()}
go_overrideBtn := &widget.Button{Text: "覆盖", Icon: theme.ConfirmIcon()}
go_overrideBtn.Hide()
go_allSelBtn.Hide()
// 覆盖
go_overrideBtn.OnTapped = func() {
go_overrideBtn.Disable()
defer func() {
go_overrideBtn.Enable()
this.goList.ItemList.Refresh()
}()
for _, v := range this.goList.SelItemIds {
// logrus.WithField("path1", filepath.Join(tmpDir.Text, "go", v)).Debug("copy go")
// logrus.WithField("path2", filepath.Join(projectDir.Text, outputCodeDir.Text, v)).Debug("copy go")
_, err := common.Copy(filepath.Join(tmpDir.Text, "go", v),
filepath.Join(projectDir.Text, outputCodeDir.Text, v))
if err != nil {
logrus.Error(err)
return
}
this.goList.DeleteItem(v)
}
this.goList.ChangeFileCount()
}
//取消checked
go_allCancelBtn.OnTapped = func() {
defer func() {
go_allCancelBtn.Hide()
go_allSelBtn.Show()
}()
this.goList.SelItemIds = []string{}
for i, v := range this.goList.CachedList.Items {
this.goList.CachedList.Items[i].Checked = false
this.goList.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
}
this.goList.ChangeFileCount()
this.goList.ItemList.Refresh()
}
//选择所有
go_allSelBtn.OnTapped = func() {
defer func() {
go_allCancelBtn.Show()
go_allSelBtn.Hide()
}()
for i, v := range this.goList.CachedList.Items {
this.goList.CachedList.Items[i].Checked = true
this.goList.SelItemIds = append(this.goList.SelItemIds, v.Text)
this.goList.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
}
this.goList.ChangeFileCount()
// this.goList.titleLabel.SetText(fmt.Sprintf("(%d/%d)", len(this.goList.selItemIds), this.goList.fileTotal))
this.goList.ItemList.Refresh()
}
// json
this.jsonList.TitleLabel = widget.NewLabel("Json文件")
this.jsonList.TitleLabel.Hide()
// 复选列表
this.jsonList.ItemList = this.jsonList.CreateDefaultCheckList()
// 覆盖 -json
json_allSelBtn := &widget.Button{Icon: theme.CheckButtonCheckedIcon()}
json_allCancelBtn := &widget.Button{Icon: theme.CheckButtonIcon()}
json_overrideBtn := &widget.Button{Text: "覆盖", Icon: theme.ConfirmIcon()}
json_overrideBtn.Hide()
json_allSelBtn.Hide()
// 点击覆盖按钮
json_overrideBtn.OnTapped = func() {
json_overrideBtn.Disable()
defer func() {
json_overrideBtn.Enable()
this.jsonList.ItemList.Refresh()
}()
for _, v := range this.jsonList.SelItemIds {
logrus.WithField("path", v).Debug("select json")
// // logrus.WithField("path2", filepath.Join(projectDir.Text, outputJsonDir.Text, v)).Debug("copy json")
_, err := common.Copy(filepath.Join(tmpDir.Text, "json", v),
filepath.Join(projectDir.Text, outputJsonDir.Text, v))
if err != nil {
logrus.Error(err)
return
}
this.jsonList.DeleteItem(v)
}
this.jsonList.ChangeFileCount()
}
//取消checked
json_allSelBtn.OnTapped = func() {
defer func() {
json_allCancelBtn.Show()
json_allSelBtn.Hide()
}()
list := this.jsonList
list.SelItemIds = []string{}
for i, v := range list.CachedList.Items {
list.CachedList.Items[i].Checked = false
list.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
}
this.jsonList.ChangeFileCount()
list.ItemList.Refresh()
}
//选择所有
json_allCancelBtn.OnTapped = func() {
defer func() {
json_allCancelBtn.Hide()
json_allSelBtn.Show()
}()
list := this.jsonList
for i, v := range list.CachedList.Items {
list.CachedList.Items[i].Checked = true
list.SelItemIds = append(list.SelItemIds, v.Text)
list.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
}
list.ChangeFileCount()
list.ItemList.Refresh()
}
// 资源管理器
explorBtn := &widget.Button{Text: "资源管理器", Icon: theme.FolderIcon()}
explorBtn.OnTapped = func() {
OpenExplor(filepath.Join(workDir.Text, inputDir.Text))
}
genBtn := &widget.Button{Text: "生成", Icon: theme.ConfirmIcon()}
genBtn.OnTapped = func() {
genBtn.Disable()
defer func() {
genBtn.Enable()
go_allCancelBtn.Show()
go_allSelBtn.Hide()
json_allCancelBtn.Show()
json_allSelBtn.Hide()
}()
if runtime.GOOS != "windows" {
dialog.ShowError(errors.New("no support "+runtime.GOOS), toolWin.w)
return
}
if genTypeText == "" {
dialog.ShowError(errors.New("类型未选择"), toolWin.w)
return
}
changeGo := func() {
this.goList.ChangeItem(filepath.Join(tmpDir.Text, "go"), filepath.Join(projectDir.Text, outputCodeDir.Text))
this.goList.TitleLabel.SetText(fmt.Sprintf("(%d/%d个)", len(this.goList.SelItemIds), this.goList.ItemTotal))
go_overrideBtn.Show()
this.goList.TitleLabel.Show()
}
changeJson := func() {
this.jsonList.ChangeItem(filepath.Join(tmpDir.Text, "json"), filepath.Join(projectDir.Text, outputJsonDir.Text))
this.jsonList.TitleLabel.SetText(fmt.Sprintf("(%d/%d)", len(this.jsonList.SelItemIds), this.jsonList.ItemTotal))
json_overrideBtn.Show()
this.jsonList.TitleLabel.Show()
}
gen := func() {
commandStr := `%s -h %s -j cfg -- -d %s --input_data_dir %s --output_code_dir %s --output_data_dir %s --gen_types %s --go:bright_module_name bright -s server`
arg := fmt.Sprintf(commandStr,
filepath.Join(workDir.Text, client.Text),
serverAddr.Text,
filepath.Join(workDir.Text, define.Text),
filepath.Join(workDir.Text, inputDir.Text),
filepath.Join(tmpDir.Text, "go"),
filepath.Join(tmpDir.Text, "json"),
getType(),
)
logrus.Debug(arg)
c := exec.Command("cmd.exe", "/c", arg)
if err := c.Run(); err != nil {
dialog.ShowError(err, toolWin.w)
return
}
// 更新列表
if genTypeText == "go" {
changeGo()
} else if genTypeText == "json" {
changeJson()
} else if genTypeText == "all" {
changeGo()
changeJson()
}
}
dc := dialog.NewConfirm("提示", "Excel文件需要更新吗?", func(b bool) {
if !b {
gen()
return
}
OpenExplor(filepath.Join(workDir.Text, inputDir.Text))
}, toolWin.w)
dc.SetDismissText("不更新")
dc.SetConfirmText("更新")
dc.Show()
}
//使用说明
desBtn := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
quesWin := fyne.CurrentApp().NewWindow("使用说明")
quesWin.SetContent(widget.NewRichTextFromMarkdown(
`
1. SVN更新excel文件
2. 配置Luban(已做可忽略)
3. 选择**生成类型**,单击生成
4. 选择要覆盖的文件
`))
quesWin.Resize(fyne.NewSize(350, 200))
quesWin.SetFixedSize(true)
quesWin.CenterOnScreen()
quesWin.Show()
})
// layout
left := container.NewVBox(form, container.NewHBox(&layout.Spacer{}, desBtn, saveBtn, &layout.Spacer{}, explorBtn, genBtn))
right := container.NewGridWithColumns(2,
container.NewBorder(
container.NewHBox(go_allSelBtn, go_allCancelBtn, go_overrideBtn, widget.NewLabel("Go文件"), this.goList.TitleLabel),
nil, nil, nil,
this.goList.ItemList),
container.NewBorder(
container.NewHBox(json_allSelBtn, json_allCancelBtn, json_overrideBtn, widget.NewLabel("Json文件"), this.jsonList.TitleLabel),
nil, nil, nil,
this.jsonList.ItemList))
content.Objects = append(content.Objects, container.NewGridWithColumns(2, left, right))
this.tabItem.Content = content
return nil
}
// 打开目录
func openFolder(entry *widget.Entry, w fyne.Window) {
dConf := dialog.NewFolderOpen(func(lu fyne.ListableURI, err error) {
if lu == nil {
return
}
entry.Text = lu.Path()
entry.Refresh()
}, w)
luri, _ := fyne_storage.ListerForURI(fyne_storage.NewFileURI("."))
dConf.SetLocation(luri)
dConf.SetConfirmText("打开")
dConf.SetDismissText("取消")
dConf.Resize(fyne.NewSize(750, 500))
dConf.Show()
}
func openFile(entry *widget.Entry, w fyne.Window) {
dConf := dialog.NewFileOpen(func(lu fyne.URIReadCloser, err error) {
if lu == nil {
return
}
entry.SetText(lu.URI().Path())
entry.Refresh()
}, w)
dConf.SetConfirmText("打开")
dConf.SetDismissText("取消")
dConf.SetFilter(fyne_storage.NewExtensionFileFilter([]string{".exe"}))
dConf.Resize(fyne.NewSize(750, 500))
dConf.Show()
}
func (a *appGen) GetAppName() string {
return common.TOOLBAR_GEN
}
func(a *appGen) Icon() fyne.Resource{
return theme.ContentCopyIcon()
}

53
cmd/v2/ui/tool_menu.go Normal file
View File

@ -0,0 +1,53 @@
package ui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
"github.com/sirupsen/logrus"
)
type toolMenu struct {
*fyne.MainMenu
sysMenu *fyne.Menu
appMenus []*fyne.MenuItem
//退出
quite *fyne.MenuItem
// 工具
toolMenu *fyne.Menu
//同步配置
syncConfMenu *fyne.MenuItem
}
func newToolMenu() *toolMenu {
var mm toolMenu
// system
mm.appMenus = make([]*fyne.MenuItem, len(toolRegister))
for i, app := range toolRegister {
mm.appMenus[i] = fyne.NewMenuItem(app.GetAppName(), func() {
err := toolWin.at.openApp(app)
if err != nil {
logrus.Errorf("打开 %s, err:%v", app.GetAppName(), err)
}
})
mm.appMenus[i].Icon = app.Icon()
}
mm.quite = fyne.NewMenuItem("退出", toolWin.quiteHandle)
mm.quite.Icon = theme.LogoutIcon()
mm.quite.IsQuit = true
mm.sysMenu = fyne.NewMenu("应用", mm.appMenus...)
mm.sysMenu.Items = append(mm.sysMenu.Items, fyne.NewMenuItemSeparator(), mm.quite)
//tool
mm.syncConfMenu = fyne.NewMenuItem("同步云配置", toolWin.syncConfig)
mm.toolMenu = fyne.NewMenu("工具",
mm.syncConfMenu,
)
mm.MainMenu = fyne.NewMainMenu(
mm.sysMenu,
mm.toolMenu,
)
return &mm
}

251
cmd/v2/ui/tool_mgo.go Normal file
View File

@ -0,0 +1,251 @@
package ui
import (
"context"
"errors"
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
os_storage "go_dreamfactory/cmd/v2/lib/storage"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/pb"
"strings"
mytheme "go_dreamfactory/cmd/v2/theme"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type appMgo struct {
appAdapter
conf *os_storage.Config
storage os_storage.Storage
db *mongo.Database
client *mongo.Client
scriptEntry *widget.Entry
}
func (this *appMgo) LazyInit(ptService service.PttService, obs observer.Observer) error {
this.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_MGODB, theme.DownloadIcon(), nil)
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
this.scriptEntry = widget.NewMultiLineEntry()
connWinBtn := widget.NewButton("连接设置", this.showConWin)
openConnBtn := widget.NewButton("打开连接", this.openConn)
closeConnBtn := widget.NewButton("关闭连接", this.closeConn)
createServerConfBtn := widget.NewButton("创建服务配置", this.createServerConf)
btns := container.NewHBox(connWinBtn, openConnBtn, createServerConfBtn, layout.NewSpacer(), closeConnBtn)
content.Objects = append(content.Objects, container.NewBorder(btns, nil, nil, nil, this.scriptEntry))
this.tabItem.Content = content
this.storage, _ = os_storage.NewOSStorage()
var err error
this.conf, err = this.storage.LoadConfig()
if err != nil {
logrus.Error(err)
return err
}
return nil
}
func (this *appMgo) openConn() {
if this.conf.MgoDB == nil {
dialog.ShowError(errors.New("MgoDB没有配置"), toolWin.w)
return
}
var option *options.ClientOptions
if (this.conf.MgoDB.User == "" && this.conf.MgoDB.Password == "") &&
this.conf.MgoDB.Host != "" && this.conf.MgoDB.Port != 0 && this.conf.MgoDB.Database != "" {
option = options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%d", this.conf.MgoDB.Host, this.conf.MgoDB.Port))
} else {
option = options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%s@%s:%d", this.conf.MgoDB.User, this.conf.MgoDB.Password,
this.conf.MgoDB.Host, this.conf.MgoDB.Port))
}
client, err := mongo.Connect(context.TODO(), option)
if err != nil {
logrus.Error(err)
return
}
if err2 := client.Ping(context.TODO(), nil); err2 != nil {
logrus.Error("Mongo连接失败", err2)
return
}
this.db = client.Database(this.conf.MgoDB.Database)
this.scriptEntry.Text = this.db.Name() + " 连接成功"
this.scriptEntry.Refresh()
}
func (this *appMgo) closeConn() {
if this.db != nil {
if err := this.db.Client().Disconnect(context.TODO()); err != nil {
this.scriptEntry.Text = "连接关闭失败"
} else {
this.scriptEntry.Text = "连接关闭成功"
}
this.scriptEntry.Refresh()
}
}
func (this *appMgo) createServerConf() {
serverId := widget.NewEntry()
serverName := widget.NewEntry()
owner := widget.NewEntry()
cross := widget.NewEntry()
crossId := widget.NewEntry()
singleserver := widget.NewEntry()
opentime := widget.NewEntry()
redisIsCluster := widget.NewEntry()
redisAddr := widget.NewEntry()
redisPassword := widget.NewEntry()
redisDb := widget.NewEntry()
mongoUrl := widget.NewEntry()
mongoDatabase := widget.NewEntry()
form := widget.NewForm(
widget.NewFormItem("serverid", serverId),
widget.NewFormItem("serverName", serverName),
widget.NewFormItem("owner", owner),
widget.NewFormItem("cross", cross),
widget.NewFormItem("crossId", crossId),
widget.NewFormItem("singleserver", singleserver),
widget.NewFormItem("opentime", opentime),
widget.NewFormItem("redisIsCluster", redisIsCluster),
widget.NewFormItem("redisAddr", redisAddr),
widget.NewFormItem("redisPassword", redisPassword),
widget.NewFormItem("redisDb", redisDb),
widget.NewFormItem("mongoUrl", mongoUrl),
widget.NewFormItem("mongoDatabase", mongoDatabase),
)
if this.conf.ServiceDBInfo != nil {
serverId.Text = this.conf.ServiceDBInfo.Serverid
serverName.Text = this.conf.ServiceDBInfo.ServerName
owner.Text = this.conf.ServiceDBInfo.Owner
cross.Text = this.conf.ServiceDBInfo.Cross
crossId.Text = this.conf.ServiceDBInfo.CrossId
singleserver.Text = this.conf.ServiceDBInfo.Singleserver
opentime.Text = cast.ToString(this.conf.ServiceDBInfo.Opentime)
redisIsCluster.Text = cast.ToString(this.conf.ServiceDBInfo.RedisIsCluster)
redisAddr.Text = strings.Join(this.conf.ServiceDBInfo.RedisAddr, ",")
redisPassword.Text = this.conf.ServiceDBInfo.RedisPassword
redisDb.Text = cast.ToString(this.conf.ServiceDBInfo.RedisDb)
mongoUrl.Text = this.conf.ServiceDBInfo.MongodbUrl
mongoDatabase.Text = this.conf.ServiceDBInfo.MongodbDatabase
}
subBtn := widget.NewButton("保存", func() {
this.conf.ServiceDBInfo = &pb.ServiceDBInfo{
Serverid: serverId.Text,
ServerName: serverName.Text,
Owner: owner.Text,
Cross: cross.Text,
CrossId: crossId.Text,
Singleserver: singleserver.Text,
Opentime: cast.ToInt64(opentime.Text),
RedisIsCluster: cast.ToBool(redisIsCluster.Text),
RedisAddr: strings.Split(redisAddr.Text, ","),
RedisPassword: redisPassword.Text,
RedisDb: cast.ToInt32(redisDb.Text),
MongodbUrl: mongoUrl.Text,
MongodbDatabase: mongoDatabase.Text,
}
if err := this.storage.StoreConfig(this.conf); err != nil {
logrus.Error(err)
}
})
subBtn.Importance = widget.HighImportance
execBtn := widget.NewButton("插入", func() {
if this.db == nil {
common.ShowTip("请先打开连接")
return
}
c := this.db.Collection("serverdata")
if _, err := c.InsertOne(context.TODO(), this.conf.ServiceDBInfo); err != nil {
logrus.Error(err)
return
}
this.scriptEntry.Text = "插入成功"
this.scriptEntry.Refresh()
})
dconf := dialog.NewCustom("配置", "关闭", container.NewVBox(form, container.NewHBox(layout.NewSpacer(), subBtn, execBtn)), toolWin.w)
dconf.Resize(fyne.NewSize(400, 600))
dconf.Show()
}
func (this *appMgo) showConWin() {
connName := widget.NewEntry()
address := widget.NewEntry()
port := widget.NewEntry()
user := widget.NewEntry()
password := widget.NewPasswordEntry()
database := widget.NewEntry()
form := widget.NewForm(
widget.NewFormItem("Name", connName),
widget.NewFormItem("Address", container.NewBorder(nil, nil, nil, port, address)),
widget.NewFormItem("User", user),
widget.NewFormItem("Password", password),
widget.NewFormItem("Database", database),
)
if this.conf.MgoDB != nil {
connName.Text = this.conf.MgoDB.Name
address.Text = this.conf.MgoDB.Host
port.Text = cast.ToString(this.conf.MgoDB.Port)
user.Text = this.conf.MgoDB.User
password.Text = this.conf.MgoDB.Password
database.Text = this.conf.MgoDB.Database
}
form.OnSubmit = func() {
this.conf.MgoDB = &os_storage.MgoDB{
Name: connName.Text,
Host: address.Text,
Port: cast.ToInt32(port.Text),
User: user.Text,
Password: password.Text,
Database: database.Text,
}
if connName.Text == "" || address.Text == "" || port.Text == "" {
common.ShowTip("请填写完整")
return
}
if err := this.storage.StoreConfig(this.conf); err != nil {
logrus.Error(err)
return
}
dialog.ShowInformation("提示", "保存成功", toolWin.w)
}
form.SubmitText = "保存"
dconf := dialog.NewCustom("设置链接", "关闭", form, toolWin.w)
dconf.Resize(fyne.NewSize(400, 200))
dconf.Show()
}
func (a *appMgo) GetAppName() string {
return common.TOOLBAR_MGODB
}
func (a *appMgo) Icon() fyne.Resource {
return mytheme.ResourceMongodbJpg
}

73
cmd/v2/ui/tool_newlock.go Normal file
View File

@ -0,0 +1,73 @@
package ui
import (
"errors"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"image/color"
"strconv"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sethvargo/go-password/password"
)
type appLock struct {
appAdapter
}
func (this *appLock) LazyInit(ptService service.PttService, obs observer.Observer) error {
this.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_SEC, theme.DownloadIcon(), nil)
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
title := canvas.NewText("输入密码长度", color.White)
input := widget.NewEntry()
input.SetPlaceHolder("长度范围10-63")
text := canvas.NewText("", color.White)
text.TextSize = 16
btn := widget.NewButtonWithIcon("生成", theme.ConfirmIcon(), func() {
passwordLength, _ := strconv.Atoi(input.Text)
//Avoids length errors
if passwordLength <= 10 || passwordLength >= 63 {
passwordLength = 10
}
text.Text = password.MustGenerate(passwordLength, 5, 5, false, false)
text.Refresh()
})
copybtn := widget.NewButtonWithIcon("复制密码", theme.ContentCopyIcon(), func() {
if text.Text == "" {
dialog.ShowError(errors.New("请生成密码"), toolWin.w)
return
}
toolWin.w.Clipboard().SetContent(text.Text)
common.ShowTip("已复制到剪贴板")
})
c := container.NewVBox(title, input, text, btn, copybtn)
content.Objects = append(content.Objects, c)
this.tabItem.Content = content
return nil
}
func (a *appLock) GetAppName() string {
return common.TOOLBAR_SEC
}
func (a *appLock) Icon() fyne.Resource {
return theme.DownloadIcon()
}

263
cmd/v2/ui/tool_pb.go Normal file
View File

@ -0,0 +1,263 @@
package ui
import (
"errors"
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
os_storage "go_dreamfactory/cmd/v2/lib/storage"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/utils"
"io/ioutil"
"os/exec"
"runtime"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
fyne_storage "fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
type appPbGen struct {
appAdapter
folderList *folderList
}
func (this *appPbGen) LazyInit(ptService service.PttService, obs observer.Observer) error {
this.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_PB, theme.ContentAddIcon(), nil)
this.folderList = NewFolderList()
countLabel := widget.NewLabel("")
//load
storage, _ := os_storage.NewOSStorage()
conf, err := storage.LoadConfig()
if err != nil {
return err
}
// 打开目录
openFolder := func(entry *widget.Entry) {
dConf := dialog.NewFolderOpen(func(lu fyne.ListableURI, err error) {
if lu == nil {
return
}
entry.Text = lu.Path()
entry.Refresh()
}, toolWin.w)
luri, _ := fyne_storage.ListerForURI(fyne_storage.NewFileURI("."))
dConf.SetLocation(luri)
dConf.SetConfirmText("打开")
dConf.SetDismissText("取消")
dConf.Resize(fyne.NewSize(750, 500))
dConf.Show()
}
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
protoDir := widget.NewEntry()
protoDir.PlaceHolder = "proto目录"
protoDir.OnChanged = func(s string) {
this.folderList.initItem(protoDir.Text)
countLabel.SetText(fmt.Sprintf("总数:%v", this.folderList.fileTotal))
}
outDir := widget.NewEntry()
outDir.PlaceHolder = "go输出目录"
form := widget.NewForm(
widget.NewFormItem("proto目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(protoDir)
this.folderList.initItem(protoDir.Text)
}), protoDir)),
widget.NewFormItem("输出目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(outDir)
}), outDir)),
)
if conf.PbConf != nil {
protoDir.SetText(conf.PbConf.ProtoDir)
outDir.SetText(conf.PbConf.OutputDir)
}
//打开资源管理器
explorBtn := &widget.Button{Text: "资源管理器", Icon: theme.FolderIcon()}
explorBtn.OnTapped = func() {
OpenExplor(protoDir.Text)
}
genBtn := &widget.Button{Text: "生成", Icon: theme.ConfirmIcon()}
genBtn.OnTapped = func() {
genBtn.Disable()
defer func() {
genBtn.Enable()
}()
if runtime.GOOS != "windows" {
dialog.ShowError(errors.New("no support "+runtime.GOOS), toolWin.w)
return
}
dialog.ShowConfirm("确认",
strings.Join(this.folderList.selItemIds, ","), func(b bool) {
if !b {
return
}
if outDir.Text == "" {
dialog.ShowError(errors.New("请设置输出目录"), toolWin.w)
return
}
if protoDir.Text == "" {
dialog.ShowError(errors.New("请设置proto目录"), toolWin.w)
return
}
commandStr1 := `protoc --proto_path=%s --go_out=%s --go_opt=paths=import %s\*.proto`
commandStr2 := `protoc --proto_path=%s --go_out=%s --go_opt=paths=import %s\%s\*.proto`
var arg1, arg2 string
arg1 = fmt.Sprintf(commandStr1,
protoDir.Text,
outDir.Text,
protoDir.Text,
)
for _, name := range this.folderList.selItemIds {
arg2 = fmt.Sprintf(commandStr2,
protoDir.Text,
outDir.Text,
protoDir.Text,
name,
)
}
logrus.Debug(arg1)
logrus.Debug(arg2)
c1 := exec.Command("cmd.exe", "/c", arg1)
if err := c1.Run(); err != nil {
dialog.ShowError(err, toolWin.w)
return
}
c2 := exec.Command("cmd.exe", "/c", arg2)
if err := c2.Run(); err != nil {
dialog.ShowError(err, toolWin.w)
return
}
}, toolWin.w)
}
refreshBtn := widget.NewButtonWithIcon("", theme.ViewRefreshIcon(), func() {
this.folderList.cachedList = common.NewList("")
this.folderList.selItemIds = []string{}
this.folderList.initItem(protoDir.Text)
countLabel.SetText(fmt.Sprintf("总数:%v", this.folderList.fileTotal))
})
confBtn := &widget.Button{Text: "保存配置", Icon: theme.DocumentSaveIcon()}
confBtn.OnTapped = func() {
pbconf := &os_storage.ProtobufConfig{
ProtoDir: protoDir.Text,
OutputDir: outDir.Text,
}
conf.PbConf = pbconf
if err := storage.StoreConfig(conf); err != nil {
logrus.WithField("err", err).Debug("保存配置")
return
}
logrus.Debug("save pb conf")
}
this.folderList.itemList = this.folderList.createList()
// layout
c := container.NewBorder(
form,
container.NewHBox(confBtn, explorBtn, genBtn, refreshBtn, layout.NewSpacer(), countLabel), nil, nil,
container.NewMax(
container.NewVScroll(this.folderList.itemList),
),
)
content.Objects = append(content.Objects, c)
this.tabItem.Content = content
return nil
}
func (a *appPbGen) GetAppName() string {
return common.TOOLBAR_PB
}
func (a *appPbGen) Icon() fyne.Resource {
return theme.ContentAddIcon()
}
type folderList struct {
selItemIds []string //选择的ID
cachedList common.List
itemList *widget.List
fileTotal int //文件总数
}
func NewFolderList() *folderList {
return &folderList{
cachedList: common.NewList(""),
}
}
func (f *folderList) createList() *widget.List {
return widget.NewList(
func() int {
return len(f.cachedList.Items)
},
func() fyne.CanvasObject {
return widget.NewCheck("Template", func(b bool) {})
},
func(id widget.ListItemID, item fyne.CanvasObject) {
c, _ := item.(*widget.Check)
im := f.cachedList.Items[id]
c.Text = im.Text
c.Checked = im.Checked
c.OnChanged = func(b bool) {
if b {
f.selItemIds = append(f.selItemIds, c.Text)
} else {
f.selItemIds = utils.DeleteString(f.selItemIds, c.Text)
}
f.cachedList.Items[id].Checked = b
f.itemList.Refresh()
}
c.Refresh()
},
)
}
func (f *folderList) initItem(dir string) {
files, err := ioutil.ReadDir(dir)
if err != nil {
logrus.Error(err)
return
}
for _, file := range files {
if file.IsDir() {
if file.Name() == ".vscode"||
file.Name() == ".svn" {
continue
}
fm := common.Item{
Text: file.Name(),
Checked: false,
}
f.cachedList.Items = append(f.cachedList.Items, fm)
// f.selItemIds = append(f.selItemIds, fm.Id)
f.fileTotal++
// logrus.Debugf("%v", fm.Id)
}
}
}

180
cmd/v2/ui/tool_ping.go Normal file
View File

@ -0,0 +1,180 @@
package ui
import (
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
os_storage "go_dreamfactory/cmd/v2/lib/storage"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"net"
"runtime"
"strings"
"sync"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/go-ping/ping"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type portStatus struct {
port int
err string
}
type appPing struct {
appAdapter
resultCh chan portStatus
}
func (this *appPing) LazyInit(ptService service.PttService, obs observer.Observer) error {
this.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_PING, theme.DownloadIcon(), nil)
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
targetHost := widget.NewEntry()
targetHost.PlaceHolder = "目标主机Ip"
//load
storage, _ := os_storage.NewOSStorage()
conf, err := storage.LoadConfig()
if err != nil {
return err
}
portEntry := widget.NewMultiLineEntry()
portEntry.Text = "20,21,22,80,1521,2379,2380,3306,6379,8080,8020,8500,9000,9090,13306,50070,27019,10013,8300,8301,8600,10012,10011"
saveBtn := widget.NewButton("保存配置", func() {
pingConf := &os_storage.PingConf{
Host: targetHost.Text,
Ports: portEntry.Text,
}
conf.PingConf = pingConf
if err := storage.StoreConfig(conf); err != nil {
logrus.WithField("err", err).Debug("保存配置")
return
}
})
form := widget.NewForm(
widget.NewFormItem("主机", targetHost),
widget.NewFormItem("端口", container.NewBorder(nil, nil, nil, saveBtn, portEntry)),
)
if conf.PingConf != nil {
targetHost.SetText(conf.PingConf.Host)
portEntry.SetText(conf.PingConf.Ports)
}
// result
result := widget.NewMultiLineEntry()
result.Disable()
//初始化端口对应说明字典
portDesc := map[int]string{
7891: "websocket",
10013: "MongoDB",
10011: "Redis",
10012: "Consul",
9567: "gateway",
9568: "mainte",
9569: "worker",
9897: "battle",
8001: "cross_mainte",
9570: "cross_worker",
8000: "web",
9571: "cross_web",
}
form.OnSubmit = func() {
result.Text = ""
if portEntry.Text == "" {
return
}
this.resultCh = make(chan portStatus)
ports := strings.Split(portEntry.Text, ",")
pinger, err := ping.NewPinger(targetHost.Text)
if err != nil {
panic(err)
}
pinger.Count = 3
if runtime.GOOS == "windows" {
pinger.SetPrivileged(true)
}
var msgs []string
pinger.OnRecv = func(p *ping.Packet) {
msgs = append(msgs, fmt.Sprintf("来自:%s 的回复 字节=%d 时间=%dms TTL=%d",
p.IPAddr.String(),
p.Nbytes,
p.Rtt.Milliseconds(),
p.Ttl))
result.Text = strings.Join(msgs, "\n")
result.Refresh()
}
if err := pinger.Run(); err != nil {
logrus.Error(err)
}
ip, err := net.ResolveIPAddr("ip", targetHost.Text)
if err != nil {
logrus.Error(err)
return
}
this.ping(ip.String(), ports)
for p := range this.resultCh {
desc, ok := portDesc[p.port]
if !ok {
desc = "未知"
}
msgs = append(msgs, fmt.Sprintf("%s 端口:%d %s", desc, p.port, p.err))
result.Text = strings.Join(msgs, "\n")
result.Refresh()
}
}
form.Items[1].HintText = "多个端口使用英文,号分隔"
form.SubmitText = "Ping"
result.OnChanged = func(s string) {
result.Refresh()
}
content.Objects = append(content.Objects, container.NewBorder(form, nil, nil, nil, result))
this.tabItem.Content = content
return nil
}
func (this *appPing) ping(targetHost string, ports []string) {
var wg sync.WaitGroup
wg.Add(len(ports))
for _, port := range ports {
go func(p int) {
defer wg.Done()
_, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", targetHost, p), time.Millisecond*500)
var errStr string
if err == nil {
errStr = "OK"
} else {
errStr = "ERR"
logrus.Error(err)
}
this.resultCh <- portStatus{port: p, err: errStr}
}(cast.ToInt(port))
}
go func() {
defer close(this.resultCh)
wg.Wait()
}()
}
func (a *appPing) GetAppName() string {
return common.TOOLBAR_PING
}
func (a *appPing) Icon() fyne.Resource {
return theme.ComputerIcon()
}

788
cmd/v2/ui/tool_term.go Normal file
View File

@ -0,0 +1,788 @@
package ui
import (
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
os_storage "go_dreamfactory/cmd/v2/lib/storage"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"image/color"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/pkg/errors"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type appTerm struct {
appAdapter
obs observer.Observer
sshService *service.SSHService
jsonList *common.ItemList //json列表
cProgress *widget.ProgressBarInfinite //连接进度条进度条
upProgress *widget.ProgressBar //上传进度条
endProgress sync.WaitGroup
downloadList *common.ItemList //download列表
}
func (app *appTerm) LazyInit(ptService service.PttService, obs observer.Observer) error {
app.obs = obs
app.sshService = &service.SSHService{}
app.jsonList = common.NewItemList()
//progress
app.cProgress = widget.NewProgressBarInfinite()
app.cProgress.Hide()
app.upProgress = widget.NewProgressBar()
app.upProgress.Hide()
app.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_TERM, theme.MailSendIcon(), nil)
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
//load term conf
storage, _ := os_storage.NewOSStorage()
conf, err := storage.LoadConfig()
if err != nil {
return err
}
//left-top
localDir := widget.NewEntry()
localDir.PlaceHolder = `本地Json目录`
remoteDir := widget.NewEntry()
remoteDir.PlaceHolder = `远程目录`
remoteDir.Text = "/home/liwei/go_dreamfactory/bin/json"
//config
ip := widget.NewEntry()
ip.PlaceHolder = "10.0.0.9"
port := widget.NewEntry()
port.Text = "22" //default port
port.PlaceHolder = "端口"
userName := widget.NewEntry()
userName.PlaceHolder = "账号"
password := widget.NewPasswordEntry()
password.PlaceHolder = "密码"
passwordItem := &widget.FormItem{Text: "密码:", Widget: password}
configForm := widget.NewForm(
&widget.FormItem{Text: "IP:", Widget: container.NewBorder(nil, nil, nil, port, ip)},
&widget.FormItem{Text: "用户名:", Widget: userName},
passwordItem,
widget.NewFormItem("Json目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(localDir, toolWin.w)
}), localDir)),
widget.NewFormItem("远程目录", remoteDir),
)
configForm.Items[4].HintText = "谨慎修改远程目录"
//svn form
lubanAddr := widget.NewEntry()
lubanAddr.PlaceHolder = "Luban服务IP"
workDir := widget.NewEntry()
workDir.PlaceHolder = "工作目录"
lubanCli := widget.NewEntry()
lubanCli.PlaceHolder = "luban客户端可执行文件"
//define
define := widget.NewEntry()
define.PlaceHolder = "定义文件"
define.Text = `Defines\\__root__.xml`
dataDir := widget.NewEntry()
dataDir.PlaceHolder = "Data目录"
jsonDir := widget.NewEntry()
jsonDir.PlaceHolder = "Json目录"
svnForm := widget.NewForm(
&widget.FormItem{Text: "服务地址", Widget: lubanAddr},
&widget.FormItem{Text: "工作目录", Widget: container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(workDir, toolWin.w)
}), workDir)},
&widget.FormItem{Text: "LubanCli", Widget: lubanCli},
&widget.FormItem{Text: "Data目录", Widget: dataDir},
&widget.FormItem{Text: "JSON目录", Widget: jsonDir},
)
//set input entry
if conf.SyncConf != nil {
ip.Text = conf.SyncConf.Ip
port.Text = conf.SyncConf.Port
userName.Text = conf.SyncConf.UserName
password.Text = conf.SyncConf.Password
localDir.Text = conf.SyncConf.LocalDir
remoteDir.Text = conf.SyncConf.RemoteDir
// //
lubanAddr.Text = conf.SyncConf.ServerIp
workDir.Text = conf.SyncConf.WorkDir
lubanCli.Text = conf.SyncConf.LubanCli
dataDir.Text = conf.SyncConf.DataDir
jsonDir.Text = conf.SyncConf.JsonDir
} else {
conf.SyncConf = &os_storage.SyncConfig{
Ip: "10.0.0.9",
UserName: "root",
Password: "Legu.cc()123",
RemoteDir: "/home/liwei/go_dreamfactory/bin/json/",
ServerIp: "10.0.1.11",
LubanCli: `Luban.Client\Luban.Client.exe`,
DataDir: "Datas",
JsonDir: `output\json`,
}
}
// 解决文本没显示的问题
passwordItem.Widget.Refresh()
ip.Refresh()
localDir.Refresh()
workDir.Refresh()
// save func
saveFunc := func() {
syncConf := &os_storage.SyncConfig{
Ip: ip.Text,
UserName: userName.Text,
Password: password.Text,
Port: port.Text,
LocalDir: localDir.Text,
RemoteDir: remoteDir.Text,
ServerIp: lubanAddr.Text,
WorkDir: workDir.Text,
LubanCli: lubanCli.Text,
DataDir: dataDir.Text,
JsonDir: jsonDir.Text,
}
conf.SyncConf = syncConf
if err := storage.StoreConfig(conf); err != nil {
logrus.Error(err)
}
}
saveBtn1 := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), saveFunc)
saveBtn2 := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), saveFunc)
// conn func
connBtn := &widget.Button{Text: "连接", Icon: theme.MailForwardIcon()}
disConnBtn := &widget.Button{Text: "断开", Icon: theme.CancelIcon()}
syncBtn := &widget.Button{Text: "同步JSON", Icon: theme.ConfirmIcon()}
refreshBtn := &widget.Button{Text: "刷新", Icon: theme.ViewRefreshIcon()}
svnBtn := &widget.Button{Text: "SVN更新", Icon: theme.ConfirmIcon()}
excelBtn := &widget.Button{Text: "更新Excel", Icon: theme.FolderIcon()}
explorBtn := &widget.Button{Text: "资源管理器", Icon: theme.FolderIcon()}
dlBtn := widget.NewButtonWithIcon("", theme.DownloadIcon(), nil)
searchEntry := widget.NewEntry()
// 全选/全取消
allSelBtn := &widget.Button{Icon: theme.CheckButtonCheckedIcon()}
allCancelBtn := &widget.Button{Icon: theme.CheckButtonIcon()}
allSelBtn.Hide()
// 连接
connBtn.OnTapped = func() {
app.cProgress.Show()
app.cProgress.Start()
ciphers := []string{}
if ip.Text == "" {
dialog.ShowError(errors.New("IP未填写"), toolWin.w)
return
}
if userName.Text == "" {
dialog.ShowError(errors.New("账号未填写"), toolWin.w)
return
}
if password.Text == "" {
dialog.ShowError(errors.New("密码未填写"), toolWin.w)
return
}
if port.Text == "" {
dialog.ShowError(errors.New("端口未填写"), toolWin.w)
return
}
if localDir.Text == "" {
dialog.ShowError(errors.New("JSON目录未填写"), toolWin.w)
return
}
if remoteDir.Text == "" {
dialog.ShowError(errors.New("远程目录未填写"), toolWin.w)
return
}
connBtn.Disable()
err := app.sshService.Connect(userName.Text, password.Text, ip.Text, "", cast.ToInt(port.Text), ciphers)
if err != nil {
dialog.ShowError(err, toolWin.w)
app.cProgress.Stop()
app.cProgress.Hide()
connBtn.Enable()
return
} else {
app.jsonList.LoadItem(localDir.Text)
app.cProgress.Stop()
app.cProgress.Hide()
disConnBtn.Enable()
syncBtn.Enable()
app.upProgress.Hide()
allCancelBtn.Show()
allSelBtn.Hide()
refreshBtn.Enable()
dlBtn.Enable()
}
}
// 刷新JSON列表
refreshBtn.Disable()
reloadItem := func() {
defer func() {
syncBtn.Enable()
allSelBtn.Hide()
allCancelBtn.Show()
}()
app.jsonList.LoadItem(localDir.Text)
}
refreshBtn.OnTapped = reloadItem
// 断开
disConnBtn.Disable()
disConnBtn.OnTapped = func() {
defer func() {
app.jsonList.Reset()
connBtn.Enable()
disConnBtn.Disable()
syncBtn.Disable()
allSelBtn.Hide()
allCancelBtn.Show()
refreshBtn.Disable()
dlBtn.Disable()
}()
app.sshService.Close()
}
//资源管理器
openExplor := func(dir string) {
if runtime.GOOS == "windows" {
if err := exec.Command("explorer", filepath.Join(dir)).Start(); err != nil {
dialog.ShowError(errors.WithMessage(err, "请确认Json目录是否填写正确"), toolWin.w)
return
}
}
}
explorBtn.OnTapped = func() {
// logrus.Debug(localDir.Text)
if localDir.Text == "" {
common.ShowTip("Json目录必须填写")
} else {
openExplor(localDir.Text)
}
}
//使用说明
helpBtn1 := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
quesWin := fyne.CurrentApp().NewWindow("同步JSON使用说明")
quesWin.SetContent(widget.NewRichTextFromMarkdown(
`
* 同步Json的操作仅限于数值热更,非结构热更
* 将json文件热更到内网测试环境
* 提示:
* IP:10.0.0.9 22 (保持一致)
* 用户名:root(保持一致)
* 密码:(服务端同学提供)
* Json目录:(json文件目录全路径)
* 远程目录:/home/liwei/go_dreamfactory/bin/json/(保持一致)
* 全部填写完点击保存配置
* 点击连接,右侧面板选择要同步的文件
`))
quesWin.Resize(fyne.NewSize(350, 200))
quesWin.SetFixedSize(true)
quesWin.CenterOnScreen()
quesWin.Show()
})
helpBtn2 := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
quesWin := fyne.CurrentApp().NewWindow("SVN更新使用说明")
c := widget.NewRichTextFromMarkdown(
`
* 基于Luban工具生成json
* 建议每次同步JSON之前先点击SVN更新
* 提示:
* 服务地址:10.0.1.11 (保持一致)
* 工作目录:svn下ExcelFile目录全路径
* LubanCli:Luban.Client\Luban.Client.exe (保持一致)
* Data目录:Datas(保持一致)
* JSON目录:output\json(保持一致)
* 全部填写完点击保存配置
* 点击SVN更新生成json文件
`,
)
quesWin.SetContent(c)
quesWin.Resize(fyne.NewSize(350, 200))
quesWin.SetFixedSize(true)
quesWin.CenterOnScreen()
quesWin.Show()
})
//同步JSON
syncBtn.Disable()
syncNext := func() {
defer func() {
syncBtn.Enable()
app.upProgress.Hide()
reloadItem()
dialog.ShowInformation("提示", "所有文件均上传完毕,需等1-2分钟待文件热更", toolWin.w)
}()
syncBtn.Disable()
app.upProgress.Show()
app.upProgress.SetValue(0)
len := len(app.jsonList.SelItemIds)
num := 0.0
increment := func(wg *sync.WaitGroup) {
num += float64(1) / float64(len)
app.upProgress.SetValue(num)
wg.Done()
}
for _, fileName := range app.jsonList.SelItemIds {
app.endProgress.Add(1)
go func(fn string) {
// time.Sleep(time.Second * time.Duration(rand.Intn(3)))
if err := app.sshService.ScpCopy(filepath.Join(localDir.Text, fn), remoteDir.Text); err != nil {
logrus.WithField("err", err).Error("同步json")
common.ShowTip(err.Error())
}
increment(&app.endProgress)
// 移除已上传的
app.jsonList.DeleteItem(fn)
common.ShowTip(fmt.Sprintf("%s 成功上传", fn))
}(fileName)
}
app.endProgress.Wait()
app.upProgress.SetValue(1)
}
syncBtn.OnTapped = func() {
if app.sshService.Client == nil {
dialog.ShowError(errors.New("请先连接终端"), toolWin.w)
return
}
//SVN更新提示
dc := dialog.NewConfirm("提示", "是否要进行SVN更新", func(b bool) {
if b {
common.ShowTip("单击【SVN更新】按钮进行更新")
svnBtn.FocusGained()
return
} else {
if len(app.jsonList.SelItemIds) == 0 {
common.ShowTip("没有选择任何文件,或尝试点击【刷新】")
return
}
syncNext()
}
}, toolWin.w)
dc.SetConfirmText("必须的")
dc.SetDismissText("我拿生命担保无需更新")
dc.Show()
}
// excel更新
excelBtn.OnTapped = func() {
if workDir.Text == "" {
common.ShowTip("工作目录必须填写")
} else {
openExplor(workDir.Text)
}
}
// SVN更新
svnNext := func() {
defer func() {
app.cProgress.Hide()
app.cProgress.Stop()
svnBtn.Enable()
}()
svnBtn.Disable()
app.cProgress.Show()
app.cProgress.Start()
commandStr := `%s -h %s -j cfg -- -d %s --input_data_dir %s --output_data_dir %s --gen_types data_json -s server`
arg := fmt.Sprintf(commandStr,
filepath.Join(workDir.Text, lubanCli.Text),
lubanAddr.Text,
filepath.Join(workDir.Text, define.Text),
filepath.Join(workDir.Text, dataDir.Text),
filepath.Join(workDir.Text, jsonDir.Text),
)
logrus.Debug(arg)
c := exec.Command("cmd.exe", "/c", arg)
if err := c.Run(); err != nil {
dialog.ShowError(err, toolWin.w)
return
}
}
svnBtn.OnTapped = func() {
//提示更新excel
dc := dialog.NewConfirm("提示", "是否要更新excel?", func(b bool) {
if b {
//打开资源管理器
openExplor(workDir.Text)
return
} else {
svnNext()
}
}, toolWin.w)
dc.SetConfirmText("必须的")
dc.SetDismissText("我拿生命担保无需更新")
dc.Show()
}
// 全部取消
allCancelBtn.OnTapped = func() {
defer func() {
allCancelBtn.Hide()
allSelBtn.Show()
}()
for i, v := range app.jsonList.CachedList.Items {
app.jsonList.CachedList.Items[i].Checked = true
app.jsonList.SelItemIds = append(app.jsonList.SelItemIds, v.Text)
app.jsonList.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
}
app.jsonList.ItemList.Refresh()
}
// 全选
allSelBtn.OnTapped = func() {
defer func() {
allCancelBtn.Show()
allSelBtn.Hide()
}()
app.jsonList.SelItemIds = []string{}
for i, v := range app.jsonList.CachedList.Items {
app.jsonList.CachedList.Items[i].Checked = false
app.jsonList.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
}
app.jsonList.ItemList.Refresh()
}
// 搜索
searchEntry.PlaceHolder = "搜索"
searchEntry.OnChanged = func(s string) {
if app.sshService.Client == nil {
dialog.ShowError(errors.New("请先连接终端"), toolWin.w)
return
}
if s == "" {
reloadItem()
} else {
// go func() {
newList := []common.Item{}
for _, v := range app.jsonList.SearchItem {
if strings.Contains(v.Text, s) {
newList = append(newList, v)
}
}
app.jsonList.CachedList.Items = newList
app.jsonList.ItemList.Refresh()
// }()
}
}
// 下载日志
dlBtn.Disable()
dlBtn.OnTapped = func() {
w := app.createDownloadWindow()
w.Show()
w.SetCloseIntercept(func() {
dlBtn.Enable()
w.Close()
})
dlBtn.Disable()
}
// 创建json列表
app.jsonList.ItemList = app.jsonList.CreateDefaultCheckList()
btns1 := container.NewHBox(helpBtn1, dlBtn, &layout.Spacer{}, saveBtn1, connBtn, disConnBtn)
btns2 := container.NewHBox(helpBtn2, &layout.Spacer{}, saveBtn2, excelBtn, svnBtn)
split := container.NewHSplit(container.NewVBox(canvas.NewText("---只能在非上产环境!!!同步Json的操作仅限于数值热更,非结构热更---", color.RGBA{255, 0, 0, 255}), configForm,
btns1, svnForm, btns2, app.cProgress), container.NewBorder(
container.NewBorder(nil, nil, container.NewHBox(allCancelBtn, allSelBtn, syncBtn, refreshBtn), container.NewHBox(explorBtn), searchEntry),
nil, nil, nil, app.jsonList.ItemList))
split.Offset = 0.45
content.Objects = append(content.Objects, container.NewBorder(nil, app.upProgress, nil, nil, split))
app.tabItem.Content = content
return nil
}
func (a *appTerm) GetAppName() string {
return common.TOOLBAR_TERM
}
func (a *appTerm) Icon() fyne.Resource {
return theme.MailSendIcon()
}
func OpenExplor(dir string) {
if dir == "" {
common.ShowTip("资源管理器路径错误")
return
}
if runtime.GOOS == "windows" {
if err := exec.Command("explorer", filepath.Join(dir)).Start(); err != nil {
dialog.ShowError(errors.WithMessage(err, "请确认Json目录是否填写正确"), toolWin.w)
return
}
}
}
func (a *appTerm) OnClose() bool {
dialog.ShowConfirm("关闭终端", "你希望断开连接吗?", func(b bool) {
if !b {
return
}
if a.sshService.Client != nil {
a.sshService.Client.Close()
}
}, toolWin.w)
return true
}
func (a *appTerm) createDownloadWindow() fyne.Window {
w := toolWin.app.NewWindow("日志")
a.downloadList = common.NewItemList()
a.downloadList.ItemList = a.downloadList.CreateDownloadList()
remoteLogDirEntry := widget.NewEntry()
remoteLogDirEntry.PlaceHolder = "下载到"
remoteLogDirEntry.Text = "/home/liwei/go_dreamfactory/bin/log/"
saveDirEntry := widget.NewEntry()
saveDirEntry.PlaceHolder = "保存目录"
editorEntry := widget.NewEntry()
editorEntry.PlaceHolder = "编辑器可执行文件路径"
// config
confForm := widget.NewForm(
widget.NewFormItem("远程目录", remoteLogDirEntry),
widget.NewFormItem("下载目录", saveDirEntry),
widget.NewFormItem("编辑器", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFile(editorEntry, w)
}), editorEntry)),
)
// 进度条
downloadProgress := widget.NewProgressBarInfinite()
var (
storage os_storage.Storage
conf *os_storage.Config
err error
)
defer func() {
downloadProgress.Hide()
//加载配置
storage, _ = os_storage.NewOSStorage()
conf, err = storage.LoadConfig()
if err != nil {
return
}
// sshConf = service.GetDbService().GetSSHConf(common.BUCKET_SSHCONF)
if conf.SyncConf != nil {
if conf.SyncConf.LogDir == "" {
remoteLogDirEntry.Text = "/home/liwei/go_dreamfactory/bin/log/"
} else {
remoteLogDirEntry.Text = conf.SyncConf.LogDir
}
saveDirEntry.Text = conf.SyncConf.SaveDir
editorEntry.Text = conf.SyncConf.Editor
} else {
conf.SyncConf = &os_storage.SyncConfig{}
}
remoteLogDirEntry.Refresh()
saveDirEntry.Refresh()
editorEntry.Refresh()
//load list
if remoteLogDirEntry.Text == "" {
return
}
files, err := a.sshService.GetRemoteDir(remoteLogDirEntry.Text)
if err != nil {
dialog.ShowError(err, toolWin.w)
return
}
for _, f := range files {
a.downloadList.AddItem(common.Item{Id: f.FileName, Text: f.FileName, Size: f.Size})
}
}()
//下载
allDownloadBtn := widget.NewButtonWithIcon("下载", theme.DownloadIcon(), nil)
DownloadDirBtn := widget.NewButtonWithIcon("打开下载目录", theme.FolderIcon(), nil)
EditorBtn := widget.NewButtonWithIcon("打开编辑器", theme.FolderIcon(), nil)
saveConfBtn := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), func() {
// 保存配置
conf.SyncConf.LogDir = remoteLogDirEntry.Text
conf.SyncConf.SaveDir = saveDirEntry.Text
conf.SyncConf.Editor = editorEntry.Text
if err := storage.StoreConfig(conf); err != nil {
logrus.Error(err)
}
})
allSelBtn := &widget.Button{Icon: theme.CheckButtonCheckedIcon()}
allCancelBtn := &widget.Button{Icon: theme.CheckButtonIcon()}
allSelBtn.Hide()
// 全部取消
allCancelBtn.OnTapped = func() {
defer func() {
allCancelBtn.Hide()
allSelBtn.Show()
}()
dlist := a.downloadList
for i, v := range dlist.CachedList.Items {
dlist.CachedList.Items[i].Checked = true
dlist.SelItemIds = append(dlist.SelItemIds, v.Text)
dlist.ItemList.UpdateItem(i, container.NewHBox(
widget.NewCheck(v.Text, nil),
&layout.Spacer{},
widget.NewLabel(common.ConvertFileSize(v.Size))))
}
dlist.ItemList.Refresh()
}
// 全选
allSelBtn.OnTapped = func() {
defer func() {
allCancelBtn.Show()
allSelBtn.Hide()
}()
dlist := a.downloadList
dlist.SelItemIds = []string{}
for i, v := range dlist.CachedList.Items {
dlist.CachedList.Items[i].Checked = false
dlist.ItemList.UpdateItem(i, container.NewHBox(
widget.NewCheck(v.Text, nil),
&layout.Spacer{},
widget.NewLabel(common.ConvertFileSize(v.Size))))
}
dlist.ItemList.Refresh()
}
// 打开下载目录
DownloadDirBtn.OnTapped = func() {
OpenExplor(saveDirEntry.Text)
}
EditorBtn.OnTapped = func() {
if editorEntry.Text == "" {
common.ShowTip("请配置编辑器")
return
}
if runtime.GOOS == "windows" {
for _, v := range a.downloadList.SelItemIds {
if err := exec.Command(editorEntry.Text, filepath.Join(saveDirEntry.Text, v)).Start(); err != nil {
dialog.ShowError(errors.WithMessage(err, "请确认编辑器目录是否填写正确"), toolWin.w)
return
}
}
}
}
//使用说明
helpBtn := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
quesWin := fyne.CurrentApp().NewWindow("日志使用说明")
quesWin.SetContent(widget.NewRichTextFromMarkdown(
`
* 下载远程服务日志到本地分析
* 提示:
* 远程目录 (保持一致)
* 下载目录:填写本地任意目录
* 编辑器:选择已安装的编辑器(例如:notepad++Sublime)
* 操作:
* 选择文件,点击打开编辑器
`))
quesWin.Resize(fyne.NewSize(350, 200))
quesWin.SetFixedSize(true)
quesWin.CenterOnScreen()
quesWin.Show()
})
var wg sync.WaitGroup
//download
allDownloadBtn.OnTapped = func() {
downloadProgress.Show()
downloadProgress.Start()
selItems := a.downloadList.SelItemIds
if len(selItems) == 0 {
common.ShowTip("请选择下载的文件")
return
} else {
for _, item := range selItems {
wg.Add(1)
go func(name string) {
defer func() {
downloadProgress.Hide()
wg.Done()
}()
logrus.WithField("filepath", saveDirEntry.Text+name).Debug("下载")
if err := a.sshService.ScpDownload(saveDirEntry.Text, remoteLogDirEntry.Text+name); err != nil {
common.ShowTip(name + " 下载失败")
}
}(item)
}
wg.Wait()
}
}
btns := container.NewHBox(allSelBtn, allCancelBtn, allDownloadBtn, DownloadDirBtn, EditorBtn, helpBtn)
//toolbar
toolbar := container.NewBorder(nil, btns, nil, saveConfBtn, confForm)
//layout
w.SetContent(container.NewBorder(toolbar, downloadProgress, nil, nil, a.downloadList.ItemList))
w.Resize(fyne.NewSize(800, 450))
w.CenterOnScreen()
return w
}

172
cmd/v2/ui/tool_valid.go Normal file
View File

@ -0,0 +1,172 @@
package ui
import (
"go_dreamfactory/cmd/v2/lib/common"
os_storage "go_dreamfactory/cmd/v2/lib/storage"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
mytheme "go_dreamfactory/cmd/v2/theme"
"io/ioutil"
"path"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
fyne_storage "fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
type jsonFileList struct {
selItemId string //选择的文件ID
cachedList common.List
itemList *widget.List
fileTotal int //文件数
//字段
columnList *widget.List
}
func newJsonFileList() *jsonFileList {
return &jsonFileList{
cachedList: common.NewList(""),
}
}
type appConfigValid struct {
appAdapter
jsonFileList *jsonFileList
jsonDirString string //json目录
}
func (a *appConfigValid) LazyInit(pt service.PttService, obs observer.Observer) error {
a.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_VALID, mytheme.ResourceExcelJpg, nil)
a.jsonFileList = newJsonFileList()
//load conf
storage, _ := os_storage.NewOSStorage()
conf, err := storage.LoadConfig()
if err != nil {
logrus.Error(err)
return err
}
if conf.JsonDir != "" {
a.jsonDirString = conf.JsonDir
}
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
// 打开目录
openFolder := func(entry *widget.Entry) {
dConf := dialog.NewFolderOpen(func(lu fyne.ListableURI, err error) {
if lu == nil {
return
}
entry.Text = lu.Path()
entry.Refresh()
}, toolWin.w)
luri, _ := fyne_storage.ListerForURI(fyne_storage.NewFileURI("."))
dConf.SetLocation(luri)
dConf.SetConfirmText("打开")
dConf.SetDismissText("取消")
dConf.Resize(fyne.NewSize(750, 500))
dConf.Show()
}
//配置json路径
jsonDir := widget.NewEntry()
jsonDir.PlaceHolder = "json目录"
jsonDir.OnChanged = func(s string) {
a.jsonFileList.initItem(jsonDir.Text)
a.jsonDirString = jsonDir.Text
}
form := widget.NewForm(
widget.NewFormItem("json目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(jsonDir)
a.jsonFileList.initItem(jsonDir.Text)
}), jsonDir)),
)
jsonDir.SetText(conf.JsonDir)
confBtn := &widget.Button{Text: "保存配置", Icon: theme.DocumentCreateIcon()}
confBtn.OnTapped = func() {
conf.JsonDir = jsonDir.Text
if err := storage.StoreConfig(conf); err != nil {
logrus.WithField("err", err).Debug("保存配置")
return
}
}
// 创建json文件 List
a.jsonFileList.itemList = a.jsonFileList.createList()
//创建字段
c := container.NewBorder(form,
container.NewHBox(confBtn),
nil, nil,
container.NewGridWithColumns(3,
container.NewVScroll(a.jsonFileList.itemList)))
content.Objects = append(content.Objects, c)
a.tabItem.Content = content
return nil
}
func (a *appConfigValid) GetAppName() string {
return common.TOOLBAR_VALID
}
func (a *appConfigValid) Icon() fyne.Resource {
return mytheme.ResourceExcelJpg
}
func (f *jsonFileList) initItem(dir string) {
files, err := ioutil.ReadDir(dir)
if err != nil {
logrus.Error(err)
return
}
for _, file := range files {
fileSuffix := path.Ext(file.Name())
name := strings.TrimSuffix(file.Name(), fileSuffix)
name = strings.TrimPrefix(name, "game_")
fm := common.Item{
Id: file.Name(),
Text: name,
Checked: false,
}
f.cachedList.Items = append(f.cachedList.Items, fm)
f.fileTotal++
// logrus.Debugf("%v", fm.Id)
}
}
func (f *jsonFileList) createList() *widget.List {
f.itemList = widget.NewList(
func() int {
return len(f.cachedList.Items)
},
func() fyne.CanvasObject {
return widget.NewLabel("Template")
},
func(id widget.ListItemID, item fyne.CanvasObject) {
c, _ := item.(*widget.Label)
c.Text = f.cachedList.Items[id].Text
c.Refresh()
},
)
f.itemList.OnSelected = func(id widget.ListItemID) {
selId := f.cachedList.Items[id].Id
f.selItemId = selId
return
}
return f.itemList
}

63
cmd/v2/ui/tool_welcome.go Normal file
View File

@ -0,0 +1,63 @@
package ui
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
them "go_dreamfactory/cmd/v2/theme"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type appWelcome struct {
appAdapter
}
func (a *appWelcome) LazyInit(service service.PttService, obs observer.Observer) error {
logo := canvas.NewImageFromResource(them.ResourceLogoPng)
logo.FillMode = canvas.ImageFillContain
logo.SetMinSize(fyne.NewSize(362*0.8, 192*0.8))
a.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_WEL, theme.InfoIcon(), nil)
wel := widget.NewRichTextFromMarkdown("# " + common.WELCOME_MSG)
for i := range wel.Segments {
if seg, ok := wel.Segments[i].(*widget.TextSegment); ok {
seg.Style.Alignment = fyne.TextAlignCenter
}
}
a.tabItem.Content = container.NewCenter(
container.NewVBox(
wel,
logo,
// shortCuts,
))
return nil
}
func (a *appWelcome) GetAppName() string {
return common.TOOLBAR_WEL
}
func (a *appWelcome) OpenDefault() string {
return common.TOOLBAR_WEL
}
func (a *appWelcome) ShortCut() fyne.Shortcut {
return &desktop.CustomShortcut{KeyName: fyne.Key1, Modifier: fyne.KeyModifierAlt}
}
func (a *appWelcome) Icon() fyne.Resource {
return theme.InfoIcon()
}
func (a *appWelcome) OnClose() bool {
return false
}

171
cmd/v2/ui/toolwindow.go Normal file
View File

@ -0,0 +1,171 @@
package ui
import (
"encoding/json"
"errors"
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/lib/storage"
"io/ioutil"
"net/http"
"time"
mytheme "go_dreamfactory/cmd/v2/theme"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
var toolWin *ToolWindowImpl
type ToolWindow interface {
WindowInterface
}
type ToolWindowImpl struct {
UIImpl
parent fyne.Window
w fyne.Window
tb *toolBar //工具条
sb *statusBar //状态栏
at *appContainer //tabs
mm *toolMenu //菜单
}
func NewToolWindow(ui *UIImpl, parent fyne.Window) ToolWindow {
mw := &ToolWindowImpl{
UIImpl: *ui,
parent: parent,
}
toolWin = mw
mw.sb = newStatusBar()
toolbar := widget.NewToolbar(
widget.NewToolbarAction(theme.ContentCopyIcon(), func() {
openApp2(mw.at, common.TOOLBAR_GEN)
}),
widget.NewToolbarAction(theme.ContentAddIcon(), func() {
openApp2(mw.at, common.TOOLBAR_PB)
}),
widget.NewToolbarAction(theme.DownloadIcon(), func() {
openApp2(mw.at, common.TOOLBAR_SEC)
}),
widget.NewToolbarAction(theme.MailSendIcon(), func() {
openApp2(mw.at, common.TOOLBAR_TERM)
}),
widget.NewToolbarAction(theme.ComputerIcon(), func() {
openApp2(mw.at, common.TOOLBAR_PING)
}),
widget.NewToolbarAction(mytheme.ResourceMongodbJpg, func() {
openApp2(mw.at, common.TOOLBAR_MGODB)
}),
widget.NewToolbarAction(mytheme.ResourceExcelJpg, func() {
openApp2(mw.at, common.TOOLBAR_VALID)
}),
widget.NewToolbarSpacer(),
widget.NewToolbarAction(theme.HelpIcon(), func() {
showAbout()
}),
)
mw.tb = newToolBar(toolbar)
mw.at = newAppContainer(toolRegister, nil, ui.obs)
return mw
}
func (ui *ToolWindowImpl) CreateWindow(title string, width, height float32, _ bool) {
w := ui.app.NewWindow(fmt.Sprintf("工具箱 %s %s", title, ui.app.Metadata().Version))
ui.AddWindow("tool", w)
ui.w = w
ui.mm = newToolMenu()
ui.w.SetMainMenu(ui.mm.MainMenu)
// content
content := container.NewBorder(ui.tb.toolbar, ui.sb.widget,
nil, nil, ui.at)
ui.w.SetContent(content)
appName, err := ui.at.openWelcome()
if err != nil {
logrus.WithField("appName", appName).Error(err)
}
w.Resize(fyne.NewSize(width, height))
// w.SetMaster()
w.Canvas().SetOnTypedKey(func(ke *fyne.KeyEvent) {
switch ke.Name {
case fyne.KeyEscape:
{
w.Close()
ui.parent.Show()
}
}
})
w.CenterOnScreen()
w.SetCloseIntercept(func() {
ui.parent.Show()
w.Close()
})
w.Show()
}
func (ui *ToolWindowImpl) quiteHandle() {
dialog.ShowConfirm("提示", "确定退出吗", func(b bool) {
if !b {
return
}
ui.app.Quit()
}, ui.w)
}
func (ui *ToolWindowImpl) syncConfig() {
cli := http.Client{Timeout: time.Second * 10}
r, err := cli.Get("http://10.0.0.9:8080/prd/config.json")
if err != nil {
logrus.Error(err)
dialog.ShowError(err, ui.w)
return
}
defer r.Body.Close()
b, err2 := ioutil.ReadAll(r.Body)
if err2 != nil {
logrus.Error(err)
dialog.ShowError(err2, ui.w)
return
}
if len(b) == 0 {
dialog.ShowError(errors.New("云配置可能不存在"), ui.w)
return
}
config := &storage.Config{}
if err := json.Unmarshal(b, config); err != nil {
dialog.ShowError(fmt.Errorf("云配置解析错误: %s", err), ui.w)
return
}
s, err := storage.NewOSStorage()
s.StoreConfig(config)
dialog.ShowConfirm("提示", "云配置同步成功,需要重新打开窗口", func(b bool) {
if !b {
return
}
ui.parent.Show()
ui.w.Close()
}, ui.w)
}

72
cmd/v2/ui/toy_datetime.go Normal file
View File

@ -0,0 +1,72 @@
package ui
import (
"go_dreamfactory/cmd/v2/service/observer"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
"github.com/BabySid/gobase"
)
type toyDateTime struct {
toyAdapter
date *canvas.Text
week *canvas.Text
time *canvas.Text
}
func (tdt *toyDateTime) Init(obs observer.Observer) error {
tdt.date = canvas.NewText("", nil)
tdt.date.TextSize = 15
tdt.week = canvas.NewText("", nil)
tdt.week.TextSize = 15
tdt.time = canvas.NewText("", nil)
tdt.time.TextSize = 42
tdt.widget = widget.NewCard("", "",
container.NewVBox(
container.NewHBox(layout.NewSpacer(), tdt.date, tdt.week, layout.NewSpacer()),
container.NewCenter(tdt.time),
),
)
tdt.widget.Resize(fyne.NewSize(ToyWidth, 110))
// for init
tdt.Run()
_ = gobase.GlobalScheduler.AddJob("toy_datetime", "*/1 * * * * *", tdt)
// todo assert(err == nil)
return nil
}
var (
weekStr = [...]string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
}
)
func (tdt *toyDateTime) Run() {
now := time.Now()
tdt.date.Text = gobase.FormatTimeStampWithFormat(now.Unix(), gobase.DateFormat)
tdt.date.Refresh()
tdt.week.Text = weekStr[now.Weekday()]
tdt.week.Refresh()
tdt.time.Text = gobase.FormatTimeStampWithFormat(now.Unix(), gobase.TimeFormat)
tdt.time.Refresh()
}

View File

@ -0,0 +1,35 @@
package ui
import (
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2/widget"
)
type toyInterface interface {
Init(obs observer.Observer) error
GetToyCard() *widget.Card
}
var (
toyRegister = []toyInterface{
&toyDateTime{},
&toyUserInfo{},
}
)
const (
ToyWidth = 250
)
type toyAdapter struct {
widget *widget.Card
}
func (t toyAdapter) Init(obs observer.Observer) error {
panic("implement Init")
}
func (t toyAdapter) GetToyCard() *widget.Card {
return t.widget
}

321
cmd/v2/ui/toy_userinfo.go Normal file
View File

@ -0,0 +1,321 @@
package ui
import (
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/comm"
// "go_dreamfactory/modules/user"
"go_dreamfactory/pb"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/atotto/clipboard"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type toyUserInfo struct {
toyAdapter
dataList *widget.List
titleLabel *widget.Label
userInfo *service.UserInfo
data binding.ExternalStringList
obs observer.Observer
copyBtn *widget.Button
refreshBtn *widget.Button
logoutBtn *widget.Button
}
func (this *toyUserInfo) Init(obs observer.Observer) error {
this.obs = obs
this.titleLabel = widget.NewLabel(common.USERINFO_PANEL_TITLE)
this.data = binding.BindStringList(&[]string{})
// 复制UID button
this.copyBtn = widget.NewButtonWithIcon("", theme.ContentCopyIcon(), func() {
if this.userInfo != nil && this.userInfo.DbUser != nil {
_ = clipboard.WriteAll(this.userInfo.DbUser.Uid)
common.ShowTip("已复制UID到剪贴板")
}
})
this.copyBtn.Disable()
// 刷新
this.refreshBtn = widget.NewButtonWithIcon("", theme.ViewRefreshIcon(), func() {
if err := service.GetPttService().SendToClient(
string("user"),
"info",
&pb.UserInfoReq{}); err != nil {
logrus.Error(err)
return
}
})
this.refreshBtn.Disable()
// 注销用户
this.logoutBtn = widget.NewButtonWithIcon("", theme.AccountIcon(), func() {
// if err := service.GetPttService().SendToClient(
// string("user"),
// user.UserSubTypeInfo,
// &pb.UserInfoReq{}); err != nil {
// logrus.Error(err)
// return
// }
})
// list
this.dataList = widget.NewListWithData(this.data,
func() fyne.CanvasObject {
return widget.NewLabel("template")
},
func(i binding.DataItem, o fyne.CanvasObject) {
o.(*widget.Label).Bind(i.(binding.String))
},
)
// md
// sign := widget.NewRichTextFromMarkdown(``)
// layout
this.widget = widget.NewCard("", "",
container.NewBorder(container.NewHBox(this.titleLabel, layout.NewSpacer(), this.logoutBtn, this.refreshBtn, this.copyBtn),
nil, nil, nil, container.NewVScroll(this.dataList)))
this.widget.Resize(fyne.NewSize(ToyWidth, 750))
this.dataListener()
return nil
}
func (this *toyUserInfo) dataListener() {
this.obs.AddListener(observer.EVENT_USERINFO, observer.Listener{
OnNotify: func(d interface{}, args ...interface{}) {
time.Sleep(time.Millisecond * 50)
_ = this.data.Set([]string{})
if this.copyBtn.Disabled() {
this.copyBtn.Enable()
}
if this.refreshBtn.Disabled() {
this.refreshBtn.Enable()
}
this.userInfo = d.(*service.UserInfo)
if this.userInfo == nil {
return
}
uid = this.userInfo.DbUser.Uid
// _ = this.data.Append(fmt.Sprintf("%-3s\t: %s", common.USERINFO_UID, this.userInfo.DbUser.Uid))
_ = this.data.Append(this.getAcc()) //0
_ = this.data.Append(this.getSid()) //1
_ = this.data.Append(this.getName()) //2
_ = this.data.Append(this.getAvatar()) //3
_ = this.data.Append(this.getVip()) //4
_ = this.data.Append(this.getLv()) //5
_ = this.data.Append(this.getGold()) //6
_ = this.data.Append(this.getExp()) //7
_ = this.data.Append(this.getDiamond()) //8
_ = this.data.Append(this.getTitle()) //9
_ = this.data.Append(this.getActiveDay()) //10
_ = this.data.Append(this.getActiveWeek()) //11
_ = this.data.Append(this.getFriendPoint()) //12
_ = this.data.Append(this.getModiNameCount()) //13
_ = this.data.Append(this.getCreateTime())
_ = this.data.Append(this.getSign()) //15
_ = this.data.Append(this.getBgp()) //16
_ = this.data.Append(this.getFigure()) //17
_ = this.data.Append(this.getVit()) //18
this.setProp(2, common.USERINFO_NAME, this.userInfo.DbUser.Name)
},
})
this.obs.AddListener(observer.EVENT_REQ_RSP, observer.Listener{
OnNotify: func(d interface{}, args ...interface{}) {
data := d.(*pb.UserMessage)
//change name
if data.MainType == string("user") &&
data.SubType == "modifyname" {
rsp := &pb.UserModifynameResp{}
if !comm.ProtoUnmarshal(data, rsp) {
logrus.Error("unmarshal err")
return
}
// updatename
this.setProp(2, common.USERINFO_NAME, rsp.Name)
}
//update info
if data.MainType == string("user") &&
data.SubType == "info" {
rsp := &pb.UserInfoResp{}
if !comm.ProtoUnmarshal(data, rsp) {
logrus.Error("unmarshal err")
return
}
if rsp.Data == nil {
logrus.Debug("没有用户数据返回")
}
this.setProp(1, common.USERINFO_SERVER, rsp.Data.Sid)
this.setProp(2, common.USERINFO_NAME, rsp.Data.Name)
this.setProp(3, common.USERINFO_AVATAR, rsp.Data.Avatar)
this.setProp(4, common.USERINFO_VIP, rsp.Data.Vip)
this.setProp(5, common.USERINFO_LV, rsp.Data.Lv)
this.setProp(6, common.USERINFO_GOLD, rsp.Data.Gold)
this.setProp(7, common.USERINFO_EXP, rsp.Data.Exp)
this.setProp(8, common.USERINFO_DIAMOND, rsp.Data.Diamond)
this.setProp(9, common.USERINFO_TITLE, rsp.Data.Title)
this.setProp(10, common.USERINFO_ACTIVE_DAY, rsp.Ex.Activeday)
this.setProp(11, common.USERINFO_ACTIVE_WEEK, rsp.Ex.Activeweek)
this.setProp(12, common.USERINFO_FRIENDPOINT, rsp.Ex.FriendPoint)
this.setProp(13, common.USERINFO_MODINAME, rsp.Ex.ModifynameCount)
this.setProp(13, common.USERINFO_MODINAME, rsp.Ex.ModifynameCount)
this.setProp(15, common.USERINFO_SIGN, rsp.Ex.Sign)
this.setProp(16, "背景", rsp.Data.Bgp)
this.setProp(17, "形象", rsp.Data.Figure)
this.setProp(18, "体力", rsp.Data.Ps)
}
},
})
this.obs.AddListener(observer.EVENT_USER_CHANGE, observer.Listener{
OnNotify: func(d interface{}, args ...interface{}) {
data := d.(*model.PushModel)
// listener gold
if data.Msg.MainType == string("user") &&
data.Msg.SubType == "reschange" {
rsp := &pb.UserResChangedPush{}
if !comm.ProtoUnmarshal(data.Msg, rsp) {
logrus.Error("unmarshal err")
return
}
// this.setProp(6, common.USERINFO_GOLD, rsp.Gold)
// this.setProp(7, common.USERINFO_EXP, rsp.Exp)
// this.setProp(8, common.USERINFO_DIAMOND, rsp.Diamond)
}
// listener exp
// if data.Msg.MainType == string("user") &&
// data.Msg.SubType == "" {
//TODO change exp
// }
},
})
}
func (this *toyUserInfo) setProp(idx int, label string, val interface{}) {
// v, _ := this.data.GetValue(idx)
// if v != "" {
logrus.WithFields(logrus.Fields{"idx": idx, "lbl": label, "val": val}).Debug("更新Prop")
if err := this.data.SetValue(idx, fmt.Sprintf("%-3s\t: %v", label, val)); err != nil {
logrus.WithFields(logrus.Fields{"idx": idx, "val": val}).Error(err)
}
// }
}
func (this *toyUserInfo) getAcc() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_ACC, this.userInfo.DbUser.Binduid)
}
func (this *toyUserInfo) getSid() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_SERVER, this.userInfo.DbUser.Sid)
}
func (this *toyUserInfo) getName() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_NAME, this.userInfo.DbUser.Name)
}
func (this *toyUserInfo) getAvatar() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_AVATAR, this.userInfo.DbUser.Avatar)
}
func (this *toyUserInfo) getVip() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_VIP, cast.ToString(this.userInfo.DbUser.Vip))
}
func (this *toyUserInfo) getLv() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_LV, cast.ToString(this.userInfo.DbUser.Lv))
}
func (this *toyUserInfo) getGold() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_GOLD, cast.ToString(this.userInfo.DbUser.Gold))
}
func (this *toyUserInfo) getExp() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_EXP, cast.ToString(this.userInfo.DbUser.Exp))
}
func (this *toyUserInfo) getDiamond() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_DIAMOND, cast.ToString(this.userInfo.DbUser.Diamond))
}
func (this *toyUserInfo) getTitle() string {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_TITLE, cast.ToString(this.userInfo.DbUser.Exp))
}
func (this *toyUserInfo) getFigure() string {
return fmt.Sprintf("%-3s\t: %s", "形象", cast.ToString(this.userInfo.DbUser.Figure))
}
func (this *toyUserInfo) getBgp() string {
return fmt.Sprintf("%-3s\t: %s", "背景", cast.ToString(this.userInfo.DbUser.Bgp))
}
func (this *toyUserInfo) getVit() string {
return fmt.Sprintf("%-3s\t: %s", "体力", cast.ToString(this.userInfo.DbUser.Ps))
}
func (this *toyUserInfo) getActiveDay() string {
if this.userInfo.DbUserExpand == nil {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_ACTIVE_DAY, cast.ToString(0))
}
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_ACTIVE_DAY, cast.ToString(this.userInfo.DbUserExpand.Activeday))
}
func (this *toyUserInfo) getActiveWeek() string {
if this.userInfo.DbUserExpand == nil {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_ACTIVE_WEEK, cast.ToString(0))
}
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_ACTIVE_WEEK, cast.ToString(this.userInfo.DbUserExpand.Activeweek))
}
func (this *toyUserInfo) getFriendPoint() string {
if this.userInfo.DbUserExpand == nil {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_FRIENDPOINT, cast.ToString(0))
}
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_FRIENDPOINT, cast.ToString(this.userInfo.DbUserExpand.FriendPoint))
}
func (this *toyUserInfo) getModiNameCount() string {
if this.userInfo.DbUserExpand == nil {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_MODINAME, "")
}
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_MODINAME, cast.ToString(this.userInfo.DbUserExpand.ModifynameCount))
}
func (this *toyUserInfo) getSign() string {
if this.userInfo.DbUserExpand == nil {
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_SIGN, "")
}
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_SIGN, cast.ToString(this.userInfo.DbUserExpand.Sign))
}
func (this *toyUserInfo) getCreateTime() string {
ctime := this.userInfo.DbUser.Ctime
if ctime <= 0 {
return ""
}
tt := time.Unix(ctime, 0)
return fmt.Sprintf("%-3s\t: %s", common.USERINFO_CREATETM, tt.Format(time.RFC3339))
}

31
cmd/v2/ui/toys.go Normal file
View File

@ -0,0 +1,31 @@
package ui
import (
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
)
type toys struct {
widget *fyne.Container
}
func newToys(obs observer.Observer) *toys {
var t toys
cards := make([]fyne.CanvasObject, len(toyRegister))
for i, toy := range toyRegister {
toy := toy
err := toy.Init(obs)
if err != nil {
panic(err)
}
cards[i] = toy.GetToyCard()
}
t.widget = container.New(NewVCZBoxLayout(),
cards...)
return &t
}

83
cmd/v2/ui/ui.go Normal file
View File

@ -0,0 +1,83 @@
package ui
import (
"go_dreamfactory/cmd/v2/lib/storage"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/cmd/v2/theme"
"os"
"sync"
"fyne.io/fyne/v2"
"github.com/BabySid/gobase"
"github.com/sirupsen/logrus"
)
var uid string
type UI interface {
AddWindow(name string, w fyne.Window)
Run()
Stop()
}
type UIImpl struct {
app fyne.App
windows map[string]fyne.Window
winMux *sync.Mutex
connService service.ConnService
pttService service.PttService
obs observer.Observer
storage storage.Storage
config *storage.Config
}
func NewUI(app fyne.App,
connService service.ConnService,
pttService service.PttService,
obs observer.Observer,
) (*UIImpl, error) {
app.Settings().SetTheme(&theme.MyTheme{})
iStorage, err := storage.NewOSStorage()
if err != nil {
logrus.Errorf("new storage err:%v", err)
return nil, err
}
// 加载配置
config, err := iStorage.LoadConfig()
if err != nil {
logrus.Errorf("Load config err:%v", err)
return nil, err
}
return &UIImpl{
app: app,
windows: make(map[string]fyne.Window),
winMux: &sync.Mutex{},
connService: connService,
pttService: pttService,
obs: obs,
storage: iStorage,
config: config,
}, nil
}
func (ui *UIImpl) AddWindow(name string, w fyne.Window) {
ui.winMux.Lock()
defer ui.winMux.Unlock()
ui.windows[name] = w
}
func (ui *UIImpl) Run() {
defer func() {
_ = os.Unsetenv("FYNE_SCALE")
gobase.Exit()
}()
ui.app.Run()
}
func (ui *UIImpl) Stop() {
ui.app.Quit()
}

View File

@ -0,0 +1,31 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
)
type BaseformView struct {
form *widget.Form
obs observer.Observer
w fyne.Window
res *widget.Entry
service service.PttService
}
func (view *BaseformView) Init(service service.PttService, obs observer.Observer, w fyne.Window, res *widget.Entry) {
view.service = service
view.obs = obs
view.w = w
view.res = res
view.form = widget.NewForm()
view.form.SubmitText = common.BUTTON_OK
}
func (view *BaseformView) Load() {
view.form.OnSubmit()
}

View File

@ -0,0 +1,99 @@
package formview
import (
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
// "go_dreamfactory/comm"
"go_dreamfactory/pb"
"go_dreamfactory/utils"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
type ListBaseView struct {
BaseformView
dataListWidget *widget.List
dataBinding binding.UntypedList
selItemIds []string //选择的ID
itemListData *model.ItemModelList
}
// init data
func (this *ListBaseView) initItemList() {
this.dataBinding = binding.NewUntypedList()
this.itemListData = model.NewItemModelList()
}
// create list view with check widget
func (this *ListBaseView) createItemListWithCheck() *widget.List {
this.dataListWidget = widget.NewListWithData(this.dataBinding,
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewCheck("", func(b bool) {}),
widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{}),
)
},
func(data binding.DataItem, item fyne.CanvasObject) {
o, _ := data.(binding.Untyped).Get()
pd := o.(*model.ItemModel)
item.(*fyne.Container).Objects[0].(*widget.Check).OnChanged = func(b bool) {
if b {
this.selItemIds = append(this.selItemIds, pd.Id)
} else {
utils.DeleteString(this.selItemIds, pd.Id)
}
}
item.(*fyne.Container).Objects[1].(*widget.Label).SetText(pd.Label)
},
)
return this.dataListWidget
}
// create list , single select
func (this *ListBaseView) createItemList() *widget.List {
this.dataListWidget = widget.NewListWithData(this.dataBinding,
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{}),
)
},
func(data binding.DataItem, item fyne.CanvasObject) {
o, _ := data.(binding.Untyped).Get()
pd := o.(*model.ItemModel)
item.(*fyne.Container).Objects[0].(*widget.Label).SetText(pd.Label)
},
)
// single select
this.dataListWidget.OnSelected = func(id widget.ListItemID) {
sel := this.itemListData.DataList[id]
this.selItemIds = []string{sel.Id}
}
return this.dataListWidget
}
func (this *ListBaseView) listBtnFun() func() {
return func() {
if err := service.GetPttService().SendToClient(
string("friend"),
// friend.FriendSubTypeApplyList,
"",
&pb.FriendApplyListReq{}); err != nil {
logrus.Error(err)
}
this.itemListData = model.NewItemModelList()
}
}
// set data
func (this *ListBaseView) reloadListData() {
if this.itemListData != nil {
d := this.itemListData.AsInterfaceArray()
this.dataBinding.Set(d)
}
}

View File

@ -0,0 +1,116 @@
package formview
import (
"errors"
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/pb"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
type BingoView struct {
BaseformView
}
func (this *BingoView) CreateView(t *model.TestCase) fyne.CanvasObject {
var (
gridContainer *fyne.Container
ttxt string //T text
aSel *widget.Select
)
tEntry := widget.NewEntry()
tEntry.PlaceHolder = "配置ID"
tEntry.OnChanged = func(s string) {
ttxt = s
}
nEntry := widget.NewEntry()
nEntry.PlaceHolder = "数量"
attrSel := &widget.Select{}
attrSel.Options = []string{"选择", "gold", "diamond", "exp", "moongold", "friendPoint", "ps", "guildcoin", "arenacoin", "merchantmoney"}
attrSel.SetSelected("选择")
attrSel.OnChanged = func(s string) {
ttxt = s
}
aSel = &widget.Select{}
gridContainer = container.NewGridWithColumns(3, aSel, tEntry, nEntry)
aSel.Options = []string{"选择", "attr", "item", "hero", "equi", "mapid", "pataid", "alltask", "worldtask", "sociatyexp", "sociatyactivity", "allgrowtask", "lv"}
aSel.SetSelected("选择")
aSel.OnChanged = func(s string) {
if s == "attr" {
gridContainer.Objects[1] = attrSel
ttxt = attrSel.Selected
} else {
gridContainer.Objects[1] = tEntry
ttxt = tEntry.Text
}
}
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_BINGO, gridContainer))
this.form.OnSubmit = func() {
var bingo string
if ttxt == "" && nEntry.Text == "" {
bingo = fmt.Sprintf("bingo:%s", aSel.Selected)
} else if ttxt != "" && nEntry.Text == "" {
bingo = fmt.Sprintf("bingo:%s,%s", aSel.Selected, ttxt)
} else if ttxt == "" && nEntry.Text != "" {
bingo = fmt.Sprintf("bingo:%s,%s", aSel.Selected, nEntry.Text)
} else if ttxt != "" && nEntry.Text != "" {
bingo = fmt.Sprintf("bingo:%s,%s,%s", aSel.Selected, ttxt, nEntry.Text)
}
logrus.Info(bingo)
if aSel.Selected == "选择" || nEntry.Text == "0" {
dialog.ShowError(errors.New("请填写完整且正确的数值"), this.w)
return
}
if err := service.GetPttService().SendToClient(
t.MainType,
t.SubType,
&pb.GMCmdReq{Cmod: bingo},
); err != nil {
logrus.Error(err)
return
}
aSel.SetSelected("")
attrSel.SetSelected("")
nEntry.SetText("")
tEntry.SetText("")
}
bingoEntry := widget.NewEntry()
bingoEntry.PlaceHolder = "bingo:item,10001,1"
customForm := widget.NewForm(widget.NewFormItem("自定义", bingoEntry))
customForm.OnSubmit = func() {
if bingoEntry.Text == "" || !strings.HasPrefix(bingoEntry.Text, "bingo:") {
dialog.ShowError(errors.New("请填写完整并加上bingo:前缀"), this.w)
return
}
if err := service.GetPttService().SendToClient(
t.MainType,
t.SubType,
&pb.GMCmdReq{Cmod: bingoEntry.Text},
); err != nil {
logrus.Error(err)
return
}
}
customForm.SubmitText = common.BUTTON_OK
c := container.NewVBox(this.form, customForm)
return c
}

View File

@ -0,0 +1,67 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
cfg "go_dreamfactory/sys/configure/structs"
)
// 世界任务检查
type CheckWorldtaskView struct {
BaseformView
taskList *common.ItemList //世界任务执行过程列表
}
// - 检查世界任务状态
// 1. 输入任务ID 查询 任务状态
// 2. 查询所有任务执行结果
// - 模拟世界任务流程
// 1.根据配置自动执行所有流程
// 2.输出每个任务执行过程(文件)
// 3.如果有异常,输出执行错误原因
func (view *CheckWorldtaskView) CreateView(t *model.TestCase) fyne.CanvasObject {
//taskId entry
// taskId:= widget.NewEntry()
//输出每个任务执行过程(文件)
path := widget.NewEntry()
jsonPathEntry := widget.NewEntry()
des := widget.NewEntry()
mockForm := widget.NewForm(
widget.NewFormItem("任务类型", des),
widget.NewFormItem("Json配置", jsonPathEntry),
widget.NewFormItem("结果目录", path),
)
view.taskList = common.NewItemList()
view.taskList.ItemList = view.taskList.CreateList()
helpBtn := widget.NewButtonWithIcon("", theme.HelpIcon(), func() {})
mockBtn := widget.NewButton("模拟执行", func() {
commCfg := &common.Config{
Path: jsonPathEntry.Text,
}
// 读取配置
if tb, err := cfg.NewTables(commCfg.Loader); err == nil {
tb.WorldTask.GetDataList()
}
// 读取协议
// 设置协议参数
// 输出结果
})
top := container.NewHBox()
bottom := container.NewHBox(helpBtn, mockBtn)
c := container.NewBorder(top, nil, nil, nil, view.taskList.ItemList)
layout := container.NewVSplit(
container.NewBorder(nil, bottom, nil, nil, container.NewGridWithColumns(2, mockForm)), c)
layout.Offset = 0.2
return layout
}

31
cmd/v2/ui/views/common.go Normal file
View File

@ -0,0 +1,31 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"fyne.io/fyne/v2/widget"
)
// 获取任务标签
func getTaskTagSelect() *widget.Select {
var tagSelect *widget.Select
tagSelect = widget.NewSelect([]string{
common.AAP_TESTCASE_FORM_TASK_DAY,
common.APP_TESTCASE_FORM_TASK_WEEK,
common.APP_TESTCASE_FORM_TASK_ACHIEVE}, func(s string) {
switch s {
case common.AAP_TESTCASE_FORM_TASK_DAY:
tagSelect.Selected = "1"
case common.APP_TESTCASE_FORM_TASK_WEEK:
tagSelect.Selected = "2"
case common.APP_TESTCASE_FORM_TASK_ACHIEVE:
tagSelect.Selected = "3"
default:
tagSelect.Selected = "0"
}
})
tagSelect.SetSelectedIndex(0)
return tagSelect
}

Some files were not shown because too many files have changed in this diff Show More