Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
wang
/
golang-open-platform
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
3ff83008
authored
Feb 26, 2021
by
wang
Browse files
Options
_('Browse Files')
Download
Plain Diff
流量限制功能发布
parents
aa55afc5
af56849f
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
121 additions
and
63 deletions
.gitignore
cmd/http/http_server.go
doc/redis_config_generate.go
model/filter.go
model/sku_model.go
open/Code.go
open/flowmeter.go
open/validate.go
.gitignore
View file @
3ff83008
...
@@ -26,4 +26,5 @@ cmd.exe~
...
@@ -26,4 +26,5 @@ cmd.exe~
/tests
/tests
/controller/test.go
/controller/test.go
/regconfig
/regconfig
/cmd/http/regTest
/cmd/http/regTest
\ No newline at end of file
/cmd/http/redisTest
cmd/http/http_server.go
View file @
3ff83008
...
@@ -3,8 +3,6 @@ package main
...
@@ -3,8 +3,6 @@ package main
import
(
import
(
"flag"
"flag"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
Iconfig
"github.com/ichunt2019/ichunt-micro-registry/config"
Iregistry
"github.com/ichunt2019/ichunt-micro-registry/registry"
_
"github.com/ichunt2019/ichunt-micro-registry/registry/etcd"
_
"github.com/ichunt2019/ichunt-micro-registry/registry/etcd"
"github.com/micro/go-micro/v2/web"
"github.com/micro/go-micro/v2/web"
"golang_open_platform/boot"
"golang_open_platform/boot"
...
@@ -23,7 +21,6 @@ func main() {
...
@@ -23,7 +21,6 @@ func main() {
r
:=
routes
.
InitRouter
()
r
:=
routes
.
InitRouter
()
port
:=
config
.
Get
(
"web.port"
)
.
String
()
port
:=
config
.
Get
(
"web.port"
)
.
String
()
//web改成micro 就是grpc,并直接注册到etcd里面
//web改成micro 就是grpc,并直接注册到etcd里面
Registry
()
service
:=
web
.
NewService
(
service
:=
web
.
NewService
(
web
.
Name
(
"go.micro.api.http.search"
),
web
.
Name
(
"go.micro.api.http.search"
),
web
.
Handler
(
r
),
web
.
Handler
(
r
),
...
@@ -35,23 +32,4 @@ func main() {
...
@@ -35,23 +32,4 @@ func main() {
if
err
:=
service
.
Run
();
err
!=
nil
{
if
err
:=
service
.
Run
();
err
!=
nil
{
panic
(
err
)
panic
(
err
)
}
}
}
}
func
Registry
(){
nodes
:=
[]
*
Iregistry
.
Node
{
{
IP
:
"192.168.2.72"
,
//当前服务的ip
Port
:
60006
,
//当前服务的端口
Weight
:
2
,
//当前服务的权重
},
}
etcdConfig
:=
Iregistry
.
EtcdConfig
{
Address
:
[]
string
{
"192.168.2.232:2379"
},
//etcd的节点ip
Username
:
""
,
//etcd的节点的用户名
Password
:
""
,
//etcd的节点的密码
Path
:
"/ichuntMicroService/"
,
//网关前缀,目前固定写即可
}
Iconfig
.
Register
(
"open"
,
etcdConfig
,
nodes
)
// 第一个参数:服务名,第二个参数etcd配置,第三个参数当前注册节点信息
}
\ No newline at end of file
doc/redis_config_generate.go
View file @
3ff83008
...
@@ -25,6 +25,7 @@ var(
...
@@ -25,6 +25,7 @@ var(
//redis 表key
//redis 表key
openWhiteList
=
"openWhiteList"
//白名单
openWhiteList
=
"openWhiteList"
//白名单
openBlackList
=
"openBlackList"
//白名单
openTokenBusinessList
=
"openTokenBusinessList"
//商家列表
openTokenBusinessList
=
"openTokenBusinessList"
//商家列表
openBusinessInterface
=
"openBusinessInterface"
//商家接口配置
openBusinessInterface
=
"openBusinessInterface"
//商家接口配置
...
@@ -48,9 +49,11 @@ func main() {
...
@@ -48,9 +49,11 @@ func main() {
}
}
func
generate
()
{
func
generate
()
{
whiteIp
()
//
whiteIp()
businessList
()
//
businessList()
businessInterfaceList
()
businessInterfaceList
()
newWhiteBlack
()
newBusinessList
()
}
}
//白名单配置
//白名单配置
...
@@ -76,7 +79,9 @@ func whiteIp() {
...
@@ -76,7 +79,9 @@ func whiteIp() {
"192.168.2.129"
,
"192.168.2.129"
,
"19.123.78.115"
,
"19.123.78.115"
,
"222.92.21.99"
,
"222.92.21.99"
,
"192.168.2.72"
,
"::1"
,
"::1"
,
"119.123.79.225"
,
}
}
/*whiteList:=[]string{
/*whiteList:=[]string{
...
@@ -97,10 +102,31 @@ func businessList() {
...
@@ -97,10 +102,31 @@ func businessList() {
jdToken
,
//京东
jdToken
,
//京东
//baiduToken,//百度
//baiduToken,//百度
}
}
fmt
.
Println
(
"商家列表:key:"
+
redisKey
)
fmt
.
Println
(
"商家列表:key:"
+
redisKey
)
redisStringSet
(
redisKey
,
businessList
)
redisStringSet
(
redisKey
,
businessList
)
}
}
func
newBusinessList
()
{
config
:=
map
[
string
]
string
{
openTokenBusinessList
:
jdToken
,
}
fmt
.
Println
(
"新商家列表配置:"
)
redisStringSadd
(
config
)
}
func
newWhiteBlack
()
{
jdWhiteList
:=
jdToken
+
"_"
+
openWhiteList
jdBlackList
:=
jdToken
+
"_"
+
openBlackList
config
:=
map
[
string
]
string
{
jdWhiteList
:
"192.168.2.72 222.92.21.99 192.168.2.129 ::1"
,
jdBlackList
:
"::1 "
,
}
fmt
.
Println
(
"新白名单配置:"
)
redisStringSadd
(
config
)
}
//商家接口配置
//商家接口配置
func
businessInterfaceList
()
{
func
businessInterfaceList
()
{
...
@@ -110,24 +136,26 @@ func businessInterfaceList() {
...
@@ -110,24 +136,26 @@ func businessInterfaceList() {
//获取sku列表(根据class_id查询) 500,10000000
//获取sku列表(根据class_id查询) 500,10000000
//获取sku列表(full-显示所有字段) 50,10000000
//获取sku列表(full-显示所有字段) 50,10000000
jdToken
+
"_"
+
GetSkuListByClass
:
map
[
string
]
interface
{}{
jdToken
+
"_"
+
GetSkuListByClass
:
map
[
string
]
interface
{}{
"dayMaxNum"
:
61
00
,
"dayMaxNum"
:
10000
00
,
"minMaxNum"
:
20
00
,
"minMaxNum"
:
3
00
,
"totalMaxNum"
:
10000
,
"totalMaxNum"
:
10000
,
},
},
jdToken
+
"_"
+
GetSkuListFull
:
map
[
string
]
interface
{}{
jdToken
+
"_"
+
GetSkuListFull
:
map
[
string
]
interface
{}{
"dayMaxNum"
:
61
00
,
"dayMaxNum"
:
10000
00
,
"minMaxNum"
:
20
00
,
"minMaxNum"
:
3
00
,
"totalMaxNum"
:
10000
,
"totalMaxNum"
:
10000
,
},
},
jdToken
+
"_"
+
GetSkuListPrice
:
map
[
string
]
interface
{}{
jdToken
+
"_"
+
GetSkuListPrice
:
map
[
string
]
interface
{}{
"dayMaxNum"
:
61
00
,
"dayMaxNum"
:
10000
00
,
"minMaxNum"
:
20
00
,
"minMaxNum"
:
3
00
,
"totalMaxNum"
:
10000
,
"totalMaxNum"
:
10000
,
},
},
jdToken
+
"_"
+
GetClassList
:
map
[
string
]
interface
{}{
jdToken
+
"_"
+
GetClassList
:
map
[
string
]
interface
{}{
"dayMaxNum"
:
61
00
,
"dayMaxNum"
:
10000
00
,
"minMaxNum"
:
20
00
,
"minMaxNum"
:
3
00
,
"totalMaxNum"
:
10000
,
"totalMaxNum"
:
10000
,
},
},
/*baiduToken+"_"+getSkuListByClass: map[string]interface{}{
/*baiduToken+"_"+getSkuListByClass: map[string]interface{}{
"dayMaxNum":100,
"dayMaxNum":100,
...
@@ -139,14 +167,17 @@ func businessInterfaceList() {
...
@@ -139,14 +167,17 @@ func businessInterfaceList() {
redisHashSet
(
redisKey
,
config
)
redisHashSet
(
redisKey
,
config
)
}
}
func
redisHashSet
(
key
string
,
values
map
[
string
]
interface
{})
{
func
redisHashSet
(
key
string
,
values
map
[
string
]
interface
{})
{
for
hashk
,
valueOne
:=
range
values
{
for
hashk
,
valueOne
:=
range
values
{
b
,
_
:=
json
.
Marshal
(
valueOne
)
b
,
_
:=
json
.
Marshal
(
valueOne
)
b
,
_
=
json
.
Marshal
(
string
(
b
))
b
,
_
=
json
.
Marshal
(
string
(
b
))
hsetStr
:=
"Hset "
+
key
+
" "
+
string
(
hashk
)
+
" "
+
string
(
b
)
hsetStr
:=
"Hset "
+
key
+
" "
+
string
(
hashk
)
+
" "
+
string
(
b
)
fmt
.
Println
(
hsetStr
)
fmt
.
Println
(
hsetStr
)
//
fmt.Println("对应计时的min key\nflowUse_"+hashk+"_min")
fmt
.
Println
(
"对应计时的min key
\n
flowUse_"
+
hashk
+
"_min"
)
//
fmt.Println("对应计时的day key\nflowUse_"+hashk+"_day")
fmt
.
Println
(
"对应计时的day key
\n
flowUse_"
+
hashk
+
"_day"
)
}
}
}
}
...
@@ -155,5 +186,13 @@ func redisStringSet(key string,sliceString []string) {
...
@@ -155,5 +186,13 @@ func redisStringSet(key string,sliceString []string) {
b
,
_
=
json
.
Marshal
(
string
(
b
))
b
,
_
=
json
.
Marshal
(
string
(
b
))
setStr
:=
"set "
+
key
+
" "
+
string
(
b
)
setStr
:=
"set "
+
key
+
" "
+
string
(
b
)
fmt
.
Println
(
setStr
)
fmt
.
Println
(
setStr
)
}
func
redisStringSadd
(
config
map
[
string
]
string
)
{
for
hashk
,
valueOne
:=
range
config
{
SaddStr
:=
"SADD "
+
hashk
+
" "
+
valueOne
fmt
.
Println
(
SaddStr
)
//fmt.Println("对应计时的min key\nflowUse_"+hashk+"_min")
//fmt.Println("对应计时的day key\nflowUse_"+hashk+"_day")
}
}
}
\ No newline at end of file
model/filter.go
View file @
3ff83008
...
@@ -8,12 +8,12 @@ import (
...
@@ -8,12 +8,12 @@ import (
)
)
//过滤
//过滤
func
SkuFilter
(
remoteData
RemoteSkuData
,
filedStr
[]
string
)
(
data
Remote
SkuData
)
{
func
SkuFilter
(
remoteData
RemoteSkuData
,
filedStr
[]
string
)
(
Array
SkuData
)
{
remoteData
=
filterData
(
remoteData
,
filedStr
)
arraySku
:
=
filterData
(
remoteData
,
filedStr
)
if
(
len
(
remoteData
)
==
0
){
if
(
len
(
arraySku
)
==
0
){
return
nil
return
nil
}
}
return
remoteData
return
arraySku
}
}
/**
/**
...
@@ -22,9 +22,13 @@ func SkuFilter(remoteData RemoteSkuData,filedStr []string) (data RemoteSkuData)
...
@@ -22,9 +22,13 @@ func SkuFilter(remoteData RemoteSkuData,filedStr []string) (data RemoteSkuData)
@param filterField 需要过滤的字段
@param filterField 需要过滤的字段
@return data 返回新的数据
@return data 返回新的数据
*/
*/
func
filterData
(
remoteData
RemoteSkuData
,
filterField
[]
string
)(
data
RemoteSkuData
){
func
filterData
(
remoteData
RemoteSkuData
,
filterField
[]
string
)(
ArraySkuData
){
//arraySku:=make([]map[string]interface{},0)
arraySkud
:=
ArraySkuData
{}
for
skuId
,
skuInfo
:=
range
remoteData
{
for
skuId
,
skuInfo
:=
range
remoteData
{
newItem
:=
make
(
map
[
string
]
interface
{})
newItem
:=
make
(
map
[
string
]
interface
{})
/*if _,ok:=skuInfo.(bool);ok{
/*if _,ok:=skuInfo.(bool);ok{
delete(remoteData,skuId)
delete(remoteData,skuId)
continue
continue
...
@@ -41,14 +45,15 @@ func filterData(remoteData RemoteSkuData,filterField []string)(data RemoteSkuDat
...
@@ -41,14 +45,15 @@ func filterData(remoteData RemoteSkuData,filterField []string)(data RemoteSkuDat
//common.PrintStdout().Printf("skuId:%s,字段:%s 不存在",string(skuId),field)
//common.PrintStdout().Printf("skuId:%s,字段:%s 不存在",string(skuId),field)
}
}
}
}
remoteData
[
skuId
]
=
newItem
arraySkud
=
append
(
arraySkud
,
newItem
)
//remoteData[skuId]=newItem
}
else
{
//有问题的格式
}
else
{
//有问题的格式
common
.
PrintStdout
()
.
Printf
(
"skuId:%s,是个有问题的格式"
,
string
(
skuId
))
common
.
PrintStdout
()
.
Printf
(
"skuId:%s,是个有问题的格式"
,
string
(
skuId
))
delete
(
remoteData
,
skuId
)
//
delete(remoteData,skuId)
continue
continue
}
}
}
}
return
remoteData
return
arraySkud
}
}
func
filterLadder
(
ladder
interface
{})
interface
{}{
func
filterLadder
(
ladder
interface
{})
interface
{}{
...
...
model/sku_model.go
View file @
3ff83008
...
@@ -10,7 +10,7 @@ type QuerySkuCreq struct {
...
@@ -10,7 +10,7 @@ type QuerySkuCreq struct {
//根据class获取sku列表 返回参数
//根据class获取sku列表 返回参数
type
QuerySkuCrsp
struct
{
type
QuerySkuCrsp
struct
{
Count
int
`json:"count"`
Count
int
`json:"count"`
SkuData
map
[
string
]
interface
{}
`json:"sku_data"`
SkuData
ArraySkuData
`json:"sku_data"`
PageSize
int
`json:"page_size"`
PageSize
int
`json:"page_size"`
Page
int
`json:"page"`
Page
int
`json:"page"`
}
}
...
@@ -29,9 +29,12 @@ type QuerySkuReq struct {
...
@@ -29,9 +29,12 @@ type QuerySkuReq struct {
//根据goodsId获取sku列表 返回参数
//根据goodsId获取sku列表 返回参数
type
QuerySkuRsp
struct
{
type
QuerySkuRsp
struct
{
SkuData
map
[
string
]
interface
{}
`json:"sku_data"`
SkuData
ArraySkuData
`json:"sku_data"`
}
}
//============end=============
//============end=============
//商详接口返回的数据 data字段
//商详接口返回的数据 data字段
type
RemoteSkuData
map
[
string
]
interface
{}
type
RemoteSkuData
map
[
string
]
interface
{}
//
type
ArraySkuData
[]
map
[
string
]
interface
{}
open/Code.go
View file @
3ff83008
...
@@ -16,7 +16,9 @@ const (
...
@@ -16,7 +16,9 @@ const (
//白名单配置813
//白名单配置813
WHILTREDISEERR1
=
813505
//白名单读取redis错误
WHILTREDISEERR1
=
813505
//白名单读取redis错误
WHILTREDISEERR2
=
813404
//ip不在白名单里面
WHILTREDISEERR2
=
813404
//ip不在白名单里面
WHILTREDISEERR5
=
813408
//黑名单限制
WHILTREDISEERR3
=
813406
//没有设置白名单
WHILTREDISEERR3
=
813406
//没有设置白名单
WHILTREDISEERR4
=
813407
//没有设置黑名单
//锁814
//锁814
FLOWERR1
=
814505
//lua脚本执行出错
FLOWERR1
=
814505
//lua脚本执行出错
...
...
open/flowmeter.go
View file @
3ff83008
...
@@ -2,7 +2,6 @@ package open
...
@@ -2,7 +2,6 @@ package open
import
(
import
(
"context"
"context"
"fmt"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/gconv"
"github.com/gomodule/redigo/redis"
"github.com/gomodule/redigo/redis"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/common"
...
@@ -93,8 +92,8 @@ func (this *flowmeter) checkout(business *business) error{
...
@@ -93,8 +92,8 @@ func (this *flowmeter) checkout(business *business) error{
minMax
:=
interfaceConfig
.
minMaxNum
minMax
:=
interfaceConfig
.
minMaxNum
dayKey
,
_
:=
this
.
getFlowKeyAndEx
(
INCRTYPEDAY
)
dayKey
,
_
:=
this
.
getFlowKeyAndEx
(
INCRTYPEDAY
)
minKey
,
_
:=
this
.
getFlowKeyAndEx
(
INCRTYPEMIN
)
minKey
,
_
:=
this
.
getFlowKeyAndEx
(
INCRTYPEMIN
)
fmt
.
Println
(
dayKey
)
/*
fmt.Println(dayKey)
fmt
.
Println
(
minKey
)
fmt.Println(minKey)
*/
script
:=
redis
.
NewScript
(
2
,
incrLua
)
script
:=
redis
.
NewScript
(
2
,
incrLua
)
//脚本执行5秒算它卡死,用script kill杀死此次脚本
//脚本执行5秒算它卡死,用script kill杀死此次脚本
context
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
time
.
Second
*
1
)
context
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
time
.
Second
*
1
)
...
...
open/validate.go
View file @
3ff83008
...
@@ -2,9 +2,11 @@ package open
...
@@ -2,9 +2,11 @@ package open
import
(
import
(
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
"github.com/gomodule/redigo/redis"
"github.com/tidwall/gjson"
"github.com/tidwall/gjson"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/e"
"golang_open_platform/pkg/e"
"golang_open_platform/pkg/gredis"
"strconv"
"strconv"
)
)
...
@@ -17,6 +19,36 @@ func NewopenValidate(business *business) *openValidate{
...
@@ -17,6 +19,36 @@ func NewopenValidate(business *business) *openValidate{
return
&
openValidate
{
business
:
business
,
dao
:&
Dao
{}}
return
&
openValidate
{
business
:
business
,
dao
:&
Dao
{}}
}
}
//ip限制验证(白名单黑名单)
func
(
this
*
openValidate
)
ipValidate
(
ctx
*
gin
.
Context
,
token
string
)
error
{
redisCon
:=
gredis
.
Conn
(
"search_w"
)
defer
redisCon
.
Close
()
ip
:=
ctx
.
ClientIP
()
openWhiteList
:=
token
+
"_openWhiteList"
//白名单
openBlackList
:=
token
+
"_openBlackList"
//黑名单
boolInt
,
err
:=
redis
.
Int
(
redisCon
.
Do
(
"SISMEMBER"
,
openBlackList
,
ip
))
if
(
err
!=
nil
&&
err
!=
redis
.
ErrNil
){
common
.
PrintStdout
()
.
Printf
(
"读取黑名单redis 出错"
)
return
e
.
NewApiError
(
"service err"
,
WHILTREDISEERR5
)
}
if
(
boolInt
==
1
){
common
.
PrintStdout
()
.
Printf
(
"黑名单限制:"
+
ctx
.
ClientIP
())
return
e
.
NewApiError
(
"ip黑名单限制"
,
WHILTREDISEERR5
)
}
boolInt
,
err
=
redis
.
Int
(
redisCon
.
Do
(
"SISMEMBER"
,
openWhiteList
,
ip
))
if
(
err
!=
nil
&&
err
!=
redis
.
ErrNil
){
common
.
PrintStdout
()
.
Printf
(
"读取白名单redis 出错"
)
return
e
.
NewApiError
(
"service err"
,
WHILTREDISEERR1
)
}
if
(
boolInt
!=
1
){
common
.
PrintStdout
()
.
Printf
(
"ip不在白名单:"
+
ctx
.
ClientIP
())
return
e
.
NewApiError
(
"ip白名单限制"
,
WHILTREDISEERR2
)
}
return
nil
}
//白名单验证
//白名单验证
func
(
this
*
openValidate
)
whiteValidate
(
ctx
*
gin
.
Context
)
error
{
func
(
this
*
openValidate
)
whiteValidate
(
ctx
*
gin
.
Context
)
error
{
...
@@ -42,21 +74,21 @@ func (this *openValidate) whiteValidate(ctx *gin.Context)error {
...
@@ -42,21 +74,21 @@ func (this *openValidate) whiteValidate(ctx *gin.Context)error {
根据token到 商家表查询,如果存在,token就存在
根据token到 商家表查询,如果存在,token就存在
*/
*/
func
(
this
*
openValidate
)
exitValidate
(
token
string
)
error
{
func
(
this
*
openValidate
)
exitValidate
(
token
string
)
error
{
businessList
,
err
:=
this
.
dao
.
getBusinessList
()
redisCon
:=
gredis
.
Conn
(
"search_w"
)
if
(
err
!=
nil
){
defer
redisCon
.
Close
()
return
err
key
:=
"openTokenBusinessList"
}
business
:=
gjson
.
Parse
(
businessList
)
if
(
businessList
==
""
||
business
.
IsArray
()
!=
true
||
len
(
business
.
Array
())
<=
0
){
common
.
PrintStdout
()
.
Printf
(
strconv
.
Itoa
(
WHILTREDISEERR3
)
+
":token 没有设置"
)
return
e
.
NewApiError
(
"white err"
,
WHILTREDISEERR3
)
boolInt
,
err
:=
redis
.
Int
(
redisCon
.
Do
(
"SISMEMBER"
,
key
,
token
))
if
(
err
!=
nil
&&
err
!=
redis
.
ErrNil
){
common
.
PrintStdout
()
.
Printf
(
"读取供应商列表出错 出错"
)
return
e
.
NewApiError
(
"service err"
,
BUSINESSERR1
)
}
}
if
(
InArrayGjson
(
token
,
business
.
Array
())){
if
(
boolInt
!=
1
){
return
nil
common
.
PrintStdout
()
.
Printf
(
strconv
.
Itoa
(
BUSINESSERR2
)
+
":token 无效:"
+
token
)
return
e
.
NewApiError
(
"token invalid"
,
BUSINESSERR2
)
}
}
common
.
PrintStdout
()
.
Printf
(
strconv
.
Itoa
(
BUSINESSERR2
)
+
":token 无效:"
+
token
)
return
nil
return
e
.
NewApiError
(
"token invalid"
,
BUSINESSERR2
)
}
}
/**
/**
...
@@ -74,7 +106,7 @@ func (this *openValidate) permissionValidate(interfaceName string) error{
...
@@ -74,7 +106,7 @@ func (this *openValidate) permissionValidate(interfaceName string) error{
//check验证
//check验证
func
(
this
*
openValidate
)
Check
(
ctx
*
gin
.
Context
,
interfaceName
string
)
error
{
func
(
this
*
openValidate
)
Check
(
ctx
*
gin
.
Context
,
interfaceName
string
)
error
{
err
:=
this
.
whiteValidate
(
ctx
)
err
:=
this
.
ipValidate
(
ctx
,
this
.
business
.
token
)
if
(
err
!=
nil
){
if
(
err
!=
nil
){
return
err
return
err
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment