This commit is contained in:
wh_zcy 2023-06-09 18:11:18 +08:00
parent 2dc37245e4
commit c870813677
182 changed files with 0 additions and 17963 deletions

View File

@ -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()
})
}
}

View File

@ -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))
}

View File

@ -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.")
// }
}

View File

@ -1,14 +0,0 @@
package robot
// 场景:在测试的接口中,输入参数为了能达到自动化,都是从业务中已有的协议中返回获取,作为下一个协议的输入参数。
// 但是会存在协议中的输入参数无法从上一个协议中获取比如添加好友协议输入参数是“目标玩家ID”为了能够动态
// 测试添加好友协议,如果专门为这个协议添加返回目标玩家的接口,显然有点不合适。一个方案是,设计一个助手,专门处
// 理这类问题,这个助手可以返回合适的数据,作为输入参数。
type assistant struct {
}
func NewAssistant() *assistant {
return &assistant{}
}

View File

@ -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")
})
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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()
}
```

View File

@ -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
}

View File

@ -1,15 +0,0 @@
package robot
var (
storyBuilders = []*TestCase{
{
//enabled: true,
},
}
)
//声明加入到构建器并发起请求
func (r *Robot) RunMainline() {
r.addBuilders(storyBuilders)
}

View File

@ -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)
}

View File

@ -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)
}

View File

View File

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

View File

@ -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)
}
}

View File

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

View File

@ -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
}

View File

@ -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
}

View File

@ -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> &nbsp;&nbsp; <span>${ dir.dateTime}</span>
</list-item>
</list>
</div>
<script>
new Vue({
el: "#app",
delimiters: ['${', '}'],
data: {
zips: [],
dirs: [],
},
methods: {
handleSuccess(response, file, fileList) {
this.zips.push(response.url)
}
},
created() {
axios.get("/dirs").then((res) => {
this.dirs = res.data.dirs
})
},
})
</script>
</body>
</html>

View File

@ -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> &nbsp;&nbsp; <span>${ dir.dateTime}</span>
</list-item>
</list>
</div>
<script>
new Vue({
el: "#app",
delimiters: ['${', '}'],
data: {
zips: [],
dirs: [],
},
methods: {
handleSuccess(response, file, fileList) {
this.zips.push(response.url)
}
},
created() {
axios.get("/dirs").then((res) => {
this.dirs = res.data.dirs
})
},
})
</script>
</body>
</html>

BIN
cmd/v2/.DS_Store vendored

Binary file not shown.

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

View File

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

View File

@ -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)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

View File

@ -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)
}

View File

@ -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
)

View File

@ -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"
)

View File

@ -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--
}
}
}
}

View File

@ -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 = "更新"
)

View File

@ -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{},
}
}

View File

@ -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"`
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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()
}()
}
}

View File

@ -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:]
}

View File

@ -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))
}

View File

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

View File

@ -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))
}

View File

@ -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 //协议名称
}

View File

@ -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
}

View File

@ -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,
},
}
)

View File

@ -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,
},
}
)

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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 //生成类型
}

View File

@ -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
}

View File

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

View File

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

View File

@ -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
}

View File

@ -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 //编辑器
}

View File

@ -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 //顺序号
}

View File

@ -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.

View File

@ -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()
}

View File

@ -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)
}
}

View File

@ -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"
)

View File

@ -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...)
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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)
}

View File

@ -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 },
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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()
})
}

View File

@ -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{},
}
)

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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