This commit is contained in:
parent
2dc37245e4
commit
c870813677
@ -1,75 +0,0 @@
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
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
88
cmd/cmd.go
@ -1,88 +0,0 @@
|
||||
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.")
|
||||
// }
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package robot
|
||||
|
||||
// 场景:在测试的接口中,输入参数为了能达到自动化,都是从业务中已有的协议中返回获取,作为下一个协议的输入参数。
|
||||
// 但是会存在,协议中的输入参数无法从上一个协议中获取,比如:添加好友协议,输入参数是“目标玩家ID”,为了能够动态
|
||||
// 测试添加好友协议,如果专门为这个协议添加返回目标玩家的接口,显然有点不合适。一个方案是,设计一个助手,专门处
|
||||
// 理这类问题,这个助手可以返回合适的数据,作为输入参数。
|
||||
|
||||
type assistant struct {
|
||||
}
|
||||
|
||||
func NewAssistant() *assistant {
|
||||
return &assistant{}
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
var dbOnce sync.Once
|
||||
var mgoCli *mongo.Client
|
||||
var mgoDb *mongo.Database
|
||||
|
||||
func InitDb() {
|
||||
dbOnce.Do(func() {
|
||||
var err error
|
||||
// Set client options
|
||||
clientOptions := options.Client().ApplyURI("mongodb://admin:123456@10.0.0.9:27018")
|
||||
|
||||
// Connect to MongoDB
|
||||
mgoCli, err = mongo.Connect(context.TODO(), clientOptions)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Check the connection
|
||||
err = mgoCli.Ping(context.TODO(), nil)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
mgoDb = mgoCli.Database("dreamfactory")
|
||||
})
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"go_dreamfactory/comm"
|
||||
"go_dreamfactory/modules/friend"
|
||||
"go_dreamfactory/pb"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
//friend
|
||||
friendBuilders = []*TestCase{
|
||||
{
|
||||
desc: "好友列表",
|
||||
mainType: string(comm.ModuleFriend),
|
||||
subType: friend.FriendSubTypeList,
|
||||
req: &pb.FriendListReq{},
|
||||
rsp: &pb.FriendListResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "黑名单列表",
|
||||
mainType: string(comm.ModuleFriend),
|
||||
subType: friend.FriendSubTypeBlacklist,
|
||||
req: &pb.FriendBlackListReq{},
|
||||
rsp: &pb.FriendBlackListResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
//search
|
||||
mainType: string(comm.ModuleFriend),
|
||||
subType: friend.FriendSubTypeSearch,
|
||||
req: &pb.FriendSearchReq{
|
||||
NickName: "乐谷745", //设置测试参数
|
||||
},
|
||||
rsp: &pb.FriendSearchResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "好友申请",
|
||||
mainType: string(comm.ModuleFriend),
|
||||
subType: friend.FriendSubTypeApply,
|
||||
req: &pb.FriendApplyReq{
|
||||
FriendId: "0_62ccd236e799cee5e7c97930",
|
||||
},
|
||||
rsp: &pb.FriendApplyResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "申请列表",
|
||||
mainType: string(comm.ModuleFriend),
|
||||
subType: friend.FriendSubTypeApplyList,
|
||||
req: &pb.FriendApplyListReq{},
|
||||
rsp: &pb.FriendApplyListResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "同意",
|
||||
mainType: string(comm.ModuleFriend),
|
||||
subType: friend.FriendSubTypeAgree,
|
||||
req: &pb.FriendAgreeReq{
|
||||
FriendIds: []string{"0_62c28ab569b7d416cf9eb8c7"},
|
||||
},
|
||||
rsp: &pb.FriendAgreeResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "拒绝",
|
||||
mainType: string(comm.ModuleFriend),
|
||||
subType: friend.FriendSubTypeRefuse,
|
||||
req: &pb.FriendRefuseReq{
|
||||
FriendIds: []string{"0_62c28bcb69b7d416cf9eb8c9"},
|
||||
},
|
||||
rsp: &pb.FriendRefuseResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "添加黑名单",
|
||||
mainType: string(comm.ModuleFriend),
|
||||
subType: friend.FriendSubTypeAddBlack,
|
||||
req: &pb.FriendAddBlackReq{
|
||||
FriendId: "0_62ccd236e799cee5e7c97930",
|
||||
},
|
||||
rsp: &pb.FriendAddBlackResp{},
|
||||
// enabled: true,
|
||||
next: func(robot *Robot, rsp proto.Message) {
|
||||
tcs := []*TestCase{}
|
||||
if r, ok := rsp.(*pb.FriendAddBlackResp); ok {
|
||||
tc := &TestCase{
|
||||
desc: "删除黑名单",
|
||||
mainType: string(comm.ModuleFriend),
|
||||
subType: friend.FriendSubTypeDelBlack,
|
||||
req: &pb.FriendDelBlackReq{
|
||||
FriendId: r.FriendId,
|
||||
},
|
||||
rsp: &pb.FriendDelBlackResp{},
|
||||
enabled: true,
|
||||
}
|
||||
tcs = append(tcs, tc)
|
||||
robot.addBuilders(tcs) //这里一定要调用此方法才会发送请求
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
//声明加入到构建器
|
||||
func (r *Robot) RunFriend() {
|
||||
r.addBuilders(friendBuilders)
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go_dreamfactory/comm"
|
||||
"go_dreamfactory/modules/hero"
|
||||
"go_dreamfactory/pb"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
//hero
|
||||
heroBuilders = []*TestCase{
|
||||
{
|
||||
desc: "英雄列表",
|
||||
mainType: string(comm.ModuleHero),
|
||||
subType: hero.HeroSubTypeList,
|
||||
req: &pb.HeroListReq{},
|
||||
rsp: &pb.HeroListResp{},
|
||||
print: func(rsp proto.Message) {
|
||||
out := rsp.(*pb.HeroListResp)
|
||||
for i, v := range out.List {
|
||||
fmt.Printf("%d- %v\n", (i + 1), v)
|
||||
}
|
||||
},
|
||||
// enabled: true,
|
||||
next: func(robot *Robot, rsp proto.Message) {
|
||||
tcs := []*TestCase{}
|
||||
if _, ok := rsp.(*pb.HeroListResp); ok {
|
||||
tc := &TestCase{
|
||||
desc: "英雄升级",
|
||||
mainType: string(comm.ModuleHero),
|
||||
subType: hero.StrengthenUplv,
|
||||
req: &pb.HeroStrengthenUplvReq{
|
||||
HeroObjID: "62e778117cc92ac48c4b2464",
|
||||
},
|
||||
rsp: &pb.HeroStrengthenUplvResp{},
|
||||
// enabled: true,
|
||||
}
|
||||
tcs = append(tcs, tc)
|
||||
}
|
||||
robot.addBuilders(tcs)
|
||||
},
|
||||
}, {
|
||||
desc: "英雄列表",
|
||||
mainType: string(comm.ModuleHero),
|
||||
subType: hero.HeroSubTypeList,
|
||||
req: &pb.HeroListReq{},
|
||||
rsp: &pb.HeroListResp{},
|
||||
// enabled: true,
|
||||
next: func(robot *Robot, rsp proto.Message) {
|
||||
if r, ok := rsp.(*pb.HeroListResp); ok {
|
||||
tcs := []*TestCase{}
|
||||
selHero := r.List[0] //选中的英雄
|
||||
for _, v := range r.List {
|
||||
heroId := v.Id
|
||||
tc := &TestCase{
|
||||
desc: "英雄升星",
|
||||
mainType: string(comm.ModuleHero),
|
||||
subType: hero.StrengthenUpStar,
|
||||
req: &pb.HeroStrengthenUpStarReq{
|
||||
HeroObjID: selHero.Id,
|
||||
},
|
||||
rsp: &pb.HeroStrengthenUpStarResp{},
|
||||
// enabled: true,
|
||||
}
|
||||
tcs = append(tcs, tc)
|
||||
|
||||
tc2 := &TestCase{
|
||||
desc: "英雄升级",
|
||||
mainType: string(comm.ModuleHero),
|
||||
subType: hero.StrengthenUplv,
|
||||
req: &pb.HeroStrengthenUplvReq{
|
||||
HeroObjID: heroId,
|
||||
},
|
||||
rsp: &pb.HeroStrengthenUplvResp{},
|
||||
enabled: true,
|
||||
}
|
||||
tcs = append(tcs, tc2)
|
||||
|
||||
tc3 := &TestCase{
|
||||
desc: "觉醒",
|
||||
mainType: string(comm.ModuleHero),
|
||||
subType: hero.Awaken,
|
||||
req: &pb.HeroAwakenReq{
|
||||
HeroObjID: heroId,
|
||||
},
|
||||
rsp: &pb.HeroAwakenResp{},
|
||||
// enabled: true,
|
||||
}
|
||||
tcs = append(tcs, tc3)
|
||||
}
|
||||
robot.addBuilders(tcs)
|
||||
}
|
||||
},
|
||||
}, {
|
||||
desc: "GM",
|
||||
mainType: "gm",
|
||||
subType: "cmd",
|
||||
req: &pb.GMCmdReq{
|
||||
Cmod: "bingo:attr,gold,100",
|
||||
},
|
||||
rsp: &pb.GMCmdResp{},
|
||||
// enabled: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// 声明加入到构建器并发起请求
|
||||
func (r *Robot) RunHero() {
|
||||
r.addBuilders(heroBuilders)
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type CaseNode struct {
|
||||
testCase *TestCase //用例名
|
||||
Next *CaseNode //测试用例
|
||||
}
|
||||
|
||||
type LinkCase struct {
|
||||
Head *CaseNode
|
||||
force bool
|
||||
}
|
||||
|
||||
func NewLinkCase() *LinkCase {
|
||||
return &LinkCase{}
|
||||
}
|
||||
|
||||
func NewDefault(testCast *TestCase) *LinkCase {
|
||||
return &LinkCase{
|
||||
force: true,
|
||||
Head: &CaseNode{testCase: testCast},
|
||||
}
|
||||
}
|
||||
|
||||
func (this *LinkCase) isEmpty() bool {
|
||||
return this.Head == nil
|
||||
}
|
||||
|
||||
func (this *LinkCase) lastNode() *CaseNode {
|
||||
cur := this.Head
|
||||
for cur.Next != nil {
|
||||
cur = cur.Next
|
||||
}
|
||||
return cur
|
||||
}
|
||||
|
||||
//尾部追加
|
||||
func (this *LinkCase) Append(testCase *TestCase) {
|
||||
newNode := &CaseNode{testCase: testCase}
|
||||
if this.isEmpty() {
|
||||
this.Head = newNode
|
||||
} else {
|
||||
this.lastNode().Next = newNode
|
||||
}
|
||||
}
|
||||
|
||||
//头部添加
|
||||
func (this *LinkCase) Unshift(testCase *TestCase) {
|
||||
if this.force {
|
||||
log.Fatal("use `NewLinkCase` method create link if you want to use `Unshift` opt")
|
||||
return
|
||||
}
|
||||
newNode := &CaseNode{testCase: testCase}
|
||||
newNode.Next = this.Head
|
||||
this.Head = newNode
|
||||
}
|
||||
|
||||
func (this *LinkCase) length() int {
|
||||
cur := this.Head
|
||||
count := 0
|
||||
for cur.Next != nil {
|
||||
cur = cur.Next
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (this *LinkCase) Insert(index int, testCase *TestCase) {
|
||||
if this.force {
|
||||
log.Fatal("use `NewLinkCase` method create link if you want to use `Insert` opt")
|
||||
return
|
||||
}
|
||||
if index < 0 {
|
||||
this.Unshift(testCase)
|
||||
} else if index > this.length() {
|
||||
this.Append(testCase)
|
||||
} else {
|
||||
preNode := this.Head
|
||||
count := 0
|
||||
for count < index-1 {
|
||||
preNode = preNode.Next
|
||||
count++
|
||||
}
|
||||
newNode := &CaseNode{testCase: testCase, Next: preNode.Next}
|
||||
preNode.Next = newNode
|
||||
}
|
||||
}
|
||||
|
||||
func (this *CaseNode) String() string {
|
||||
return fmt.Sprintf("value=%v", this.testCase.desc)
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go_dreamfactory/comm"
|
||||
"go_dreamfactory/modules/user"
|
||||
"go_dreamfactory/pb"
|
||||
"go_dreamfactory/utils"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/Pallinder/go-randomdata"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type LoginParam struct {
|
||||
Account string `json:"account"`
|
||||
ServerId string `json:"serverId"`
|
||||
TimeStamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
func (r *Robot) BuildSecStr() string {
|
||||
jsonByte, _ := jsoniter.Marshal(&LoginParam{
|
||||
Account: r.opts.Account,
|
||||
ServerId: r.opts.ServerId,
|
||||
TimeStamp: time.Now().Unix(),
|
||||
})
|
||||
jsonBase64 := utils.Base64Encode(jsonByte)
|
||||
// log.Printf("client base64:%s", jsonBase64)
|
||||
clientMd5key := util.Md5(jsonBase64)
|
||||
// log.Printf("client md5:%s", clientMd5key)
|
||||
return fmt.Sprintf("CE:%s%s", clientMd5key, jsonBase64)
|
||||
}
|
||||
|
||||
//处理登录请求
|
||||
func (r *Robot) AccountLogin() {
|
||||
log.Printf("区服:[%s] 账号:[%s] login...", r.opts.ServerId, r.opts.Account)
|
||||
builders := []*TestCase{
|
||||
{
|
||||
id: "login",
|
||||
desc: "登录",
|
||||
mainType: string(comm.ModuleUser),
|
||||
subType: user.UserSubTypeLogin,
|
||||
req: &pb.UserLoginReq{
|
||||
Account: r.opts.Account,
|
||||
Sid: r.opts.ServerId,
|
||||
},
|
||||
rsp: &pb.UserLoginResp{},
|
||||
enabled: true,
|
||||
next: func(r *Robot, rsp proto.Message) {
|
||||
tcs := []*TestCase{}
|
||||
if _, ok := rsp.(*pb.UserLoginResp); ok {
|
||||
nick := randomdata.SillyName()
|
||||
tc := &TestCase{
|
||||
desc: "创角",
|
||||
mainType: string(comm.ModuleUser),
|
||||
subType: user.UserSubTypeCreate,
|
||||
req: &pb.UserCreateReq{ //设置请求参数
|
||||
NickName: nick,
|
||||
},
|
||||
rsp: &pb.UserCreateResp{},
|
||||
enabled: r.enable,
|
||||
}
|
||||
tcs = append(tcs, tc)
|
||||
r.addBuilders(tcs)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
r.addTestCaseAndReq(builders)
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package robot
|
||||
|
||||
type Options struct {
|
||||
WsUrl string //客户端访问网关的ws接口地址
|
||||
RegUrl string //账号注册接口地址
|
||||
Account string //玩家账号
|
||||
Create bool
|
||||
Secretkey string //秘钥串
|
||||
ServerId string //区服ID
|
||||
Role bool //是否创角
|
||||
}
|
||||
|
||||
func DefaultOpts() *Options {
|
||||
return &Options{
|
||||
WsUrl: "ws://10.0.5.194:7891/gateway",
|
||||
RegUrl: "http://10.0.0.9:8000/register",
|
||||
Create: false,
|
||||
ServerId: "1",
|
||||
}
|
||||
}
|
||||
|
||||
type Option func(*Options)
|
||||
|
||||
func WithWsUrl(addr string) Option {
|
||||
return func(o *Options) {
|
||||
o.WsUrl = addr
|
||||
}
|
||||
}
|
||||
|
||||
func WithAccount(account string) Option {
|
||||
return func(o *Options) {
|
||||
o.Account = account
|
||||
}
|
||||
}
|
||||
|
||||
func WithCreate(create bool) Option {
|
||||
return func(o *Options) {
|
||||
o.Create = create
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"go_dreamfactory/comm"
|
||||
"go_dreamfactory/pb"
|
||||
)
|
||||
|
||||
//申明测试接口及请求和响应参数
|
||||
var pack_builders = []*TestCase{
|
||||
{
|
||||
//create
|
||||
mainType: string(comm.ModulePack),
|
||||
subType: "queryuserpackreq",
|
||||
// req: &pb.Pack_Getlist_Req{IType: 1},
|
||||
rsp: &pb.UserCreateResp{},
|
||||
// enabled: true,
|
||||
},
|
||||
}
|
||||
|
||||
//声明加入到构建器并发起请求
|
||||
func (r *Robot) RunPack() {
|
||||
r.addBuilders(pack_builders)
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
# Robot使用
|
||||
|
||||
## 命令行
|
||||
|
||||
```sh
|
||||
#使用已存在的账号测试接口
|
||||
go run cmd.go run --account yourAccount
|
||||
```
|
||||
|
||||
```sh
|
||||
#使用新账号测试接口
|
||||
go run cmd.go run --account newAccount --create true
|
||||
```
|
||||
|
||||
@[TOC]
|
||||
|
||||
### 添加测试接口
|
||||
|
||||
* 声明测试接口
|
||||
|
||||
```go
|
||||
var user_builders = []*builder{
|
||||
{
|
||||
//create
|
||||
mainType: string(comm.SM_UserModule),
|
||||
subType: user.User_SubType_Create,
|
||||
req: &pb.UserCreateReq{ //设置请求参数
|
||||
NickName: "测试",
|
||||
},
|
||||
rsp: &pb.UserCreateRsp{},
|
||||
enabled: true,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 声明加入到构建器并发起请求
|
||||
|
||||
```go
|
||||
func (r *Robot) RunUser() {
|
||||
r.addBuilders(user_builders)
|
||||
r.handleReq()
|
||||
}
|
||||
```
|
||||
|
||||
### 添加用户登录之后需要测试的接口
|
||||
|
||||
```go
|
||||
func (r *Robot) onUserLoaded() {
|
||||
//添加用户登录之后需要测试的接口
|
||||
r.RunUser()
|
||||
}
|
||||
```
|
@ -1,485 +0,0 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go_dreamfactory/comm"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
// zlog "go_dreamfactory/lego/sys/log"
|
||||
"go_dreamfactory/modules/user"
|
||||
"go_dreamfactory/pb"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Pallinder/go-randomdata"
|
||||
"github.com/gorilla/websocket"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type Robot struct {
|
||||
ws *websocket.Conn
|
||||
opts *Options
|
||||
user *pb.DBUser
|
||||
builderMap map[string]*TestCase
|
||||
// linkCase *LinkCase
|
||||
enable bool //全局开关
|
||||
reqCh chan string
|
||||
printFormat bool //是否格式化结果
|
||||
caseTotal int32 //测试数量
|
||||
caseSuccess int32 //成功数量
|
||||
caseError int32 //失败数量
|
||||
start time.Time //启动时间
|
||||
}
|
||||
|
||||
var zlog *logrus.Logger
|
||||
|
||||
func initlog() {
|
||||
zlog = logrus.New()
|
||||
zlog.SetLevel(logrus.DebugLevel)
|
||||
zlog.SetFormatter(&RobotFormatter{
|
||||
DisableTimestamp: true,
|
||||
DisableQuote: true,
|
||||
})
|
||||
|
||||
//设置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 {
|
||||
zlog.SetOutput(fileAndStdoutWriter)
|
||||
} else {
|
||||
zlog.Info("failed to log to file.")
|
||||
}
|
||||
}
|
||||
|
||||
func NewRobot(opts *Options) *Robot {
|
||||
initlog()
|
||||
ws, _, err := websocket.DefaultDialer.Dial(opts.WsUrl, nil)
|
||||
if err != nil {
|
||||
zlog.Fatalf("websocket conn err:%v", err)
|
||||
}
|
||||
r := &Robot{
|
||||
ws: ws,
|
||||
opts: opts,
|
||||
builderMap: make(map[string]*TestCase),
|
||||
reqCh: make(chan string, 1),
|
||||
printFormat: false,
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Robot) Run() {
|
||||
zlog.Debug("Robot running...")
|
||||
zlog.Infof("websocket %s ", r.opts.WsUrl)
|
||||
if r.opts.Create { //创建新用户
|
||||
r.enable = true
|
||||
r.AccountRegister(r.opts.Account, r.opts.ServerId)
|
||||
} else {
|
||||
if r.opts.Account == "" {
|
||||
zlog.Fatal("WARNNING: account is required !!!")
|
||||
}
|
||||
r.enable = r.opts.Role
|
||||
r.AccountLogin()
|
||||
}
|
||||
|
||||
// ticker := time.NewTicker(time.Second * 5)
|
||||
// go func() {
|
||||
// for {
|
||||
// r.printBuilders()
|
||||
// <-ticker.C
|
||||
// if len(r.builderMap) == 0 {
|
||||
// r.printReport(&TestReport{
|
||||
// caseTotal: r.caseTotal,
|
||||
// caseSuccess: r.caseSuccess,
|
||||
// caseError: r.caseError,
|
||||
// })
|
||||
// os.Exit(0)
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
|
||||
// select {}
|
||||
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
id string //用例ID 如果没有指定,会自动赋值uuid
|
||||
desc string //用例描述
|
||||
mainType string //协议类型 L1
|
||||
subType string //协议类型 L2
|
||||
req proto.Message //请求类型
|
||||
rsp proto.Message //响应类型
|
||||
enabled bool //是否启用
|
||||
start time.Time //启用时间
|
||||
hs time.Duration //耗时
|
||||
print func(rsp proto.Message) //定义打印
|
||||
next func(robot *Robot, rsp proto.Message) //处理下一层用例请求
|
||||
}
|
||||
|
||||
//添加测试用用例
|
||||
func (r *Robot) addBuilders(builders []*TestCase) {
|
||||
for _, b := range builders {
|
||||
if b.enabled {
|
||||
if b.id == "" {
|
||||
uuid := uuid.NewV4().String()
|
||||
b.id = uuid
|
||||
r.builderMap[uuid] = b
|
||||
} else {
|
||||
r.builderMap[b.id] = b
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//处理用例,发送请求
|
||||
func (r *Robot) handleReq() {
|
||||
for len(r.builderMap) > 0 {
|
||||
for _, b := range r.builderMap {
|
||||
if b.enabled && b.req != nil {
|
||||
// time.Sleep(time.Millisecond * 500)
|
||||
b.start = time.Now()
|
||||
r.start = time.Now()
|
||||
head := &pb.UserMessage{MainType: b.mainType, SubType: b.subType}
|
||||
// defer traceFunc(head.MainType, head.SubType, r.user.GetUid(), b.req)
|
||||
err := r.SendToClient(head, b.req)
|
||||
if err != nil {
|
||||
delete(r.builderMap, b.id)
|
||||
zlog.Errorf("send to client err:%v", err)
|
||||
}
|
||||
|
||||
r.handleRsp(b.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//加入用例并执行请求
|
||||
func (r *Robot) addTestCaseAndReq(tcs []*TestCase) {
|
||||
r.addBuilders(tcs)
|
||||
r.handleReq()
|
||||
}
|
||||
|
||||
//错误通知处理
|
||||
func (r *Robot) handleNotify(uuid string, msg *pb.UserMessage) {
|
||||
if msg.MainType == "notify" && msg.SubType == "errornotify" {
|
||||
r.caseError++
|
||||
logrus.Debug("errornotify %v", r.caseError)
|
||||
rsp := &pb.NotifyErrorNotifyPush{}
|
||||
if !comm.ProtoUnmarshal(msg, rsp) {
|
||||
return
|
||||
}
|
||||
|
||||
tc := &TestCase{
|
||||
id: uuid,
|
||||
desc: "错误通知",
|
||||
mainType: comm.MainTypeNotify,
|
||||
subType: comm.SubTypeErrorNotify,
|
||||
req: rsp.Arg,
|
||||
rsp: rsp,
|
||||
}
|
||||
|
||||
r.printReply(msg, tc)
|
||||
delete(r.builderMap, uuid)
|
||||
}
|
||||
}
|
||||
|
||||
//处理响应
|
||||
func (r *Robot) handleRsp(id string) {
|
||||
defer func() {
|
||||
r.caseTotal++
|
||||
}()
|
||||
var msg *pb.UserMessage = &pb.UserMessage{}
|
||||
_, data, err := r.ws.ReadMessage()
|
||||
if err != nil {
|
||||
zlog.Fatalf("readMessage err:%v", err)
|
||||
}
|
||||
|
||||
if err = proto.Unmarshal(data, msg); err != nil {
|
||||
zlog.Fatalf("unmarshal err:%v", err)
|
||||
}
|
||||
|
||||
uuid, _ := r.findTestCase(msg)
|
||||
if uuid == "" {
|
||||
uuid = id
|
||||
}
|
||||
|
||||
r.handleNotify(uuid, msg)
|
||||
if v, ok := r.builderMap[uuid]; ok {
|
||||
if v.enabled &&
|
||||
(msg.MainType == v.mainType &&
|
||||
msg.SubType == v.subType) {
|
||||
v.hs = time.Since(v.start)
|
||||
if !comm.ProtoUnmarshal(msg, v.rsp) {
|
||||
return
|
||||
}
|
||||
|
||||
//处理下一层用例
|
||||
if v.next != nil {
|
||||
v.next(r, v.rsp)
|
||||
}
|
||||
|
||||
//执行自定义打印
|
||||
if v.print == nil {
|
||||
r.printReply(msg, v)
|
||||
} else {
|
||||
zlog.Debug("==============================")
|
||||
zlog.Debugf("%s [%s.%s]", v.desc, msg.MainType, msg.SubType)
|
||||
v.print(v.rsp)
|
||||
zlog.Debug("==============================")
|
||||
}
|
||||
|
||||
if msg.MainType == "user" && msg.SubType == "login" {
|
||||
r.loginCallback(v.rsp)
|
||||
} else {
|
||||
//清除已执行的用例
|
||||
delete(r.builderMap, v.id)
|
||||
}
|
||||
r.caseSuccess++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Robot) findTestCase(msg *pb.UserMessage) (string, bool) {
|
||||
for k, v := range r.builderMap {
|
||||
if v.mainType == msg.MainType && v.subType == msg.SubType {
|
||||
return k, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
//登录回调
|
||||
func (r *Robot) loginCallback(rsp proto.Message) {
|
||||
//清除登录用例
|
||||
delete(r.builderMap, "login")
|
||||
lr := rsp.(*pb.UserLoginResp)
|
||||
if lr.Data != nil {
|
||||
r.user = lr.Data
|
||||
r.onUserLoaded()
|
||||
r.handleReq()
|
||||
} else {
|
||||
//请求Http接口,模拟创建新账号
|
||||
r.AccountRegister(r.opts.Account, r.opts.ServerId)
|
||||
}
|
||||
}
|
||||
|
||||
//发送消息
|
||||
func (r *Robot) SendToClient(msg *pb.UserMessage, rsp proto.Message) error {
|
||||
//模拟客户端 每次请求都会生成新的秘钥
|
||||
msg.Sec = r.BuildSecStr()
|
||||
if comm.ProtoMarshal(rsp, msg) {
|
||||
data, _ := proto.Marshal(msg)
|
||||
return r.ws.WriteMessage(websocket.BinaryMessage, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//注册账号
|
||||
func (r *Robot) AccountRegister(account string, sid string) {
|
||||
if account == "" {
|
||||
zlog.Fatal("account value is empty")
|
||||
}
|
||||
//http
|
||||
regReq := &pb.UserRegisterReq{Account: account, Sid: sid}
|
||||
jsonByte, _ := json.Marshal(regReq)
|
||||
req, err := http.NewRequest("POST", r.opts.RegUrl, bytes.NewReader(jsonByte))
|
||||
if err != nil {
|
||||
zlog.Fatalf("account register err %v", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
|
||||
httpClient := &http.Client{}
|
||||
rsp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rsp.Body.Close()
|
||||
|
||||
body, _ := ioutil.ReadAll(rsp.Body)
|
||||
regRsp := &pb.UserRegisterResp{}
|
||||
err = jsoniter.Unmarshal(body, regRsp)
|
||||
|
||||
if regRsp.Code == pb.ErrorCode_Success { //注册成功
|
||||
fmt.Printf("account:%s 注册成功", regRsp.Account)
|
||||
//登录
|
||||
var user_builders = []*TestCase{
|
||||
{
|
||||
id: "login",
|
||||
desc: "登录",
|
||||
mainType: "user",
|
||||
subType: "login",
|
||||
req: &pb.UserLoginReq{
|
||||
Account: account,
|
||||
Sid: sid,
|
||||
},
|
||||
rsp: &pb.UserLoginResp{},
|
||||
enabled: true,
|
||||
next: func(r *Robot, rsp proto.Message) {
|
||||
tcs := []*TestCase{}
|
||||
if _, ok := rsp.(*pb.UserLoginResp); ok {
|
||||
nick := randomdata.SillyName()
|
||||
tc := &TestCase{
|
||||
desc: "创角",
|
||||
mainType: string(comm.ModuleUser),
|
||||
subType: user.UserSubTypeCreate,
|
||||
req: &pb.UserCreateReq{ //设置请求参数
|
||||
NickName: nick,
|
||||
},
|
||||
rsp: &pb.UserCreateResp{},
|
||||
enabled: r.enable,
|
||||
}
|
||||
tcs = append(tcs, tc)
|
||||
r.addBuilders(tcs)
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
r.addTestCaseAndReq(user_builders)
|
||||
}
|
||||
}
|
||||
|
||||
type TestReport struct {
|
||||
caseTotal int32 //总用例数
|
||||
caseSuccess int32 //成功数量
|
||||
caseError int32 //失败数量
|
||||
hsTotal time.Duration //总耗时
|
||||
}
|
||||
|
||||
//打印响应
|
||||
func (r *Robot) printReply(msg *pb.UserMessage, tc *TestCase) {
|
||||
var (
|
||||
code int32
|
||||
mainType string
|
||||
subType string
|
||||
// arg proto.Message
|
||||
// rsp proto.Message
|
||||
// err error
|
||||
)
|
||||
|
||||
if m, ok := tc.rsp.(*pb.NotifyErrorNotifyPush); ok {
|
||||
code = int32(m.Code)
|
||||
mainType = m.ReqMainType
|
||||
subType = m.ReqSubType
|
||||
// arg, _ = jsoniter.MarshalToString(m.Arg)
|
||||
} else {
|
||||
mainType = msg.MainType
|
||||
subType = msg.SubType
|
||||
// arg, _ = jsoniter.MarshalToString(tc.req)
|
||||
}
|
||||
// if r.printFormat {
|
||||
// s, err := jsoniter.MarshalToString(tc.rsp)
|
||||
// if err != nil {
|
||||
// zlog.Errorf("MarshalToString err:%v", err)
|
||||
// return
|
||||
// }
|
||||
// if rsp, err = formatJson(s); err != nil {
|
||||
// zlog.Errorf("formatJson err:%v", err)
|
||||
// return
|
||||
// }
|
||||
|
||||
// } else {
|
||||
// if rsp, err = jsoniter.MarshalToString(tc.rsp); err != nil {
|
||||
// zlog.Errorf("MarshalToString err:%v", err)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
//表格显示
|
||||
// data = append(data, []string{builder.desc, fmt.Sprintf("%s.%s", mainType, subType), "0", arg, cast.ToString(code)})
|
||||
// table.SetHeader([]string{"描述", "协议", "耗时", "参数", "响应码"})
|
||||
// for _, v := range data {
|
||||
// table.Append(v)
|
||||
// }
|
||||
// table.Render()
|
||||
|
||||
//
|
||||
zlog.Debug("-------------------------------------")
|
||||
// zlog.Debugf("uuid:%s", tc.id)
|
||||
zlog.Debugf("描述:%s", tc.desc)
|
||||
zlog.Debugf("协议:%s.%s", mainType, subType)
|
||||
zlog.Debugf("耗时:%v", tc.hs)
|
||||
zlog.Debugf("参数:%v", tc.req)
|
||||
zlog.Debugf("响应码:%d", code)
|
||||
zlog.Debugf("返回:%v", tc.rsp)
|
||||
}
|
||||
|
||||
//打印测试报告
|
||||
func (r *Robot) printReport(report *TestReport) {
|
||||
zlog.Debug("====================================")
|
||||
zlog.Infof("测试完毕,开始输出报告")
|
||||
zlog.Infof("用例总数:%d", r.caseTotal)
|
||||
zlog.Infof("成功:%d", r.caseSuccess)
|
||||
zlog.Infof("失败:%d", r.caseError)
|
||||
zlog.Infof("总耗时: %v", time.Since(r.start))
|
||||
}
|
||||
|
||||
//格式化json
|
||||
func formatJson(data string) (string, error) {
|
||||
var out bytes.Buffer
|
||||
err := json.Indent(&out, []byte(data), "", " ")
|
||||
return out.String(), err
|
||||
}
|
||||
|
||||
//方法参数跟踪
|
||||
func traceFunc(module string, funcName string, uid string, funcArgs interface{}) {
|
||||
zlog.Debugf("req [%s.%s] [%s] [%v]", module, funcName, uid, funcArgs)
|
||||
}
|
||||
|
||||
//在这里添加玩家成功登录以后的测试方法
|
||||
//次方法在用户登录成功后调用
|
||||
func (r *Robot) onUserLoaded() {
|
||||
//user
|
||||
r.RunUser()
|
||||
|
||||
//hero
|
||||
r.RunHero()
|
||||
//friend
|
||||
r.RunFriend()
|
||||
|
||||
//pack
|
||||
// r.RunPack()
|
||||
|
||||
//task
|
||||
r.RunTask()
|
||||
|
||||
// story
|
||||
r.RunMainline()
|
||||
}
|
||||
|
||||
type RobotFormatter struct {
|
||||
DisableTimestamp bool
|
||||
DisableQuote bool
|
||||
}
|
||||
|
||||
func (r *RobotFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
var b *bytes.Buffer
|
||||
if entry.Buffer != nil {
|
||||
b = entry.Buffer
|
||||
} else {
|
||||
b = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
var timestamp string
|
||||
var newLog string
|
||||
if !r.DisableTimestamp {
|
||||
timestamp = entry.Time.Format("2006-01-02 15:04:05")
|
||||
newLog = fmt.Sprintf("[%s] %s\n", timestamp, entry.Message)
|
||||
} else {
|
||||
newLog = fmt.Sprintf("%s\n", entry.Message)
|
||||
}
|
||||
|
||||
b.WriteString(newLog)
|
||||
return b.Bytes(), nil
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package robot
|
||||
|
||||
var (
|
||||
storyBuilders = []*TestCase{
|
||||
{
|
||||
|
||||
//enabled: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
//声明加入到构建器并发起请求
|
||||
func (r *Robot) RunMainline() {
|
||||
r.addBuilders(storyBuilders)
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go_dreamfactory/comm"
|
||||
"go_dreamfactory/modules/task"
|
||||
"go_dreamfactory/pb"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
taskBuilders = []*TestCase{
|
||||
{
|
||||
desc: "任务列表",
|
||||
mainType: string(comm.ModuleTask),
|
||||
subType: task.TaskSubTypeList,
|
||||
req: &pb.TaskListReq{
|
||||
//设置任务类型
|
||||
TaskTag: int32(comm.TASK_DAILY), //每天任务
|
||||
// TaskTag: int32(comm.TASK_WEEKLY), //周任务
|
||||
// TaskTag: int32(comm.TASK_ACHIEVE),
|
||||
// TaskTag: int32(comm.TASK_STRATEGY),
|
||||
},
|
||||
rsp: &pb.TaskListResp{},
|
||||
print: func(rsp proto.Message) {
|
||||
out := rsp.(*pb.TaskListResp)
|
||||
for _, v := range out.List {
|
||||
fmt.Printf("%v \n", v)
|
||||
}
|
||||
},
|
||||
// enabled: true,
|
||||
next: func(robot *Robot, rsp proto.Message) {
|
||||
tcs := []*TestCase{}
|
||||
if _, ok := rsp.(*pb.TaskListResp); ok {
|
||||
// for _, tt := range v.List {
|
||||
tc := &TestCase{
|
||||
desc: "领取任务奖励",
|
||||
mainType: string(comm.ModuleTask),
|
||||
subType: task.TaskSubTypeReceive,
|
||||
req: &pb.TaskReceiveReq{
|
||||
TaskTag: int32(comm.TASK_ACHIEVE),
|
||||
Id: 1,
|
||||
},
|
||||
rsp: &pb.TaskReceiveResp{},
|
||||
enabled: true,
|
||||
}
|
||||
tcs = append(tcs, tc)
|
||||
// }
|
||||
robot.addBuilders(tcs)
|
||||
}
|
||||
},
|
||||
}, {
|
||||
desc: "活跃度列表",
|
||||
mainType: string(comm.ModuleTask),
|
||||
subType: task.TaskSubTypeActiveList,
|
||||
req: &pb.TaskActiveListReq{
|
||||
TaskTag: int32(comm.TASK_DAILY),
|
||||
},
|
||||
rsp: &pb.TaskActiveListResp{},
|
||||
print: func(rsp proto.Message) {
|
||||
out := rsp.(*pb.TaskActiveListResp)
|
||||
for _, v := range out.List {
|
||||
fmt.Printf("%v \n", v)
|
||||
}
|
||||
fmt.Printf("玩家活跃度 :%v \n", out.Active)
|
||||
},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "活跃度领取",
|
||||
mainType: string(comm.ModuleTask),
|
||||
subType: task.TaskSubTypeActiveReceive,
|
||||
req: &pb.TaskActiveReceiveReq{
|
||||
Id: 1,
|
||||
TaskTag: int32(comm.TASK_DAILY),
|
||||
},
|
||||
rsp: &pb.TaskActiveReceiveResp{},
|
||||
// enabled: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
//声明加入到构建器并发起请求
|
||||
func (r *Robot) RunTask() {
|
||||
r.addBuilders(taskBuilders)
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
package robot
|
||||
|
||||
import (
|
||||
"go_dreamfactory/comm"
|
||||
"go_dreamfactory/modules/user"
|
||||
"go_dreamfactory/pb"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
//申明测试接口及请求和响应参数
|
||||
var user_builders = []*TestCase{
|
||||
{
|
||||
desc: "修改名称",
|
||||
mainType: string(comm.ModuleUser),
|
||||
subType: user.UserSubTypeModifyName,
|
||||
req: &pb.UserModifynameReq{ //设置请求参数
|
||||
Name: "uuuuuu",
|
||||
},
|
||||
rsp: &pb.UserModifynameResp{},
|
||||
// enabled: true,
|
||||
},
|
||||
{
|
||||
desc: "查看图鉴",
|
||||
mainType: string(comm.ModuleUser),
|
||||
subType: "gettujian",
|
||||
req: &pb.UserGetTujianReq{},
|
||||
rsp: &pb.UserGetTujianResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "获取配置",
|
||||
mainType: string(comm.ModuleUser),
|
||||
subType: user.UserSubTypeGetSetting,
|
||||
req: &pb.UserGetSettingReq{},
|
||||
rsp: &pb.UserGetSettingResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "更新配置",
|
||||
mainType: string(comm.ModuleUser),
|
||||
subType: user.UserSubTypeUpdatesetting,
|
||||
req: &pb.UserUpdateSettingReq{
|
||||
Setting: &pb.DBUserSetting{
|
||||
Huazhi: 2,
|
||||
Kangjuchi: 1,
|
||||
Gaoguang: true,
|
||||
},
|
||||
},
|
||||
rsp: &pb.UserUpdateSettingResp{},
|
||||
// enabled: true,
|
||||
}, {
|
||||
desc: "验证码",
|
||||
mainType: string(comm.ModuleUser),
|
||||
subType: user.UserSubTypeVeriCode,
|
||||
req: &pb.UserVeriCodeReq{},
|
||||
rsp: &pb.UserVeriCodeResp{},
|
||||
// enabled: true,
|
||||
next: func(robot *Robot, rsp proto.Message) {
|
||||
if r, ok := rsp.(*pb.UserVeriCodeResp); ok {
|
||||
tcs := []*TestCase{}
|
||||
tcs = append(tcs, &TestCase{
|
||||
desc: "初始化用户",
|
||||
mainType: string(comm.ModuleUser),
|
||||
subType: user.UserSubTypeInitData,
|
||||
req: &pb.UserInitdataReq{
|
||||
Code: r.Code,
|
||||
},
|
||||
rsp: &pb.UserInitdataResp{},
|
||||
enabled: true,
|
||||
})
|
||||
robot.addBuilders(tcs)
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
//声明加入到构建器并发起请求
|
||||
func (r *Robot) RunUser() {
|
||||
r.addBuilders(user_builders)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
SET CGO_ENABLED=0
|
||||
SET GOOS=linux
|
||||
SET GOARCH=amd64
|
||||
|
||||
go build -o update main.go
|
@ -1,153 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
解压文件
|
||||
- resources
|
||||
- RobotGUI.exe
|
||||
|
||||
建议覆盖所有文件
|
@ -1,127 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<!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> <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>
|
@ -1,69 +0,0 @@
|
||||
<!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> <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
BIN
cmd/v2/.DS_Store
vendored
Binary file not shown.
@ -1,8 +0,0 @@
|
||||
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
BIN
cmd/v2/app.png
Binary file not shown.
Before Width: | Height: | Size: 5.5 KiB |
@ -1,7 +0,0 @@
|
||||
@echo off
|
||||
|
||||
set input=%1%
|
||||
|
||||
echo build version:%input%
|
||||
|
||||
fyne package --name RobotGUI-%input% -os windows
|
@ -1,42 +0,0 @@
|
||||
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
BIN
cmd/v2/icon.png
Binary file not shown.
Before Width: | Height: | Size: 51 KiB |
@ -1,304 +0,0 @@
|
||||
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)
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
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
|
||||
)
|
@ -1,27 +0,0 @@
|
||||
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"
|
||||
)
|
@ -1,241 +0,0 @@
|
||||
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--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
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 = "更新"
|
||||
)
|
@ -1,75 +0,0 @@
|
||||
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{},
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
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"`
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
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")
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
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)
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
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()
|
||||
}()
|
||||
}
|
||||
}
|
@ -1,261 +0,0 @@
|
||||
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:]
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
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))
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package lib
|
||||
|
||||
type Aiassistant interface {
|
||||
Start() bool
|
||||
Stop() error
|
||||
ShowResult()
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
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))
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
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 //协议名称
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,885 +0,0 @@
|
||||
package pb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go_dreamfactory/cmd/v2/lib/common"
|
||||
"go_dreamfactory/cmd/v2/model"
|
||||
|
||||
"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(comm.ModuleGM),
|
||||
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(comm.ModuleWorldtask),
|
||||
string(comm.ModuleReddot),
|
||||
string(comm.ModuleSmithy),
|
||||
string(comm.ModuleDispatch),
|
||||
string(comm.ModuleReputation),
|
||||
string(comm.ModuleOldtimes),
|
||||
},
|
||||
"gm": {
|
||||
common.MF(comm.ModuleGM, "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.FriendSubTypeApply),
|
||||
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.FriendSubTypeSearch),
|
||||
// common.MF(comm.ModuleFriend, friend.FriendSubTypeZanList),
|
||||
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"),
|
||||
},
|
||||
// "rtask": {
|
||||
// common.MF(comm.ModuleRtask, "rtest"),
|
||||
// },
|
||||
"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(comm.ModuleWorldtask, "test"),
|
||||
},
|
||||
"smithy": {
|
||||
common.MF(comm.ModuleSmithy, "customer"),
|
||||
},
|
||||
"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(comm.ModuleGM),
|
||||
Enabled: true,
|
||||
},
|
||||
common.MF(comm.ModuleGM, "cmd"): {
|
||||
Desc: "eg. bingo:item,10001,1 bingo:attr,gold,1000000",
|
||||
MainType: string(comm.ModuleGM),
|
||||
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,
|
||||
Print: func(rsp proto.Message) string {
|
||||
var formatStr strings.Builder
|
||||
if in, ok := rsp.(*pb.UserMessage); ok {
|
||||
out := &pb.FriendRandlistResp{}
|
||||
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.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(comm.ModuleWorldtask, "test"): {
|
||||
NavLabel: "触发条件",
|
||||
Desc: "触发条件",
|
||||
MainType: string(comm.ModuleWorldtask),
|
||||
SubType: "test",
|
||||
Enabled: true,
|
||||
},
|
||||
// smithy
|
||||
string(comm.ModuleSmithy): {
|
||||
NavLabel: "铁匠铺",
|
||||
MainType: string(comm.ModuleSmithy),
|
||||
Enabled: true,
|
||||
},
|
||||
common.MF(comm.ModuleSmithy, "customer"): {
|
||||
NavLabel: "顾客",
|
||||
Desc: "当前铁匠铺中的顾客",
|
||||
MainType: string(comm.ModuleSmithy),
|
||||
SubType: "customer",
|
||||
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,
|
||||
},
|
||||
}
|
||||
)
|
@ -1,32 +0,0 @@
|
||||
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,
|
||||
},
|
||||
}
|
||||
)
|
@ -1,56 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
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)
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
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
201
cmd/v2/main.go
@ -1,201 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
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 //生成类型
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package model
|
||||
|
||||
type LoginParam struct {
|
||||
Account string `json:"account"`
|
||||
ServerId string `json:"serverId"`
|
||||
TimeStamp int64 `json:"timestamp"`
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package model
|
||||
|
||||
type PbConfModel struct {
|
||||
ProtoDir string //项目目录
|
||||
OutputDir string //输出目录
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
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 //编辑器
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
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 //顺序号
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
## 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.
|
@ -1,62 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
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"
|
||||
)
|
@ -1,56 +0,0 @@
|
||||
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...)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"go_dreamfactory/cmd/v2/lib/common"
|
||||
"go_dreamfactory/cmd/v2/service/observer"
|
||||
"go_dreamfactory/comm"
|
||||
"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(comm.ModuleUser), SubType: user.UserSubTypeLogin}
|
||||
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(comm.ModuleUser), common.SubType: user.UserSubTypeLogin,
|
||||
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(comm.ModuleGate), 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(comm.ModuleUser), SubType: user.UserSubTypeCreate}
|
||||
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
|
||||
}
|
@ -1,378 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
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
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.7 KiB |
File diff suppressed because one or more lines are too long
@ -1,29 +0,0 @@
|
||||
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
698
cmd/v2/tpl.txt
@ -1,698 +0,0 @@
|
||||
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 },
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,187 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package ui
|
||||
|
||||
import "go_dreamfactory/cmd/v2/lib/common"
|
||||
|
||||
type WindowDefaultOptions struct {
|
||||
windowAction common.WindowAction
|
||||
windowMode common.WindowMode
|
||||
windowAspect common.WindowAspect
|
||||
// windowVisible bool
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
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)
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,457 +0,0 @@
|
||||
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(comm.ModuleUser) &&
|
||||
msg.SubType == user.UserSubTypeLogin {
|
||||
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()
|
||||
})
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
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/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/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(comm.ModuleGM, "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.FriendSubTypeList): &formview.FriendListView{},
|
||||
// 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.FriendSubTypeZanList): &formview.FriendZanView{},
|
||||
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(comm.ModuleRtask, "rtest"): &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(comm.ModuleSmithy, "customer"): &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{},
|
||||
}
|
||||
)
|
@ -1,26 +0,0 @@
|
||||
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)
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,436 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,251 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,263 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,788 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
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
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user