Commit 11abbdeab9a30e15143603f3d17b656e8d2b578b
1 parent
1b1ad555
csv 读取加载
Showing
11 changed files
with
400 additions
and
5 deletions
Show diff stats
.gitignore
Makefile
@@ -13,8 +13,9 @@ account: | @@ -13,8 +13,9 @@ account: | ||
13 | game: | 13 | game: |
14 | go run cmd/game.go | 14 | go run cmd/game.go |
15 | build: | 15 | build: |
16 | - go build -o bin/account account.go | ||
17 | - go build -o bin/game game.go | 16 | + go build -o bin/account cmd/account.go |
17 | + go build -o bin/game cmd/game.go | ||
18 | + go build -o bin/test test/client.go | ||
18 | 19 | ||
19 | cert: | 20 | cert: |
20 | openssl req \ | 21 | openssl req \ |
README.md
@@ -29,9 +29,10 @@ $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc | @@ -29,9 +29,10 @@ $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc | ||
29 | 29 | ||
30 | 30 | ||
31 | ## Usage | 31 | ## Usage |
32 | -编译 & 运行 | 32 | +编译 & 运行 游戏服 & 登录服务 |
33 | ```shell | 33 | ```shell |
34 | -$ make run | 34 | +$ make account |
35 | +$ make game | ||
35 | ``` | 36 | ``` |
36 | 测试 | 37 | 测试 |
37 | ```shell | 38 | ```shell |
actions/accountaction.go renamed to actions/AccountAction.go
actions/roleaction.go renamed to actions/RoleAction.go
@@ -3,6 +3,7 @@ module pro2d | @@ -3,6 +3,7 @@ module pro2d | ||
3 | go 1.17 | 3 | go 1.17 |
4 | 4 | ||
5 | require ( | 5 | require ( |
6 | + github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 | ||
6 | github.com/dgrijalva/jwt-go v3.2.0+incompatible | 7 | github.com/dgrijalva/jwt-go v3.2.0+incompatible |
7 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b | 8 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b |
8 | go.etcd.io/etcd/api/v3 v3.5.2 | 9 | go.etcd.io/etcd/api/v3 v3.5.2 |
@@ -8,6 +8,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF | @@ -8,6 +8,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF | ||
8 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | 8 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= |
9 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= | 9 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= |
10 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= | 10 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= |
11 | +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= | ||
12 | +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg= | ||
11 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | 13 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= |
12 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | 14 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= |
13 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= | 15 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= |
@@ -0,0 +1,350 @@ | @@ -0,0 +1,350 @@ | ||
1 | +package main | ||
2 | + | ||
3 | +import ( | ||
4 | + "encoding/csv" | ||
5 | + "fmt" | ||
6 | + "github.com/axgle/mahonia" | ||
7 | + "io" | ||
8 | + "io/ioutil" | ||
9 | + "log" | ||
10 | + "os" | ||
11 | + "path" | ||
12 | + "pro2d/csvdata" | ||
13 | + "reflect" | ||
14 | + "strconv" | ||
15 | + "strings" | ||
16 | + "unicode" | ||
17 | +) | ||
18 | + | ||
19 | +/** | ||
20 | + * 将excel中的前四列转化为struct | ||
21 | + * 第一列字段类型 如 int | ||
22 | + * 第二列字段名称 如 显示顺序 | ||
23 | + * 第三列字段名 如 id | ||
24 | + * 第四列s,c,all s表示服务端使用 c表示客户端使用 all表示都使用 | ||
25 | + */ | ||
26 | + | ||
27 | +var ( | ||
28 | + lineNumber = 4 // 每个工作表至少需要读取的行数 | ||
29 | + structBegin = "type %s struct {\n" // 结构体开始 | ||
30 | + structValue = " %s %s `json:\"%s\" client:\"%s\"`" // 结构体的内容 | ||
31 | + structValueForServer = " %s %s `json:\"%s\"`" // 服务端使用的结构体内容 | ||
32 | + structRemarks = " // %s" // 结构体备注 | ||
33 | + structValueEnd = "\n" // 结构体内容结束 | ||
34 | + structEnd = "}\n" // 结构体结束 | ||
35 | + header = "package %s\n\r" // 文件头 | ||
36 | + | ||
37 | + initpkg ="package csvdata\n\nimport \"sync\"\n\nvar CsvStruct map[string]interface{}\nvar CsvData *sync.Map\n\nfunc init() {\n\tCsvStruct = make(map[string]interface{})\n\tCsvData = new(sync.Map)\n\n\t%s\n}\n\nfunc GetCsv(key string) interface{}{\n\tif data, ok := CsvData.Load(key); ok {\n\t\treturn data\n\t}\n\treturn nil\n}\nfunc SetCsv(key, value interface{}) {\n\tCsvData.Store(key, value)\n}" | ||
38 | + initline = "CsvStruct[\"%s\"] = %s{}\n" | ||
39 | +) | ||
40 | + | ||
41 | +type Generate struct { | ||
42 | + savePath string // 生成文件的保存路径 | ||
43 | + data string // 生成文件的内容 | ||
44 | + allType string // 文件当中的数据类型 | ||
45 | +} | ||
46 | + | ||
47 | +func NewGenerate(savePath, allType string) *Generate { | ||
48 | + return &Generate{ | ||
49 | + savePath: savePath, | ||
50 | + allType: allType, | ||
51 | + } | ||
52 | +} | ||
53 | + | ||
54 | +// GBK 转 UTF-8 | ||
55 | +func GbkToUtf8(s string) string { | ||
56 | + decoder := mahonia.NewDecoder("GBK") | ||
57 | + return decoder.ConvertString(s) | ||
58 | +} | ||
59 | + | ||
60 | +// UTF-8 转 GBK | ||
61 | +func Utf8ToGbk(s string) string { | ||
62 | + decoder := mahonia.NewDecoder("UTF-8") | ||
63 | + return decoder.ConvertString(s) | ||
64 | +} | ||
65 | + | ||
66 | +func GetGoTByCustomerT(t string) string { | ||
67 | + switch t { | ||
68 | + case "IntSlice": | ||
69 | + return "[]int" | ||
70 | + case "StringSlice": | ||
71 | + return "[]string" | ||
72 | + case "FloatSlice": | ||
73 | + return "[]float32" | ||
74 | + default: | ||
75 | + return t | ||
76 | + } | ||
77 | +} | ||
78 | + | ||
79 | +func CustomerTypeToGo(allType string) map[string]bool { | ||
80 | + s := strings.Split(allType, ",") | ||
81 | + mt := make(map[string]bool) | ||
82 | + for _, t := range s { | ||
83 | + mt[GetGoTByCustomerT(t)] = true | ||
84 | + } | ||
85 | + return mt | ||
86 | +} | ||
87 | + | ||
88 | +//csv to struct | ||
89 | +func (g *Generate) CsvToStruct(csvpath, filename string) error { | ||
90 | + csvfile, err := os.Open(csvpath+filename) | ||
91 | + if err != nil { | ||
92 | + return fmt.Errorf("ReadExcel|xlsx.OpenFile is err :%v", err) | ||
93 | + } | ||
94 | + defer csvfile.Close() | ||
95 | + | ||
96 | + r := csv.NewReader(csvfile) | ||
97 | + i := 0 | ||
98 | + sheetData := make([][]string, 0) | ||
99 | + for { | ||
100 | + if i >= lineNumber { | ||
101 | + break | ||
102 | + } | ||
103 | + record, err := r.Read() | ||
104 | + if err == io.EOF { | ||
105 | + break | ||
106 | + } | ||
107 | + if err != nil { | ||
108 | + log.Fatal(err) | ||
109 | + } | ||
110 | + i++ | ||
111 | + sheetData = append(sheetData, record) | ||
112 | + } | ||
113 | + name := strings.Replace(filename, ".csv", "", -1) | ||
114 | + data, err := g.SplicingData(sheetData, name) | ||
115 | + if err != nil { | ||
116 | + return fmt.Errorf("fileName:\"%v\" is err:%v", name, err) | ||
117 | + } | ||
118 | + | ||
119 | + if data == "" { | ||
120 | + return fmt.Errorf("ReadExcel|this.data is nil") | ||
121 | + } | ||
122 | + err = g.WriteNewFile(name, data) | ||
123 | + if err != nil { | ||
124 | + return err | ||
125 | + } | ||
126 | + | ||
127 | + return nil | ||
128 | +} | ||
129 | + | ||
130 | +//csv to mem | ||
131 | +func (g *Generate)CsvToMem(csvpath, filename string) error { | ||
132 | + csvfile, err := os.Open(csvpath+filename) | ||
133 | + if err != nil { | ||
134 | + return fmt.Errorf("ReadExcel|xlsx.OpenFile is err :%v", err) | ||
135 | + } | ||
136 | + defer csvfile.Close() | ||
137 | + | ||
138 | + r := csv.NewReader(csvfile) | ||
139 | + i := 0 | ||
140 | + sheetData := make([][]string, 0) | ||
141 | + name := strings.Replace(filename, ".csv", "", -1) | ||
142 | + for { | ||
143 | + record, err := r.Read() | ||
144 | + if err == io.EOF { | ||
145 | + break | ||
146 | + } | ||
147 | + if err != nil { | ||
148 | + log.Fatal(err) | ||
149 | + } | ||
150 | + | ||
151 | + if i < lineNumber { | ||
152 | + sheetData = append(sheetData, record) | ||
153 | + }else { | ||
154 | + //4行之后 | ||
155 | + fields := sheetData[1] | ||
156 | + typs := sheetData[2] //int, string | ||
157 | + //keys := sheetData[3] //all, s, c | ||
158 | + | ||
159 | + t := reflect.ValueOf(csvdata.CsvStruct[name]).Type() | ||
160 | + v := reflect.New(t).Elem() | ||
161 | + for idx, field := range fields { | ||
162 | + if field == "" || record[idx] == "" { | ||
163 | + continue | ||
164 | + } | ||
165 | + | ||
166 | + f := v.FieldByName(firstRuneToUpper(field)) | ||
167 | + switch typs[idx] { | ||
168 | + case "int": | ||
169 | + id, err := strconv.Atoi(record[idx]) | ||
170 | + if err != nil { | ||
171 | + return err | ||
172 | + } | ||
173 | + f.SetInt(int64(id)) | ||
174 | + case "string": | ||
175 | + f.SetString(record[idx]) | ||
176 | + } | ||
177 | + | ||
178 | + //fmt.Printf("%d, %s: %v, %v\n", idx, firstRuneToUpper(field), v.Field(idx), v.Interface()) | ||
179 | + csvdata.SetCsv(name, v.Interface()) | ||
180 | + } | ||
181 | + } | ||
182 | + i++ | ||
183 | + } | ||
184 | + return nil | ||
185 | +} | ||
186 | + | ||
187 | +//拼装struct | ||
188 | +func (g *Generate) SplicingData(data [][]string, structName string) (string, error) { | ||
189 | + if len(data) != lineNumber { | ||
190 | + return "", fmt.Errorf("SplicingData|sheetName:%v col's len:%d is err", data, len(data)) | ||
191 | + } | ||
192 | + marks := data[0] | ||
193 | + fields := data[1] | ||
194 | + typs := data[2] | ||
195 | + keys := data[3] | ||
196 | + structData := fmt.Sprintf(structBegin, firstRuneToUpper(structName)) | ||
197 | + | ||
198 | + for i, field := range fields { | ||
199 | + err := g.CheckType(typs[i], structName) | ||
200 | + if err != nil { | ||
201 | + return "", err | ||
202 | + } | ||
203 | + switch keys[i] { | ||
204 | + case "key","all": | ||
205 | + structData += fmt.Sprintf(structValue, firstRuneToUpper(field), GetGoTByCustomerT(typs[i]), strings.ToLower(field), strings.ToLower(field)) | ||
206 | + if field != "" { | ||
207 | + structData += fmt.Sprintf(structRemarks, GbkToUtf8(marks[i])) | ||
208 | + } | ||
209 | + structData += fmt.Sprintf(structValueEnd) | ||
210 | + case "s": | ||
211 | + structData += fmt.Sprintf(structValueForServer, firstRuneToUpper(field), GetGoTByCustomerT(typs[i]), strings.ToLower(field)) | ||
212 | + if field != "" { | ||
213 | + structData += fmt.Sprintf(structRemarks,GbkToUtf8(marks[i])) | ||
214 | + } | ||
215 | + structData += fmt.Sprintf(structValueEnd) | ||
216 | + case "c": | ||
217 | + continue | ||
218 | + default: | ||
219 | + return "", fmt.Errorf("SplicingData|keys[%d]:\"%v\" is not in s,c,all", i, keys[i]) | ||
220 | + } | ||
221 | + } | ||
222 | + structData += structEnd | ||
223 | + return structData, nil | ||
224 | +} | ||
225 | + | ||
226 | +// 拼装好的struct写入新的文件 | ||
227 | +func (g *Generate) WriteNewFile(filename, data string) error { | ||
228 | + str := strings.Split(g.savePath, "/") | ||
229 | + if len(str) == 0 { | ||
230 | + return fmt.Errorf("WriteNewFile|len(str) is 0") | ||
231 | + } | ||
232 | + | ||
233 | + header = fmt.Sprintf(header, str[len(str)-1]) | ||
234 | + data = header + data | ||
235 | + | ||
236 | + fw, err := os.OpenFile(g.savePath +"/" +filename+".go", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) | ||
237 | + if err != nil { | ||
238 | + return fmt.Errorf("WriteNewFile|OpenFile is err:%v", err) | ||
239 | + } | ||
240 | + defer fw.Close() | ||
241 | + | ||
242 | + _, err = fw.Write([]byte(data)) | ||
243 | + if err != nil { | ||
244 | + return fmt.Errorf("WriteNewFile|Write is err:%v", err) | ||
245 | + } | ||
246 | + | ||
247 | + return nil | ||
248 | +} | ||
249 | + | ||
250 | +// 拼装好的初始化写入init.go文件 | ||
251 | +func (g *Generate) WriteInitPkg(data string) error { | ||
252 | + str := strings.Split(g.savePath, "/") | ||
253 | + if len(str) == 0 { | ||
254 | + return fmt.Errorf("WriteNewFile|len(str) is 0") | ||
255 | + } | ||
256 | + | ||
257 | + fw, err := os.OpenFile(g.savePath +"/init.go", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) | ||
258 | + if err != nil { | ||
259 | + return fmt.Errorf("WriteNewFile|OpenFile is err:%v", err) | ||
260 | + } | ||
261 | + defer fw.Close() | ||
262 | + | ||
263 | + _, err = fw.Write([]byte(fmt.Sprintf(initpkg, data))) | ||
264 | + if err != nil { | ||
265 | + return fmt.Errorf("WriteNewFile|Write is err:%v", err) | ||
266 | + } | ||
267 | + | ||
268 | + return nil | ||
269 | +} | ||
270 | + | ||
271 | +// 检测解析出来的字段类型是否符合要求 | ||
272 | +func (g *Generate) CheckType(dataType, structName string) error { | ||
273 | + res := strings.Index(g.allType, dataType) | ||
274 | + if res == -1 { | ||
275 | + return fmt.Errorf("CheckType|struct:\"%v\" dataType:\"%v\" is not in provide dataType", structName, dataType) | ||
276 | + } | ||
277 | + return nil | ||
278 | +} | ||
279 | + | ||
280 | +// 字符串首字母转换成大写 | ||
281 | +func firstRuneToUpper(str string) string { | ||
282 | + data := []byte(str) | ||
283 | + for k, v := range data { | ||
284 | + if k == 0 { | ||
285 | + first := []byte(strings.ToUpper(string(v))) | ||
286 | + newData := data[1:] | ||
287 | + data = append(first, newData...) | ||
288 | + break | ||
289 | + } | ||
290 | + } | ||
291 | + return string(data[:]) | ||
292 | +} | ||
293 | + | ||
294 | +// 判断是否存在汉字或者是否为默认的工作表 | ||
295 | +func hasChineseOrDefault(r string) bool { | ||
296 | + if strings.Index(r, "Sheet") != -1 { | ||
297 | + return true | ||
298 | + } | ||
299 | + for _, v := range []rune(r) { | ||
300 | + if unicode.Is(unicode.Han, v) { | ||
301 | + return true | ||
302 | + } | ||
303 | + } | ||
304 | + return false | ||
305 | +} | ||
306 | + | ||
307 | +// 读取Csv 转化为 struct | ||
308 | +func (g *Generate) ReadCsvToStruct(readPath string) error { | ||
309 | + files, err := ioutil.ReadDir(readPath) | ||
310 | + if err != nil { | ||
311 | + return fmt.Errorf("ReadExcel|ReadDir is err:%v", err) | ||
312 | + } | ||
313 | + | ||
314 | + | ||
315 | + data := "" | ||
316 | + for _, file := range files { | ||
317 | + if path.Ext(file.Name()) != ".csv" || hasChineseOrDefault(file.Name()) { | ||
318 | + continue | ||
319 | + } | ||
320 | + err := g.CsvToStruct(readPath, file.Name()) | ||
321 | + if err != nil { | ||
322 | + return err | ||
323 | + } | ||
324 | + | ||
325 | + name := strings.Replace(file.Name(), ".csv", "", -1) | ||
326 | + data += fmt.Sprintf(initline, name, firstRuneToUpper(name)) | ||
327 | + } | ||
328 | + g.WriteInitPkg(data) | ||
329 | + return nil | ||
330 | +} | ||
331 | + | ||
332 | +// 读取Csv 转化为 memory数据 | ||
333 | +func (g *Generate) ReadCsvToMemory(readPath string) error { | ||
334 | + files, err := ioutil.ReadDir(readPath) | ||
335 | + if err != nil { | ||
336 | + return fmt.Errorf("ReadExcel|ReadDir is err:%v", err) | ||
337 | + } | ||
338 | + | ||
339 | + for _, file := range files { | ||
340 | + if path.Ext(file.Name()) != ".csv" || hasChineseOrDefault(file.Name()) { | ||
341 | + continue | ||
342 | + } | ||
343 | + err := g.CsvToMem(readPath, file.Name()) | ||
344 | + if err != nil { | ||
345 | + return err | ||
346 | + } | ||
347 | + | ||
348 | + } | ||
349 | + return nil | ||
350 | +} |
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +package main | ||
2 | + | ||
3 | +import ( | ||
4 | + "flag" | ||
5 | + "fmt" | ||
6 | + _ "pro2d/csvdata" | ||
7 | +) | ||
8 | + | ||
9 | +var ( | ||
10 | + savePath = flag.String("savePath", "/Users/mac/Documents/project/Pro2D/Pro2DServer/csvdata", "Path to save the makefile") | ||
11 | + readPath = flag.String("readPath", "/Users/mac/Documents/project/Pro2D/Pro2DServer/tools/", "The path of reading Excel") | ||
12 | + allType = flag.String("allType", "int, string", "Specified field type") | ||
13 | +) | ||
14 | + | ||
15 | +func main() { | ||
16 | + flag.Parse() | ||
17 | + if *savePath == "" || *readPath == "" || *allType == "" { | ||
18 | + fmt.Println("savePath, readPath or allType is nil") | ||
19 | + return | ||
20 | + } | ||
21 | + | ||
22 | + gt := NewGenerate(*savePath, *allType) | ||
23 | + //err := gt.ReadCsvToStruct(*readPath) | ||
24 | + err := gt.ReadCsvToMemory(*readPath) | ||
25 | + if err != nil { | ||
26 | + fmt.Printf("something err:%v\n", err) | ||
27 | + return | ||
28 | + } | ||
29 | +} | ||
0 | \ No newline at end of file | 30 | \ No newline at end of file |
utils/utils.go
@@ -44,4 +44,13 @@ func FindIndex(schema interface{}) (string, []string){ | @@ -44,4 +44,13 @@ func FindIndex(schema interface{}) (string, []string){ | ||
44 | } | 44 | } |
45 | } | 45 | } |
46 | return strings.ToLower(s.Name()), index | 46 | return strings.ToLower(s.Name()), index |
47 | +} | ||
48 | + | ||
49 | +func GetIdxBySlice(s []interface{}, i interface{}) (int){ | ||
50 | + for idx, v := range s { | ||
51 | + if v == i { | ||
52 | + return idx | ||
53 | + } | ||
54 | + } | ||
55 | + return -1 | ||
47 | } | 56 | } |
48 | \ No newline at end of file | 57 | \ No newline at end of file |