Commit 2cff7c1d by wang

开放平台第一版本

parents
Showing with 4239 additions and 0 deletions
/go.sum
/.idea/
gowatch.yml
*.exe
*.exe~
cmd.exe~
/cmd/logs/
/cmd/*.exe~
/cmd/logs
/bat/logs/
/conf/prod/*.ini
/go.mod
/logs
/mylogs
/doc/test
/vm.sh
/cmd/http
/doc/spuTest
/doc/test3
package boot
import (
"golang_open_platform/pkg/config"
"golang_open_platform/pkg/gredis"
"golang_open_platform/pkg/logger"
"golang_open_platform/pkg/mongo"
"golang_open_platform/pkg/mysql"
)
func Boot(configPath string) (err error) {
if err = config.SetUp(configPath); err != nil {
panic(err)
}
if err = mysql.Setup(); err != nil {
panic(err)
return
}
if err = gredis.Setup(); err != nil {
panic(err)
return
}
if err = mongo.SetUp(); err != nil {
panic(err)
return
}
logger.Loginit()
return
}
;总配置信息
[web]
port = 60006
mode = debug
[sku_server]
api_domain = http://192.168.1.237:60014
File mode changed
; 比如 sku_save,5000 路径 sku_save 即文件夹是 sku 文件名类似 是save_2020-12-10.log,5000代表队列的容量为5000
[log_config]
1=sku_query,5000
;存放mongodb连接信息
[mongo]
host = 192.168.1.237:27017
username = "ichunt"
password = "huntmon6699"
database = ichunt
maxPoolSize=8000
\ No newline at end of file
;存放rabmq连接信息
[rabmq]
;redis连接信息
[default_redis_read]
host = 192.168.1.235:6379
password = icDb29mLy2s
max_idle = 1000
max_active = 5000
idle_timeout = 20
[default_redis_write]
host = 192.168.1.235:6379
password = icDb29mLy2s
max_idle = 1000
max_active = 5000
idle_timeout = 20
;存放redis所有键
[redis_all]
\ No newline at end of file
package controller
import (
"fmt"
"github.com/gin-gonic/gin"
"golang_open_platform/framework/gin_"
"golang_open_platform/open"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/config"
"golang_open_platform/pkg/e"
"golang_open_platform/pkg/logger"
"net/http"
)
//放 通用中间件
//检查服务是否可用
func Check_Middleware() gin_.Middleware {
return func(next gin_.Endpoint) gin_.Endpoint {
return func(context *gin.Context, request interface{}) (response interface{}, err error) {
return next(context, request)
}
}
}
/**
@param tnterfaceName 接口别名
*/
func Open_Middleware(tnterfaceName string) gin.HandlerFunc {
return func(ctx *gin.Context) {
tokenStr:= ctx.Request.FormValue("token")
openObj:=open.NewOpen(tokenStr)
e.CheckError(openObj.Validate.Check(ctx,tnterfaceName))
ctx.Next()
}
}
func Cors_Middleware() gin_.Middleware {
return func(next gin_.Endpoint) gin_.Endpoint {
return func(c *gin.Context, request interface{}) (response interface{}, err error) {
method := c.Request.Method
corsDomains := config.Get("web.cors.domain").Strings(",")
for _, domain := range corsDomains {
c.Header("Access-Control-Allow-Origin", domain)
}
c.Header("Access-Control-Allow-Origin", "https://bom.ichunt.com")
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
//放行所有OPTIONS方法
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
// 处理请求
return next(c, request)
}
}
}
/**
@author wangsong
捕获 系统内部 panic 异常,并加入日志
*/
func Error_Middleware(logPaths ...string) gin.HandlerFunc {
logPath:="zysku_save"
if(len(logPaths)>0){
logPath=logPaths[0]
}
return func(ctx *gin.Context) {
defer func() {
if err:=recover(); err!=nil{
if apiError,ok:=err.(*e.ApiError);ok{
fmt.Println(apiError.ErrMsg)
common.NResponse(apiError.ErrMsg,apiError.Code).SetLogHandel(errlogHandle(logPath)).OpenParamLog().OutPut(ctx)
}else{
errMsg:=fmt.Sprintf("%s",err)
fmt.Println(errMsg)
common.NResponse("service err",500).SetLogHandel(errlogHandle(logPath)).OpenParamLog().OutPut(ctx)
}
ctx.Abort()
}
}()
ctx.Next()
}
}
/**
错误日志处理,是给 NResponse的SetLogHandel 方法使用(其他同事用不到NResponse的SetLogHandel可以不用管它)
@param logPath 对应 log.ini的配置文件 比如 lysku_save
@return func
*/
func errlogHandle(logPath string) func(msg string){
return func(msg string) {
logger.Select(logPath).Error(msg)
}
}
\ No newline at end of file
package controller
import (
"github.com/gin-gonic/gin"
"golang_open_platform/model"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/e"
"golang_open_platform/service"
)
/*
健康监测
*/
func Hbsdata(ctx *gin.Context) {
ctx.String(200, "ok")
}
/**
用classId获取sku列表
*/
func GetSkuListByClass(ctx *gin.Context) {
req:=&model.QuerySkuCreq{}
e.CheckError(model.ValidateBind(ctx,req))
rsp,err:=service.NewSkuService().GetSkuListByClass(req)
e.CheckError(err)
common.NResponse("",200,rsp).OutPut(ctx)
}
/**
用goods_id 获取sku列表完整字段
*/
func GetSkuListFull(ctx *gin.Context) {
req:=&model.QuerySkuReq{}
e.CheckError(model.ValidateBind(ctx,req))
rsp,err:=service.NewSkuService().GetSkuListFull(req)
e.CheckError(err)
common.NResponse("",200,rsp).OutPut(ctx)
}
/**
用goods_id 获取sku列表价格库存字段
*/
func GetSkuListPrice(ctx *gin.Context) {
req:=&model.QuerySkuReq{}
e.CheckError(model.ValidateBind(ctx,req))
rsp,err:=service.NewSkuService().GetSkuListPrice(req)
e.CheckError(err)
common.NResponse("",200,rsp).OutPut(ctx)
}
package dao
import (
"golang_open_platform/model"
"golang_open_platform/pkg/mongo"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
//根据class id获取mongo sku_open表里的内容
func GetMongoOpenSku(classId int,page int) (mongoSkuOpen *model.MongoSkuOpen,err error) {
mongodb := mongo.Conn("default")
defer func() {
mongodb.Close()
}()
mongoSkuOpen=&model.MongoSkuOpen{}
err= mongodb.DB("ichunt").C("sku_open").Find(bson.M{"class_id2": classId,"page":page}).One(&mongoSkuOpen)
if err != nil {
if(err == mgo.ErrNotFound){
return mongoSkuOpen,nil
}
return
}
return
}
//
func getSkuByClass() {
}
//
func getSkuFull() {
}
func getSkuPriceStock() {
}
package main
import (
"fmt"
"github.com/gogf/gf/util/gconv"
)
func main() {
//str:="dfjksldjfksjdkfjskdfjk"
//byteStr:=[]byte(str)
inta:="123123123"
inta2:="1"
s1:=gconv.Int(string(inta))
s2:=gconv.Int(string(inta2))
s3:=gconv.Int([]uint8(inta))
fmt.Println(s3)
fmt.Println(s1,s2)
/*fmt.Println([]byte(inta),byteStr,[]uint8(inta))
uint8S:=[]uint8(inta)
uint8S2:=[]uint8(inta2)
fmt.Println(binary.BigEndian.Uint32(uint8S))
fmt.Println(binary.BigEndian.Uint32(uint8S2))*/
}
package main
import (
"fmt"
"regexp"
"unicode"
)
func main() {
str:="中的开发健康的反馈123123sdfsd"
str2:="fsdfsdf"
fmt.Println(IsChineseChar(str))
fmt.Println(IsChineseChar(str2))
}
func IsChineseChar(str string) bool {
for _, r := range str {
if unicode.Is(unicode.Scripts["Han"], r) || (regexp.MustCompile("[\u3002\uff1b\uff0c\uff1a\u201c\u201d\uff08\uff09\u3001\uff1f\u300a\u300b]").MatchString(string(r))) {
return true
}
}
return false
}
package main
import "fmt"
func main() {
defer func() {
fmt.Print("最后在还行的")
}()
panic(123)
}
func exec1() {
}
package main
import "fmt"
func main() {
fmt.Println(f2());
//result := watShadowDefer(50)
//fmt.Println(result)
}
func watShadowDefer(i int) (ret int) {
ret = i * 2
if ret > 10 {
fmt.Println(ret)
ret := 10
defer func() int{
ret = ret + 1
return ret
}()
fmt.Println(ret)
}
fmt.Println(ret)
return
}
func f2() (r int) {
t := 5
defer func(t int) {
t = t + 5
fmt.Printf("defer内部 t等于: %d\n",t)
}(t)
fmt.Printf("return 前 t等于: %d\n",t)
return t
}
func test() {
ret:=100
if ret > 10 {
fmt.Println(ret)
ret := 10
/*defer func() {
ret = ret + 1
}()*/
fmt.Println(ret)
}
fmt.Println(ret)
}
package main
import "fmt"
type SS struct {
a int
b int
}
var SSS *SS
func main() {
SSS=&SS{}
SSS.a=2
SSS.b=2
ju(SSS.b)
}
func ju(t int) {
switch t {
case SSS.a :
fmt.Println("a")
case SSS.b:
fmt.Printf("b")
}
}
package main
import (
"encoding/json"
"fmt"
"github.com/tidwall/gjson"
)
type testS struct {
A int `json:"a"`
B string `json:"b"`
}
func main() {
gjsontest()
return
testS:=testS{}
jsonStr:=`{"a":"123","b":"dfdf"}`
err:=json.Unmarshal([]byte(jsonStr),&testS)
fmt.Println(err.Error())
}
func gjsontest() {
s:="dfjklsjdfklsjdfl"
s=`{"a":"bcd"}`
res:=gjson.Parse(s)
if(res.Exists()){
fmt.Printf("1")
}else{
fmt.Printf("2")
}
}
\ No newline at end of file
package main
import (
"fmt"
"strings"
)
func main() {
s:="a b c d e"
s1:=strings.Replace(s," ",",",-1)
fmt.Printf(s1)
}
### 日志
#### 同步调用方法(不需要额外配置)
```json
import ("golang_open_platform/pkg/logger")
logger.SyncInsert("sku_save","记录一下日志",logger.LogLevelError) //记录错误日志
logger.SyncInsert("sku_save","记录一下日志",logger.LogLevelDebug) //记录debug日志
logger.SyncInsert("sku_save","记录一下日志") //默认记录info
```
### 异步日志
#### 配置文件
```ini
[log_config]
1=sku_save,5
2=sku_edit,5
```
比如 sku_save,5 路径 sku_save 即文件夹是 sku 文件名类似 是save_2020-12-10.log
#### 调用示例
```json
import ("golang_open_platform/pkg/logger")
logger.Select("sku_save").Info(errMsg)
logger.Select("sku_save").Error(errMsg)
```
\ No newline at end of file
### 请求参数示例
batch_sn
spu_brief
goods_images
```json
{
"stock": 2055,
"pn": "maritex",
"is_api": 0,
"url": "https://en.maritex.com.pl/acoustics/audio_transducers/magnetic_buzzers_without_driving_circuit/cfd02-e.html",
"goods_name": "CFD02-E6",
"goods_sn": "CFD02-E",
"brand_name": "Changzhou EST ELECTRONICS Co.,Ltd.",
"is_error": 0,
"supplier_id": 17,
"canal": "L0010826",
"mpq": 1,
"moq": 1,
"pdf": "",
"hk_delivery_time": "5-7\u5de5\u4f5c\u65e5",
"cn_delivery_time": "6-12\u5de5\u4f5c\u65e5",
"ladder_price": [
{
"purchases": 1,
"price_cn": 0.2886,
"price_us": 0.2886
},
{
"purchases": 25,
"price_cn": 0.2662,
"price_us": 0.2662
}
],
"attrs":{
"0":{"attr_name":"标准包装","attr_value":"1"},
"2":{"attr_name":"包装","attr_value":"剪切带(CT)11"}
}
}
```
### 批次 redis Self_goods_batch 相关数据
```json
"[{"traceno":"00010486","pcs":"2950.0000","dc":"","goods_id":"10365","add_time":1578363429}]"
2) "[{"traceno":"00010489","pcs":"4500.0000","dc":"","goods_id":"10333","add_time":1578363429}]"
3) "[{"traceno":"00013255","pcs":"1350.0000","dc":0,"goods_id":"10643","add_time":1606109821,"dc_time":0}]"
4) "[{"traceno":"00011909","pcs":"2470.0000","dc":"","goods_id":"10288","add_time":1578363429},{"traceno":"00011910","pcs":"2500.0000","dc":"","goods_id":"10288","add_time":1578363429}]"
5) "[{"traceno":"00071835","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1529244995},{"traceno":"00071836","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1529244995},{"traceno":"00071837","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1529244995},{"traceno":"00071838","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1529244995},{"traceno":"00071839","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1529244995}]"
6) "[{"traceno":"00008706","pcs":"4000.0000","dc":"","goods_id":"11050","add_time":1578363429},{"traceno":"00008978","pcs":"4000.0000","dc":"","goods_id":"11050","add_time":1578363429}]"
7) "[{"traceno":"00071835","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1578240000},{"traceno":"00071836","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1578240000},{"traceno":"00071837","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1578240000},{"traceno":"00071838","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1578240000},{"traceno":"00071839","pcs":"100.0000","dc":"2002","goods_id":"95877","add_time":1578559395,"dc_time":1578240000}]"
8) "[{"traceno":"00008700","pcs":"9820.0000","dc":0,"goods_id":"11030","add_time":1579249375,"dc_time":0},{"traceno":"00009108","pcs":"10000.0000","dc":0,"goods_id":"11030","add_time":1579249375,"dc_time":0},{"traceno":"00009109","pcs":"10000.0000","dc":0,"goods_id":"11030","add_time":1579249375,"dc_time":0},{"traceno":"00071756","pcs":"5.0000","dc":0,"goods_id":"11030","add_time":1579249375,"dc_time":0},{"traceno":"00071758","pcs":"25.0000","dc":0,"goods_id":"11030","add_time":1579249375,"dc_time":0},{"traceno":"00071764","pcs":"10.0000","dc":0,"goods_id":"11030","add_time":1579249375,"dc_time":0},{"traceno":"00071766","pcs":"1.0000","dc":0,"goods_id":"11030","add_time":1579249375,"dc_time":0},{"traceno":"00071874","pcs":"400.0000","dc":"1809","goods_id":"11030","add_time":1579249375,"dc_time":1518969600},{"traceno":"00071876","pcs":"3600.0000","dc":"1810","goods_id":"11030","add_time":1579249375,"dc_time":1519574400},{"traceno":"00071871","pcs":"4000.0000","dc":"1802","goods_id":"11030","add_time":1579249375,"dc_time":1514736000},{"traceno":"00071872","pcs":"4000.0000","dc":"1802","goods_id":"11030","add_time":1579249375,"dc_time":1514736000},{"traceno":"00071873","pcs":"4000.0000","dc":"1802","goods_id":"11030","add_time":1579249375,"dc_time":1514736000},{"traceno":"00071875","pcs":"4000.0000","dc":"1830","goods_id":"11030","add_time":1579249375,"dc_time":1531670400}]"
9) "[{"traceno":"00010485","pcs":"3000.0000","dc":"","goods_id":"10345","add_time":1578363429}]"
10) "[{"traceno":"00008700","pcs":"9820.0000","dc":0,"goods_id":"11030","add_time":1579164431,"dc_time":0},{"traceno":"00009108","pcs":"10000.0000","dc":0,"goods_id":"11030","add_time":1579164431,"dc_time":0},{"traceno":"00009109","pcs":"10000.0000","dc":0,"goods_id":"11030","add_time":1579164431,"dc_time":0},{"traceno":"00071756","pcs":"5.0000","dc":0,"goods_id":"11030","add_time":1579164431,"dc_time":0},{"traceno":"00071758","pcs":"25.0000","dc":0,"goods_id":"11030","add_time":1579164431,"dc_time":0},{"traceno":"00071764","pcs":"10.0000","dc":0,"goods_id":"11030","add_time":1579164431,"dc_time":0},{"traceno":"00071766","pcs":"1.0000","dc":0,"goods_id":"11030","add_time":1579164431,"dc_time":0},{"traceno":"00071874","pcs":"400.0000","dc":"1809","goods_id":"11030","add_time":1579164431,"dc_time":1518969600},{"traceno":"00071876","pcs":"3600.0000","dc":"1810","goods_id":"11030","add_time":1579164431,"dc_time":1519574400},{"traceno":"00071871","pcs":"4000.0000","dc":"1802","goods_id":"11030","add_time":1579164431,"dc_time":1514736000},{"traceno":"00071872","pcs":"4000.0000","dc":"1802","goods_id":"11030","add_time":1579164431,"dc_time":1514736000},{"traceno":"00071873","pcs":"4000.0000","dc":"1802","goods_id":"11030","add_time":1579164431,"dc_time":1514736000},{"traceno":"00071875","pcs":"4000.0000","dc":"1830","goods_id":"11030","add_time":1579164431,"dc_time":1531670400}]"
11) "[{"traceno":"00008673","pcs":"10000.0000","dc":"","goods_id":"11033","add_time":1578363429},{"traceno":"00009122","pcs":"10000.0000","dc":"","goods_id":"11033","add_time":1578363429},{"traceno":"00024542","pcs":"10000.0000","dc":"","goods_id":"11033","add_time":1578363429},{"traceno":"00024543","pcs":"10000.0000","dc":"","goods_id":"11033","add_time":1578363429}]"
12) "[{"traceno":"00008714","pcs":"4000.0000","dc":"","goods_id":"11051","add_time":1578363429},{"traceno":"00008977","pcs":"4000.0000","dc":"","goods_id":"11051","add_time":1578363429}]
```
package main
import (
"fmt"
)
type MyError struct {
errCode uint8
}
func (e *MyError) Error() string {
switch e.errCode {
case 1:
return "file not found"
case 2:
return "time out"
case 3:
return "permission denied"
default:
return "unknown error"
}
}
// 相当于使用interface进行封箱操作
func GetError1() error {
var err *MyError = nil
return err
}
// 没有使用interface进行封箱操作
func GetError2() *MyError {
var err *MyError = nil
return err
}
func main() {
ret1 := GetError1() //封箱,所以不为nil
fmt.Println(ret1 == nil) //false
e, _ := ret1.(*MyError) //显式拆箱,所以为nil
fmt.Println(e == nil) //true
ret2 := GetError2() // 不用转换,所以为nil
fmt.Println(ret2 == nil) //true
}
package main
import "golang_open_platform/open"
func main() {
open.NewFlow().GetUsedMinNum("a","c")
}
\ No newline at end of file
package main
func main() {
}
package main
import (
"fmt"
)
func main() {
fmt.Println("sdfsdfsdf")
/*tokenStr:="fjksdjfk"
name:="fddsf"
token.NewToken(tokenStr,name).Check()*/
}
package main
import (
"fmt"
"golang_open_platform/token"
"reflect"
)
func main() {
err:=ddd()
if(err==nil){
fmt.Println("等于空")
}else{
fmt.Println("不等于空")
}
CheckError(err)
return
tokenStr:="AbopJDAbopdu800820mcspcq=="
name:="getSkuListById"
s:= open.NewToken(tokenStr,name).Check()
/*if(s==nil){
fmt.Println("等于空")
}else{
fmt.Println("不等于空")
}*/
//return
/*fmt.Println(s)
fmt.Println(s)
fmt.Println(nil)*/
CheckError(nil)
CheckError(s)
/*if(s!=nil){
fmt.Println(s.Code,s.Error())
}*/
}
func ddd()error{
return nil
//return e.NewApiError("djfklsdjfk")
}
/**
检测异常,并抛出异常
*/
func CheckError(err error) {
fmt.Println(reflect.TypeOf(err))
if(err==nil){
fmt.Println("等于空")
}else{
fmt.Println("不等于空")
}
/*if(err!=nil){
fmt.Println("不等于空")
}else{
fmt.Println("等于空")
}*/
/*if err != nil {
fmt.Print(err.Error());
if apiError,ok:=err.(*ApiError);ok{
panic(apiError)
}else{
panic(NewApiError(err.Error()))
}
}*/
}
package main
import (
"fmt"
"time"
)
func main() {
s:=time.Hour.Seconds()*24
a:=time.Minute.Seconds()
fmt.Println(s)
fmt.Println(a)
}
\ No newline at end of file
package main
import (
"fmt"
"github.com/gogf/gf/util/gconv"
"golang_open_platform/pkg/common"
"sync"
"time"
)
type SoucreData []string
var num int
func main() {
c:=make(chan SoucreData)
page:=1;
go func() {
defer close(c)
for{
zySkuEntityS,err:=GetSoucreData(page)
if(len(zySkuEntityS)<=0 || err!=nil){
if(err!=nil){
common.PrintDebugString(err.Error())
}
break
}
c<-zySkuEntityS
page++
}
}()
//消费chan
w:=sync.WaitGroup{}
limits:=make(chan struct{},1)
for soucreData:=range c{
w.Add(1)
limits<- struct{}{}
go func() {
defer func() {
defer w.Done()
<-limits
}()
exec(soucreData)
}()
}
w.Wait()
fmt.Println(num)
}
func GetSoucreData(page int) (soucreData SoucreData,err error) {
if(page >10){
return soucreData,nil
}
pre:="吱吱吱"
for i:=0;i<=100;i++{
randNum:=common.Rand(1,1000000)
soucreData=append(soucreData,pre+gconv.String(randNum))
}
return soucreData,err
}
func exec(s SoucreData) {
time.Sleep(time.Second*5)
for _,v:=range s {
num+=1
common.PrintDebugString(v)
}
}
package main
import "github.com/syyongx/php2go"
func main() {
php2go.Ceil()
}
package main
import "fmt"
//换行符
func main() {
s:="abc\n"
s=s+"erg\n"
fmt.Print(s)
}
package main
import (
"encoding/json"
"fmt"
)
const TOKENJD ="AbopJDAbopdu800820mcspcq=="
const TOKENBAIDU ="AbopJDAbopdu800820mcspcq=="
const INTERFACEC1 = "GetSkuListByClass"
const INTERFACEID2 = "GetSkuListFull"
const INTERFACEID3 = "GetSkuListPrice"
var(
jdToken=TOKENJD
baiduToken=TOKENBAIDU
GetSkuListByClass ="GetSkuListByClass"
GetSkuListFull ="GetSkuListFull"
GetSkuListPrice ="GetSkuListPrice"
//redis 表key
openWhiteList ="openWhiteList"//白名单
openTokenBusinessList="openTokenBusinessList"//商家列表
openBusinessInterface="openBusinessInterface"//商家接口配置
/**
计数key
比如 flowUse_AbopJDAbopdu800820mcspcq==_getSkuListById_min
组成说明:flowUse_{token}_{interfaceName}_min 如果是天 后面就是day
*/
/**
锁key
比如 flow_lock_flowUse_AbopJDAbopdu800820mcspcq==_getSkuListById_min
flow_lock_计数key
*/
)
func main() {
generate()
}
func generate() {
whiteIp()
businessList()
businessInterfaceList()
}
//白名单配置
func whiteIp() {
redisKey:=openWhiteList
whiteList:=[]string{
"127.0.0.1",
"127.0.0.2",
"192.168.2.72",
"::1",
}
fmt.Println("白名单配置:"+redisKey)
redisStringSet(redisKey,whiteList)
}
//商家列表
func businessList() {
redisKey:=openTokenBusinessList
businessList:=[]string{
jdToken,//京东
baiduToken,//百度
}
fmt.Println("商家列表:key:"+redisKey)
redisStringSet(redisKey,businessList)
}
//商家接口配置
func businessInterfaceList() {
redisKey:=openBusinessInterface
config:=map[string]interface{}{
jdToken+"_"+GetSkuListByClass: map[string]interface{}{
"dayMaxNum":10000000,
"minMaxNum":50,
"totalMaxNum":10000,
},
jdToken+"_"+GetSkuListFull: map[string]interface{}{
"dayMaxNum":10000000,
"minMaxNum":50,
"totalMaxNum":10000,
},
jdToken+"_"+GetSkuListPrice: map[string]interface{}{
"dayMaxNum":10000000,
"minMaxNum":50,
"totalMaxNum":10000,
},
/*baiduToken+"_"+getSkuListByClass: map[string]interface{}{
"dayMaxNum":100,
"minMaxNum":500,
"totalMaxNum":10000,
},*/
}
fmt.Println("商家接口配置:key:"+openBusinessInterface)
redisHashSet(redisKey,config)
}
func redisHashSet(key string,values map[string]interface{}) {
for hashk,valueOne:=range values{
b,_:=json.Marshal(valueOne)
b,_=json.Marshal(string(b))
hsetStr:="Hset "+key+" "+string(hashk)+" "+string(b)
fmt.Println(hsetStr)
//fmt.Println("对应计时的min key\nflowUse_"+hashk+"_min")
//fmt.Println("对应计时的day key\nflowUse_"+hashk+"_day")
}
}
func redisStringSet(key string,sliceString []string) {
b,_:=json.Marshal(sliceString)
b,_=json.Marshal(string(b))
setStr:="set "+key+" "+string(b)
fmt.Println(setStr)
}
\ No newline at end of file
package main
import "fmt"
func main() {
s:=[]string{"goods_id","spu_id","brand_id","brand_name","goods_name", "stock","moq","mpq","class_id1","class_name","attrs","ladder_price",}
}
\ No newline at end of file
package main
import "fmt"
func main() {
s:=make([]string,0)
s[0]="fjksdf"
fmt.Println(s)
}
package main
import (
"fmt"
"strings"
)
func main() {
a:="a,b,c,d dfsdf"
sliceA:=strings.Split(a,",")
fmt.Print(sliceA)
}
\ No newline at end of file
```json
```json
{
"errcode": 0,
"errmsg": "success",
"data": {
"1160558403263797403": {
"spu_id": "2160558403275219703",
"old_goods_id": 15000086888,
"update_time": 1605584032,
"cp_time": 0,
"goods_status": 1,
"goods_type": 1,
"supplier_id": 7,
"encoded": "",
"batch_sn": "",
"moq": 1,
"mpq": 1,
"stock": 123,
"hk_delivery_time": "2-3个工作日",
"cn_delivery_time": "1-3个工作日",
"ladder_price": [],
"goods_images": "//media.digikey.com/Photos/Analog%20Devices%20Photos/ADXRS652BBGZ_sml.jpg",
"canal": "",
"is_expire": 1,
"packing": "",
"goods_id": "1160558403263797403",
"goods_name": "ADXRS646TBGZ-EP",
"brand_name": "ADI",
"supplier_name": "digikey",
"attrs": [
{
"attr_name": "产品族",
"attr_value": "运动传感器 - 陀螺仪"
},
{
"attr_name": "包装",
"attr_value": "盒"
},
{
"attr_name": "封装/外壳",
"attr_value": "32-BFCBGA"
},
{
"attr_name": "工作温度",
"attr_value": "-55°C ~ 105°C(TA)"
},
{
"attr_name": "带宽",
"attr_value": "1kHz"
},
{
"attr_name": "标准包装",
"attr_value": "1"
},
{
"attr_name": "灵敏度 (LSB/(°/s))",
"attr_value": "-"
},
{
"attr_name": "灵敏度 (mV/°/s)",
"attr_value": "9"
},
{
"attr_name": "特性",
"attr_value": "温度传感器"
},
{
"attr_name": "电压 - 供电",
"attr_value": "5.75V ~ 6.25V"
},
{
"attr_name": "电流 - 供电",
"attr_value": "4mA"
},
{
"attr_name": "类别",
"attr_value": "传感器,变送器"
},
{
"attr_name": "类型",
"attr_value": "模拟"
},
{
"attr_name": "系列",
"attr_value": "-"
},
{
"attr_name": "范围 °/s",
"attr_value": "±250"
},
{
"attr_name": "轴",
"attr_value": "Z(横滚)"
},
{
"attr_name": "输出类型",
"attr_value": "模拟电压"
}
],
"scm_brand": {
"erp_brand_id": "id48qqh4XsDgUwEAAH/sXRVhlDE=",
"erp_brand_name": "ADI",
"scm_brand_id": "4041"
},
"allow_coupon": 2,
"brand_id": 2,
"supp_extend_fee": {
"cn": {
"max": "10000",
"price": "20"
},
"hk": {
"max": 69000,
"price": 138
}
},
"is_buy": 0,
"class_id1": 13287,
"class_id2": 13347,
"class_id3": 0,
"spu_name": "ADXRS646TBGZ-EP",
"status": 1,
"images_l": "//media.digikey.com/Photos/Analog%20Devices%20Photos/ADXRS652BBGZ_sml.jpg",
"encap": "",
"pdf": "http://www.analog.com/media/en/technical-documentation/data-sheets/ADXRS646-EP.pdf",
"spu_brief": "IC GYROSCOPE YAW RATE 32CBGA",
"class_name": "其他",
"erp_tax": false,
"goods_sn": "",
"goods_details": "",
"ac_type": 0,
"has_gift_activity": 0,
"gift_activity": {
"can_admin_order": null
},
"activity_info": {}
},
"1160558406333579646": {
"spu_id": "2160558406351231705",
"old_goods_id": 15000086983,
"update_time": 1605584063,
"cp_time": 0,
"goods_status": 1,
"goods_type": 1,
"supplier_id": 7,
"encoded": "",
"batch_sn": "",
"moq": 1,
"mpq": 1,
"stock": 1,
"hk_delivery_time": "2-3个工作日",
"cn_delivery_time": "1-3个工作日",
"ladder_price": [],
"goods_images": "",
"canal": "",
"is_expire": 1,
"packing": "",
"goods_id": "1160558406333579646",
"goods_name": "ADXRS300WABGZA-RL",
"brand_name": "ADI",
"supplier_name": "digikey",
"attrs": [
{
"attr_name": "产品族",
"attr_value": "运动传感器 - 陀螺仪"
},
{
"attr_name": "包装",
"attr_value": "标准卷带"
},
{
"attr_name": "封装/外壳",
"attr_value": "-"
},
{
"attr_name": "标准包装",
"attr_value": "1"
},
{
"attr_name": "类别",
"attr_value": "传感器,变送器"
},
{
"attr_name": "系列",
"attr_value": "-"
}
],
"scm_brand": {
"erp_brand_id": "id48qqh4XsDgUwEAAH/sXRVhlDE=",
"erp_brand_name": "ADI",
"scm_brand_id": "4041"
},
"allow_coupon": 2,
"brand_id": 2,
"supp_extend_fee": {
"cn": {
"max": "10000",
"price": "20"
},
"hk": {
"max": 69000,
"price": 138
}
},
"is_buy": 0,
"class_id1": 13287,
"class_id2": 13347,
"class_id3": 0,
"spu_name": "ADXRS300WABGZA-RL",
"status": 1,
"images_l": "",
"encap": "",
"pdf": "",
"spu_brief": "IC GYROSCOPE 32CSPBGA",
"class_name": "其他",
"erp_tax": false,
"goods_sn": "",
"goods_details": "",
"ac_type": 0,
"has_gift_activity": 0,
"gift_activity": {
"can_admin_order": null
},
"activity_info": {}
}
}
}
```
\ No newline at end of file
### szlc mongodb
### szlc mongodb
```json
{
"stock": [
NumberInt(5),
NumberInt(7380)
],
"tiered": [
[
NumberInt(5),
0.544911
],
[
NumberInt(50),
0.406958
],
[
NumberInt(150),
0.38162
],
[
NumberInt(500),
0.356282
],
[
NumberInt(2500),
0.34502
],
[
NumberInt(5000),
0.339456
]
],
"increment": NumberInt(5),
"pn": "szlc",
"is_api": NumberInt(0),
"encap_sulation_number": NumberInt(4000),
"goods_name": "MCZ1210AH900TA0G",
"sale_count": NumberInt(10280),
"sale_unit": "个",
"url": "http://item.szlcsc.com/147580.html",
"time": NumberInt(1568786720),
"is_error": NumberInt(0),
"goods_id": NumberLong(31000000000),
"goods_sn": "C136265",
"brand_name": "TDK",
"brand_id": NumberInt(1)
}
```
redis Self_szlc_class_mapping
```json
"{"class_id": 10329, "parent_id": 10229, "financial_classify_id": "0"}"
```
\ No newline at end of file
***redis键值: Self_SelfGoods
{
"other_attrs": "",
"goods_id": 203065,
"goods_type": 0,
"supplier_id": 10000,
"brand_id": 4492,
"class_id1": 10152,
"class_id2": 10164,
"goods_name": "RR0603(0201)L1203FTD",
"status": 1,
"encoded": "",
"encap": "0201(0603)",
"packing": 5,
"goods_unit": 1,
"goods_images": "",
"pdf": "",
"goods_brief": "",
"moq": 50,
"mpq": 15000,
"ladder_price": [
{
"purchases": 50,
"price_cn": 0.0044
},
{
"purchases": 1000,
"price_cn": 0.0044
},
{
"purchases": 15000,
"price_cn": 0.0044
}
],
"update_time": 1592218150,
"sku_name": "RR0603(0201)L1203FTD u7f16u5e26 PTC u70edu654fu7535u963b ",
"mpl": 1,
"stock": 0,
"attrs": "",
"cost": "0.000000",
"new_cost": "0.000000",
"supplier_stock": 473349,
"self_supplier_type": 4
}
#############最终结果##########
Array
(
[33072] => Array
(
[other_attrs] => Array
(
[length] =>
[gross_wegiht] => 0.000582
)
[goods_id] => 33072
[goods_type] => 0
[supplier_id] => 10000
[brand_id] => 703
[class_id1] => 161
[class_id2] => 169
[goods_name] => 3362P-1-101LF
[status] => 1
[encoded] => 10141
[encap] => 3362P
[packing] => 8
[goods_unit] => 1
[goods_images] => http://img.ichunt.com/images/goods/bb/a6/bba6b9cb12bf5f581cd4525a9d5995de.png
[pdf] => http://img.ichunt.com/doc/pdf/cms/201809/13/d110c1966263019b39775c419f57b4a2.pdf
[goods_brief] =>
[moq] => 1
[mpq] => 50
[ladder_price] => Array
(
[0] => Array
(
[purchases] => 1
[price_cn] => 0.95
[price_ac] => 0.5944
)
[1] => Array
(
[purchases] => 10
[price_cn] => 0.874
[price_ac] => 0.5944
)
[2] => Array
(
[purchases] => 20
[price_cn] => 0.836
[price_ac] => 0.5944
)
[3] => Array
(
[purchases] => 50
[price_cn] => 0.798
[price_ac] => 0.5944
)
[4] => Array
(
[purchases] => 250
[price_cn] => 0.76
[price_ac] => 0.5944
)
)
[update_time] => 1596436153
[sku_name] => 3362P-1-101LF 精密可调电阻 100Ω(101) 3362P
[mpl] => 1
[stock] => 67
[attrs] => Array
(
[0] => Array
(
[attr_name] => 阻值(欧姆)
[attr_value] => 100Ω(101)
)
[1] => Array
(
[attr_name] => 精度
[attr_value] =>
)
[2] => Array
(
[attr_name] => 温度系数
[attr_value] =>
)
)
[cost] => 0.699248
[new_cost] => 0.602800
[supplier_stock] => 0
[self_supplier_type] => 1
[cn_delivery_time] => 3-7工作日
[brand_name] => BOURNS(伯恩斯)
[supplier_name] => 猎芯自营
[goods_unit_name] => 个
[packing_name] => 管
[mpq_unit_name] => 管
[scm_brand_name] =>
[actual_stock] => 100
[ac_type] => 6
[allow_coupon] => 1
[allow_presale] => 1
[saler_atio] =>
[szlc_price] =>
[class_id1_name] => 电阻
[class_id2_name] => 精密可调电阻
[is_buy] => 1
)
)
package gin_
type ServiceBuilder struct {
service interface{}
endPoint Endpoint
requestFunc EncodeRequestFunc
responseFunc DecodeResponseFunc
methods string
middlewares []Middleware
}
func NewBuilder() *ServiceBuilder {
return &ServiceBuilder{middlewares:make([]Middleware,0)}
}
func(this *ServiceBuilder) WithService(obj interface{}) *ServiceBuilder {
this.service=obj
return this
}
func(this *ServiceBuilder) WithMiddleware(obj Middleware) *ServiceBuilder {
this.middlewares=append(this.middlewares,obj)
return this
}
func(this *ServiceBuilder) WithEndpoint(obj Endpoint) *ServiceBuilder {
this.endPoint=obj
return this
}
func(this *ServiceBuilder) WithRequest(obj EncodeRequestFunc) *ServiceBuilder {
this.requestFunc=obj
return this
}
func(this *ServiceBuilder) WithResponse(obj DecodeResponseFunc) *ServiceBuilder {
this.responseFunc=obj
return this
}
func(this *ServiceBuilder) WithMethods(methods string) *ServiceBuilder {
this.methods=methods
return this
}
func(this *ServiceBuilder) Build(path string,methods string) *ServiceBuilder{
finalEndpoint:=this.endPoint
for _,m:=range this.middlewares{ //遍历中间件
finalEndpoint=m(finalEndpoint)
}
handler:=RegisterHandler(finalEndpoint,this.requestFunc,this.responseFunc)
SetHandler(methods,path,handler)
return this
}
package gin_
import (
"fmt"
"github.com/gin-gonic/gin"
)
type Middleware func(next Endpoint) Endpoint
//业务最终函数原型
type Endpoint func(ctx *gin.Context,request interface{}) (response interface{}, err error)
//怎么取参数
type EncodeRequestFunc func(*gin.Context) (interface{}, error)
//怎么处理业务结果
type DecodeResponseFunc func(*gin.Context, interface{}) error
func RegisterHandler(endpoint Endpoint,encodeFunc EncodeRequestFunc, decodeFunc DecodeResponseFunc) func(context *gin.Context){
return func(ctx *gin.Context) {
defer func() {
if r:=recover();r!=nil{
ctx.JSON(500,gin.H{"error":fmt.Sprintf("fatal error:%s",r)})
return
}
}()
//参数:=怎么取参数(context)
//业务结果,error:=业务最终函数(context,参数)
//
//
//怎么处理业务结果(业务结果)
req,err:=encodeFunc(ctx) //获取参数
if err!=nil{
ctx.JSON(400,gin.H{"error":"param error:"+err.Error()})
return
}
rsp,err:=endpoint(ctx,req) //执行业务过程
if err!=nil{
ctx.JSON(400,gin.H{"error":"response error:"+err.Error()})
return
}
err=decodeFunc(ctx,rsp) //处理 业务执行 结果
if err!=nil{
ctx.JSON(500,gin.H{"error":"server error:"+err.Error()})
return
}
}
}
package gin_
import (
"github.com/gin-gonic/gin"
)
var HandlerMap map[string]map[string]gin.HandlerFunc
func init() {
HandlerMap=make( map[string]map[string]gin.HandlerFunc)
}
func SetHandler(methods string,path string,handler gin.HandlerFunc) {
if _,ok:=HandlerMap[path];ok{
HandlerMap[path][methods]=handler
}else{
HandlerMap[path]=make(map[string]gin.HandlerFunc)
}
HandlerMap[path][methods]=handler
}
func BootStrap(router *gin.Engine) {
for path,hmap:=range HandlerMap{
for method,handler:=range hmap{
router.Handle(method,path,handler)
}
}
}
\ No newline at end of file
[program:golang_open_platform_60006]
command=/data/golang/golang_open_platform/cmd/http/http -config=/data/golang/golang_open_platform_60006/conf/dev
numprocs=1
process_name=%(program_name)s_%(process_num)02d
autostart=true
autorestart=true
startretries=3
user=root
redirect_stderr=true
stderr_logfile=/etc/supervisord.d/logs/golang_open_platform_error.log
stdout_logfile=/etc/supervisord.d/logs/golang_open_platform_access.log
stdout_logfile_maxbytes=2MB
package model
import (
"github.com/gin-gonic/gin"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/e"
validator "golang_open_platform/pkg/validate"
)
//过滤
func SkuFilter(remoteData RemoteSkuData,filedStr []string) (data RemoteSkuData) {
remoteData=filterData(remoteData,filedStr)
return remoteData
}
/**
过滤
@param remoteData 原始商详数据
@param filterField 需要过滤的字段
@return data 返回新的数据
*/
func filterData(remoteData RemoteSkuData,filterField []string)(data RemoteSkuData){
for skuId,skuInfo:=range remoteData{
newItem:=make(map[string]interface{})
/*if _,ok:=skuInfo.(bool);ok{
delete(remoteData,skuId)
continue
}*/
if info,ok:=skuInfo.(map[string]interface{});ok{
for _,field:=range filterField{
if _,ok:=info[field];ok{
newItem[field]=info[field]
if(field=="ladder_price"){
newItem[field]=filterLadder(info[field])
}
}
}
remoteData[skuId]=newItem
}else{//有问题的格式
common.PrintStdout().Printf("skuId:%s,是个有问题的格式",string(skuId))
delete(remoteData,skuId)
continue
}
}
return remoteData
}
func filterLadder(ladder interface{}) interface{}{
var newLadderS = []map[string]interface{}{}
fileds:=[]string{"purchases","price_us","price_cn"}
if ladderSlice,ok:=ladder.([]interface{});ok{
if(len(ladderSlice)<=0){
return ladderSlice
}
for _,ladderOne:=range ladderSlice{
newLadderOne:=make(map[string]interface{})
if ladderMap,ok:=ladderOne.(map[string]interface{});ok{
for _,filed:=range fileds {
if _,ok:=ladderMap[filed];ok{
newLadderOne[filed]=ladderMap[filed]
}
}
newLadderS=append(newLadderS,newLadderOne)
}else{
common.PrintStdout().Printf("阶梯价数组其中的一个 不能转换成map")
}
}
}
return newLadderS
}
//end
//验证并绑定
func ValidateBind(ctx *gin.Context,req interface{}) (err error){
err1:=validator.NewValidator().GinShouldBind(ctx,req)
if(err1!=nil){
return e.NewApiError(err1.ErrMsg,err1.Code)
}
return
}
\ No newline at end of file
package model
//根据class获取sku列表 请求参数
type QuerySkuCreq struct {
Page int `json:"page" form:"page" binding:"required" requiredMsg:"page为必填" requiredCode:"80001"`
ClassId int `json:"class_id" form:"class_id" binding:"required" requiredMsg:"class_id为必填" requiredCode:"80001"`
}
//=======================================end================================================
//根据class获取sku列表 返回参数
type QuerySkuCrsp struct {
Count int `json:"count"`
SkuData map[string]interface{} `json:"sku_data"`
Limit int `json:"limit"`
Page int `json:"page"`
}
func (this *QuerySkuCrsp)GetClassFilter() []string{
field:=[]string{"spu_id","attrs","supplier_name","goods_images","hk_delivery_time"}
return field
}
//============end=============
//根据goodsId获取sku列表 请求参数
type QuerySkuReq struct {
GoodsIds string `json:"goods_id" form:"goods_id" binding:"required" requiredMsg:"goods_id必填" requiredCode:"80001"`
}
//=======================================end================================================
//根据goodsId获取sku列表 返回参数
type QuerySkuRsp struct {
SkuData map[string]interface{} `json:"sku_data"`
}
//============end=============
//商详接口返回的数据 data字段
type RemoteSkuData map[string]interface{}
package model
type MongoSkuOpen struct {
ClassId int `bson:"class_id2"`
Page int `bson:"page"`
Count int `bson:"count"`
SkuIds string `bson:"sku_ids"`
}
package open
const (
//开放平台错误码
BUSINESSERR1 = 10007505 //读取商家列表 redis错误
BUSINESSERR2 = 10007404 //商家不存在 token不存在
BUSINESSERR3 = 10007406 //平台没有配置商家列表
BUSINESSCONFIG1 = 10007407 //读取商家与接口关系 redis错误(openBusinessInterface)
BUSINESSCONFIG2 = 10007408 //平台没有配置 商家与接口关系 (redis hash openBusinessInterface 空)
BUSINESSCONFIG3 = 10007409 //商家没有此接口的权限
WHILTREDISEERR1 = 10008505//白名单读取redis错误
WHILTREDISEERR2 = 10008404//ip不在白名单里面
WHILTREDISEERR3 = 10008406//没有设置白名单
FLOWERR1 = 10009505//流量控制操作 redis错误
FLOWERR3 = 10009406//触发分钟级流量限制
FLOWERR4 = 10009407//触发天级流量限制
//获取sku错误码
PARAM1 = 80001 //参数问题(缺失or数据格式不对)
OTHERERROR = 80500 //其他错误,比如读取redis出错
REMOTESKUINFO = 80501 //调用远程商详接口报错
)
package open
import (
"github.com/tidwall/gjson"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/e"
"strconv"
)
//商家 比如京东
type business struct {
dao *Dao
token string
interfaceConfig *interfaceConfigOne
}
func NewBusiness(token string) *business {
return &business{token:token,dao:&Dao{}}
}
type interfaceConfigOne struct {
dayMaxNum int64 `json:"dayMaxNum"`
minMaxNum int64 `json:"minMaxNum"`
totalMaxNum int64 `json:"totalMaxNum"`
}
//获取商家与接口配置
func (this *business) GetInterfaceConfig(interfaceName string) (configOne *interfaceConfigOne,err error ){
if(this.interfaceConfig!=nil){
return this.interfaceConfig,nil
} else{
str,err:=this.dao.GetbusinessInterface(this.token,interfaceName)
if(err!=nil){
return configOne,nil
}
if(str==""){
common.PrintStdout().Printf(strconv.Itoa(BUSINESSCONFIG2)+":平台没有设置此商家的接口配置")
return nil,e.NewApiError("business err",BUSINESSCONFIG2)
}
//config:=&interfaceConfigOne{}
//json.Unmarshal([]byte(str),config)//防止有人插入redis的时候数据格式不对,就不用这个方法了
this.interfaceConfig=&interfaceConfigOne{}
this.interfaceConfig.dayMaxNum=gjson.Parse(str).Get("dayMaxNum").Int()
this.interfaceConfig.minMaxNum=gjson.Parse(str).Get("minMaxNum").Int()
this.interfaceConfig.totalMaxNum=gjson.Parse(str).Get("totalMaxNum").Int()
return this.interfaceConfig,nil
}
return
}
func UsedMinNum() {
}
func UsedDayNum() {
}
package open
import (
"github.com/gomodule/redigo/redis"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/e"
"golang_open_platform/pkg/gredis"
"strconv"
)
//此struct ,包含简单读 数据的一些操作
type Dao struct {
}
//获取白名单列表 openWhiteList
func (this *Dao) getOpenWhiteList()(whitestr string,err error){
redisReadConn := gredis.Conn("search_r")
defer redisReadConn.Close()
whitestr,err=redis.String(redisReadConn.Do("Get","openWhiteList"))
if(err!=nil && err!=redis.ErrNil){
common.PrintStdout().Printf(strconv.Itoa(WHILTREDISEERR1)+":读取redis 白名单错误"+err.Error())
return "",e.NewApiError("white err",WHILTREDISEERR1)
}
return
}
//获取商家列表 openTokenBusinessList
func (this *Dao) getBusinessList()(listStr string,err error){
redisReadConn := gredis.Conn("search_r")
defer redisReadConn.Close()
listStr,err=redis.String(redisReadConn.Do("Get","openTokenBusinessList"))
if(err!=nil && err!=redis.ErrNil){
common.PrintStdout().Printf(strconv.Itoa(BUSINESSERR1)+":读取redis token列表错误"+err.Error())
return "",e.NewApiError("business",BUSINESSERR1)
}
return listStr,nil
}
//获取商家接口配置 openBusinessInterface
func (this *Dao) GetbusinessInterface(token string,interfaceName string) (Str string,err error) {
redisReadConn := gredis.Conn("search_r")
defer redisReadConn.Close()
key:=token+"_"+interfaceName
Str,err=redis.String(redisReadConn.Do("Hget","openBusinessInterface",key))
if(err!=nil && err!=redis.ErrNil){
common.PrintStdout().Printf(strconv.Itoa(BUSINESSCONFIG1)+":读取redis 商家接口配置错误"+err.Error())
return "",e.NewApiError("business err",BUSINESSCONFIG1)
}
return Str,nil
}
//获取计数 redis set
func (this *Dao) getRedisFlowNum(key string) (num int,err error){
redisReadConn := gredis.Conn("search_r")
defer redisReadConn.Close()
num,err= redis.Int(redisReadConn.Do("Get", key))
if(err!=nil && err!=redis.ErrNil){
common.PrintStdout().Printf("读取redis "+key+"错误"+err.Error())
return 0,e.NewApiError("service err",FLOWERR1)
}
return num,nil
}
func (this *Dao) setRedisFlowEx(key string,expire interface{}) error{
redisWriteConn := gredis.Conn("search_w")
defer redisWriteConn.Close()
_, err:= redisWriteConn.Do("SET", key, "0", "EX", expire)
if(err!=nil){
common.PrintStdout().Printf("设置redis "+key+"错误")
return e.NewApiError("service err",FLOWERR1)
}
return nil
}
func (this *Dao)incrRedisFlowNum(key string) (num int,err error){
redisWriteConn := gredis.Conn("search_w")
defer redisWriteConn.Close()
num,err= redis.Int(redisWriteConn.Do("INCR", key))
if(err!=nil){
common.PrintStdout().Printf("INCR redis "+key+"错误")
return 0,e.NewApiError("service err",FLOWERR1)
}
return num,err
}
package open
import (
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/gredis"
"time"
)
const (
INCRTYPEMIN = iota //分
INCRTYPEDAY //天
)
type flowmeter struct {
dao *Dao
flowLimitType int //INCRTYPEMIN 分限制 INCRTYPEDAY 天限制
token string //token
interfaceName string //接口名称
}
func NewFlow(token string,interfaceName string) *flowmeter{
return &flowmeter{dao:&Dao{},token:token,interfaceName:interfaceName}
}
//获取redis key 和过期时间
func (this *flowmeter) getFlowKeyAndEx() (key string,expireTime int64){
key="flowUse_"+this.token+"_"+this.interfaceName
switch this.flowLimitType {
case INCRTYPEMIN:
key+="_min"
expireTime=int64(time.Minute.Seconds())
break
case INCRTYPEDAY:
key+="_day"
expireTime=int64(time.Hour.Seconds()*24)
break
default:
key+="_min"
expireTime=int64(time.Minute.Seconds())
break
}
return
}
//设置流量限制类型 INCRTYPEMIN 分限制 INCRTYPEDAY 天限制
func (this *flowmeter) SetFlowKey(flowLimitType int) {
this.flowLimitType=flowLimitType
}
//获取 已使用的流量数
func (this *flowmeter) GetNum()(int,error){
key,expireTime:=this.getFlowKeyAndEx()
this.addOnlyLock(key)//加锁
//获取计数 (读 redis key)
num,err:=this.dao.getRedisFlowNum(key)
//common.PrintStdout().Printf("读取 计数key:%s,num 为: %d",key,num)
if(err!=nil){
this.delOnlyLock(key)//解锁
return 0,err
}
if(num==0){//计数不存在(redis没有此key),就新建一个带有超时的key
err:=this.dao.setRedisFlowEx(key,expireTime)
//common.PrintStdout().Printf("计数key:%s,不存在,创建超时%d 的key",key,expireTime)
if(err!=nil){
this.delOnlyLock(key)//解锁
return 0,err
}
}
this.delOnlyLock(key)
return num,nil
}
//自增
func (this *flowmeter)incr() (err error) {
key,_:=this.getFlowKeyAndEx()
_,err=this.dao.incrRedisFlowNum(key)
if(err!=nil){
return err
}
//common.PrintStdout().Printf("加1后的值是 %d",num)
return nil
}
//如果锁不存在并设置锁
func (this *flowmeter)addOnlyLock(key string){
redisWriteConn := gredis.Conn("search_w")
defer redisWriteConn.Close()
name:="flow_lock_"+key
//common.PrintStdout().Printf("加锁 key:%s",name)
for{
s, err:= redisWriteConn.Do("SET", name, "1", "EX", "2","NX")//锁两秒没主动删就自动关闭(防止某个流程卡死,没执行到删除锁)
if(err!=nil){
common.PrintStdout().Printf("读取redis 锁 key:%d 报错",key)
}
if(s!=nil){
//common.PrintStdout().Printf("加锁完成 key :%s",name)
break
}
}
}
//删除锁
func (this *flowmeter) delOnlyLock(key string){
redisWriteConn := gredis.Conn("search_w")
defer redisWriteConn.Close()
name:="flow_lock_"+key
_, err:= redisWriteConn.Do("DEL", name)
if(err!=nil){
println(err.Error())
}
//common.PrintStdout().Printf("解锁完成")
}
\ No newline at end of file
package open
type OpenIchunt struct {
Validate *openValidate
Business *business //openObj.Business 现在用不到,后续应该能用到
}
func NewOpen(token string) *OpenIchunt {
openObj:=&OpenIchunt{}
openObj.Business=NewBusiness(token)
openObj.Validate=NewopenValidate(openObj.Business)
return openObj
}
package open
import (
"github.com/tidwall/gjson"
)
func InArrayGjson(needle string, haystack []gjson.Result) bool {
for _,ip:=range haystack{
if(ip.String()==needle){
return true
}
}
return false
}
package open
import (
"github.com/gin-gonic/gin"
"github.com/tidwall/gjson"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/e"
"strconv"
)
type openValidate struct {
business *business
dao *Dao
}
func NewopenValidate(business *business) *openValidate{
return &openValidate{business:business,dao:&Dao{}}
}
//白名单验证
func (this *openValidate) whiteValidate(ctx *gin.Context)error {
whiteStr,err:=this.dao.getOpenWhiteList()
if(err!=nil){
return err
}
white:=gjson.Parse(whiteStr)
if (whiteStr == "" || white.IsArray()!=true || len(white.Array())<=0){
common.PrintStdout().Printf(strconv.Itoa(WHILTREDISEERR3)+":redis 白名单没有设置")
return e.NewApiError("white err",WHILTREDISEERR3)
}
if(InArrayGjson(ctx.ClientIP(),white.Array())){
return nil
}
common.PrintStdout().Printf(strconv.Itoa(WHILTREDISEERR2)+":Not on the white list ip:"+ctx.ClientIP())
return e.NewApiError("Not on the white list",WHILTREDISEERR2)
}
/**
验证token是否存在
根据token到 商家表查询,如果存在,token就存在
*/
func (this *openValidate) exitValidate(token string) error{
businessList,err:=this.dao.getBusinessList()
if(err!=nil){
return err
}
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)
}
if(InArrayGjson(token,business.Array())){
return nil
}
common.PrintStdout().Printf(strconv.Itoa(BUSINESSERR2)+":token 无效:"+token)
return e.NewApiError("token invalid",BUSINESSERR2)
}
/**
根据token 和 接口名称,到商家接口配置表查询,如果存在代表是有权限
*/
func (this *openValidate) permissionValidate(interfaceName string) error{
_,err:=this.business.GetInterfaceConfig(interfaceName)
if(err!=nil){
return err
}
return nil
}
//请求次数限制
func (this *openValidate) requestsNumValidate(token string,interfaceName string) error{
interfaceConfig,err:=this.business.GetInterfaceConfig(interfaceName)//商家接口配置,包含一小时最大流量,一天最大流量
flow:=NewFlow(token,interfaceName)//流控对象
//一天最大流量验证
flow.SetFlowKey(INCRTYPEDAY)
num,err:=flow.GetNum()
if(err!=nil){
return err
}
if(num>=int(interfaceConfig.dayMaxNum)){
common.PrintStdout().Printf("一天最大流量为: %d > %d",int(interfaceConfig.dayMaxNum),num)
return e.NewApiError("触发天级流控",FLOWERR4)
}else{
flow.incr()//验证成功,自增1
}
//一分钟最大流量验证
flow.SetFlowKey(INCRTYPEMIN)
num,err=flow.GetNum()//获取分钟使用流量
if(err!=nil){
return err
}
if(num>=int(interfaceConfig.minMaxNum)){
common.PrintStdout().Printf("一分钟最大流量为: %d >%d",int(interfaceConfig.minMaxNum),num)
return e.NewApiError("触发分钟级流控",FLOWERR3)
}else{
flow.incr()//验证成功,自增1
}
return nil
}
//check验证
func (this *openValidate) Check(ctx *gin.Context,interfaceName string) error {
err:=this.whiteValidate(ctx)
if(err!=nil){
return err
}
tokenStr:=this.business.token
//验证token的合法性
err=this.exitValidate(tokenStr)
if(err!=nil){
return err
}
//接口权限验证
err=this.permissionValidate(interfaceName)
if(err!=nil){
return err
}
//流量控制验证
err=this.requestsNumValidate(tokenStr,interfaceName)
if(err!=nil){
return err
}
return nil
}
package common
import (
"github.com/gomodule/redigo/redis"
"github.com/syyongx/php2go"
"github.com/tidwall/gjson"
"golang_open_platform/pkg/config"
"golang_open_platform/pkg/gredis"
"strings"
)
//获取上次查询时间等信息
func GetLastSearchTime(goodsId string) (timeStamp int) {
redisCon := gredis.Conn("search_r")
defer redisCon.Close()
key := config.Get("redis_all.SEARCH_SHOW_SKU_TIME").String()
timeStamp, err := redis.Int(redisCon.Do("HGET", key, goodsId))
if err != nil {
timeStamp = 0
}
return
}
//获取关税信息
func GetCustomsTax(goodsName, brandName string) (tax string) {
if goodsName == "" || brandName == "" {
return
}
redisCon := gredis.Conn("search_r")
defer redisCon.Close()
member := php2go.Md5(strings.ToUpper(goodsName + brandName))
key := config.Get("redis_all.TAX_CUSTOMS_INFO")
res, _ := redis.String(redisCon.Do("HGET", key, member))
taxRateRow := gjson.Get(res, "tax_rate_low").Float()
if res == "" || taxRateRow <= 0 {
return
}
return ToString(taxRateRow) + "%"
}
package common
import (
"encoding/json"
"github.com/gin-gonic/gin"
"github.com/gomodule/redigo/redis"
"github.com/ichunt2019/logger"
log "github.com/sirupsen/logrus"
"golang_open_platform/pkg/config"
"golang_open_platform/pkg/gredis"
"golang_open_platform/pkg/message"
"os"
"strconv"
"strings"
"time"
)
type SearchApiLog struct {
Msg string `json:"msg"`
MsgCode string `json:"msgCode"`
Ts int64 `json:"ts"`
DateStr string `json:"dateStr"`
App string `json:"app"`
ServerIp string `json:"serverIp"`
FileName string `json:"fileName"`
LineNo string `json:"lineNo"`
Method string `json:"method"`
}
func AnalyzeSearchError(ctx *gin.Context,code int, ip, errMsg, file, line, method string) (log SearchApiLog) {
//获取所有参数
request := make(map[string]string)
ctx.MultipartForm()
for name, value := range ctx.Request.Form {
if value[0] != "" {
request[name] = strings.TrimSpace(value[0])
}
}
requestByte, _ := json.Marshal(request)
log.Msg = errMsg + string(requestByte)
codeStr := strconv.Itoa(code)
log.MsgCode = StrPadLeft(codeStr, 6, "0")
log.Ts = time.Now().Unix()
log.DateStr = time.Now().Format("2006-01-02 15:04:05")
log.App = "search_api"
log.ServerIp = ip
log.FileName = file
log.LineNo = line
log.Method = method
//还要判断是否要推送消息
unitSupplierLogCode := config.Cfg.Section("UNIT_SUPPLIER_LOG_CODE").KeysHash()
if unitSupplierLogCode[codeStr] == "" {
if !strings.Contains(strings.ToUpper(errMsg), "SOAP-ERROR") {
//推送到钉钉
result, err := message.DingDingPush(errMsg)
if err != nil || result.Errcode != 0 {
logger.Error("%s", err, result)
}
}
} else {
redisCon := gredis.Conn("search_w")
defer redisCon.Close()
key := config.Get("SEARCH_API_LOG.SEARCH_API_ERROR_PRE").String() + unitSupplierLogCode[codeStr]
_, err := redis.Bool(redisCon.Do("INCR", key))
if err != nil {
logger.Error("%s", err)
}
}
return
}
type LogFormatter struct{}
//格式详情
func (s *LogFormatter) Format(entry *log.Entry) ([]byte, error) {
msg := entry.Message + "\n"
return []byte(msg), nil
}
//写入到特定的文件夹给ELK使用的日志
func WriteSearchErrorLog(searchLog string) {
// 设置日志格式为json格式
log.SetFormatter(new(LogFormatter))
path := "../logs/search/"
//设置output,默认为stderr,可以为任何io.Writer,比如文件*os.File
//生成当天的日志
filePath := path + time.Now().Format("20060102") + ".log"
file, _ := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
log.SetOutput(file)
log.Error(searchLog)
}
package common
import (
"github.com/gin-gonic/gin"
"reflect"
)
/**
@author wangsong
输出工具,输出json 和输出日志(链式调用)
一、输出json
common.NResponse(err.Error()).OutPut(ctx)
1 默认的输出格式
common.NResponse().OutPut(ctx) //输出正确
common.NResponse("错了",100).OutPut(ctx) //输出错误
common.NResponse(err.Error()).SetLogHandel(saveLogHandle).OutPut(ctx)//输出错误 并且记录错误
common.NResponse(err.Error()).SetLogHandel(saveLogHandle)..OpenParamLog().OutPut(ctx)//输出错误 并且记录错误,并且记录请求参数
2.输出 自定义 struct struct 必须要继承 NewOutPut 接口
common.NResponse(&struct).OutPut(ctx)
二、输出日志(如果不是要记录传入进来的参数,不需要用这个)
SetLogHandel 回调函数传错误字符串
common.NResponse(err.Error()).SetLogHandel(saveLogHandle).OutPut(ctx)
*/
//接口
type NewOutPut interface {
GetErrorCode() int
GetErrorMsg() string
}
//输出类
type NewResponse struct {
logTool
outPutData NewOutPut
ctx *gin.Context //gin http
}
//日志类
type logTool struct {
openLog bool //开启记录log
logPath string //文件前缀
openParamLog bool //记录请求参数
logContent string //日志内容,会与ErrorMesg一起记录
loghandleFunc func(string)
//logger.LogInterface
//logConfig map[string]string
//logDrive logger.LogInterface
}
/*//日志类
type logTool struct {
openLog bool //开启记录log
LogfilePre string //文件前缀
openParamLog bool //记录请求参数
logContent string //日志内容,会与ErrorMesg一起记录
loghandleFunc func(logger.LogInterface,string)
}*/
//内部默认输出结构
type defaultOutPutDate struct {
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
Data interface{} `json:"data"`
}
//入口
func NResponse( opt...interface{}) *NewResponse {
newResponse:=&NewResponse{}
newResponse.createoutPutData(opt)
newResponse.logPath="logs"
return newResponse
}
//设置输出对象
func (NR *NewResponse) SetoutPutData(newOutPut NewOutPut) *NewResponse{
NR.outPutData=newOutPut
return NR
}
//设置日志内容
func (NR *NewResponse) SetLogContent(logStr string) *NewResponse{
NR.logContent=logStr
NR.openLog=true
return NR
}
//设置日志路径
/*func (NR *NewResponse) SetLogPath(filePre string) *NewResponse{
NR.logConfig["log_chan_size"]="10"
NR.logConfig["log_path"] = filePre
log ,_:=logger.InitLogger("file",NR.logConfig)
log.Init()
NR.logDrive=log
return NR
}*/
/**
开启日志记录参数
ctx 可以不传,传必须是ctx gin.Context
*/
func (NR *NewResponse) OpenParamLog() *NewResponse{
NR.openLog=true
NR.openParamLog=true
return NR
}
//设置 http ctx
func (NR *NewResponse) SetCtx(ctx *gin.Context) *NewResponse{
NR.ctx=ctx
return NR
}
//开启日志
func (NR *NewResponse) OpenLog() *NewResponse{
NR.openLog=true
return NR
}
func (NR *NewResponse) SetLogHandel(a func(string)) *NewResponse {
NR.openLog=true
NR.loghandleFunc=a
return NR
}
//输出到前端
/**
ctx gin.Context 只处理一个
*/
func (NR *NewResponse) OutPut(ctx *gin.Context) {
NR.SetCtx(ctx)
if(reflect.ValueOf(NR.loghandleFunc).IsZero()!=true){
NR.loghandleFunc(NR.getLogErrorStr())//日志处理
}
if(NR.outPutData==nil){//没有输出对象,就用默认的
NR.outPutData=&defaultOutPutDate{0,"ok",nil}
}
NR.ctx.JSONP(200,NR.outPutData)
}
/**
正确输出
*/
func (NR *NewResponse) SucessOutPut(ctx *gin.Context) {
NR.SetCtx(ctx)
NR.loghandleFunc(NR.getLogErrorStr())//日志处理//日志处理
NR.outPutData=&defaultOutPutDate{0,"ok",nil}
NR.ctx.JSONP(200,NR.outPutData)
}
/**
错误输出
*/
func (NR *NewResponse) ErrorOutPut(ctx *gin.Context,opt...interface{}) {
NR.SetCtx(ctx)
NR.createoutPutData(opt)
NR.loghandleFunc(NR.getLogErrorStr())//日志处理
NR.ctx.JSONP(200,NR.outPutData)
}
//输出日志
func (NR *NewResponse) OutPutLog(ctx ...*gin.Context) {
NR.setjudgeCtx(ctx)
NR.loghandleFunc(NR.getLogErrorStr())//日志处理
}
//获取输出日志的Str
func (NR *NewResponse) getLogErrorStr() string{
if(NR.openLog){
errMsg:=""
formDataStr1:=""
if(NR.outPutData!=nil){
if(NR.outPutData.GetErrorCode()!=0){
errMsg="errMsg:"
errMsg+=NR.outPutData.GetErrorMsg()
}
}
if(NR.openParamLog){
if(reflect.ValueOf(NR.ctx).IsZero()!=true){
formDataStr1="请求过来的参数:"
str:=GetRequestParam(NR.ctx)
formDataStr1+=str
}
}
logstr:=errMsg+formDataStr1+NR.logContent
return logstr
}
return ""
}
func (L *defaultOutPutDate)GetErrorMsg() string {
return L.ErrMsg
}
func (L *defaultOutPutDate)GetErrorCode() int {
return L.ErrCode
}
/**
创建 输出对象
如果 第一个参数是结构,输出对象就是此结构(就是自定义输出对象)
否则输出对象就是 defaultOutPutDate;第一个参数 是msg 第二个是code ,第三个是data
*/
func (NR *NewResponse )createoutPutData(opt []interface{}) {
if(len(opt)>0){
errMsg:="ok"
errCode:=0
var data interface{}
//第一个参数是结构,输出对象就是此结构
valueR:=reflect.ValueOf(opt[0])
if(valueR.Kind()==reflect.Ptr ){
if(valueR.Elem().Kind()==reflect.Struct){
if newOutPut,ok:=opt[0].(NewOutPut);ok{
NR.outPutData=newOutPut
}else{
panic("If it is a structure, it must inherit the interface newoutput")
}
return
}
}
//否则就是默认的结构 defaultOutPutDate 第一个参数 是msg 第二个是code ,第三个是data
errMsg=opt[0].(string)
errCode=100010
if(len(opt)>=2){
errCode=opt[1].(int)
}
if(len(opt)>=3){
data=opt[2]
}
NR.outPutData=&defaultOutPutDate{errCode,errMsg,data}
}
}
/**
NR.ctx 没有赋值就取opt[0]
如果opt有参数,必须是*gin.Context,否则pannic
*/
func (NR *NewResponse ) setjudgeCtx(opt []*gin.Context) {
if(len(opt)>0){
if(reflect.ValueOf(NR.ctx).IsZero()){
NR.SetCtx(opt[0])
}else{
panic("NewResponse.ctx No value ,Should be*gin.Context")
}
}
}
\ No newline at end of file
package common
import "math"
//分页方法,根据传递过来的页数,每页数,总数,返回分页的内容 7个页数 前 1,2,3,4,5 后 的格式返回,小于5页返回具体页数
func Paginator(page, prepage int, nums int64) map[string]interface{} {
var firstpage int //前一页地址
var lastpage int //后一页地址
//根据nums总数,和prepage每页数量 生成分页总数
totalpages := int(math.Ceil(float64(nums) / float64(prepage))) //page总数
if page > totalpages {
page = totalpages
}
if page <= 0 {
page = 1
}
var pages []int
switch {
case page >= totalpages-5 && totalpages > 5: //最后5页
start := totalpages - 5 + 1
firstpage = page - 1
lastpage = int(math.Min(float64(totalpages), float64(page+1)))
pages = make([]int, 5)
for i, _ := range pages {
pages[i] = start + i
}
case page >= 3 && totalpages > 5:
start := page - 3 + 1
pages = make([]int, 5)
firstpage = page - 3
for i, _ := range pages {
pages[i] = start + i
}
firstpage = page - 1
lastpage = page + 1
default:
pages = make([]int, int(math.Min(5, float64(totalpages))))
for i, _ := range pages {
pages[i] = i + 1
}
firstpage = int(math.Max(float64(1), float64(page-1)))
lastpage = page + 1
//fmt.Println(pages)
}
paginatorMap := make(map[string]interface{})
paginatorMap["pages"] = pages
paginatorMap["totalpages"] = totalpages
paginatorMap["firstpage"] = firstpage
paginatorMap["lastpage"] = lastpage
paginatorMap["currpage"] = page
return paginatorMap
}
\ No newline at end of file
package common
import (
"github.com/gin-gonic/gin"
"strings"
)
type RecommendRequest struct {
GoodsName string `form:"goods_name"`
Attrs string `form:"attrs"`
Encap string `form:"encap"`
Num int `form:"num"`
DeliveryType int `form:"delivery_type"`
Flag int `form:"flag"`
BrandName string `form:"brand_name"`
}
//获取所有请求参数放到字典里面
func GetAllRequestParams(c *gin.Context) (request map[string]string){
request = make(map[string]string)
c.MultipartForm()
for name, value := range c.Request.Form {
if value[0] != "" {
request[name] = strings.TrimSpace(value[0])
}
}
return
}
package common
import (
"encoding/json"
"github.com/gin-gonic/gin"
"runtime"
"strconv"
"strings"
)
type Response struct {
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
Data interface{} `json:"data"`
}
type BomResponse struct {
ErrCode int `json:"error_code"`
ErrMsg string `json:"error_msg"`
Flag int `json:"flag"`
Total int `json:"total"`
Data interface{} `json:"data"`
}
func SuccessResponse(errCode int, errMsg string, data interface{}) Response {
return Response{
ErrCode: errCode,
ErrMsg: errMsg,
Data: data,
}
}
func ErrorResponse(errCode int, errMsg string) Response {
return Response{
ErrCode: errCode,
ErrMsg: errMsg,
Data: []string{},
}
}
//统一输出,里面还要去处理jsonp
func Output(ctx *gin.Context,errCode int, errMsg string, data interface{}) {
if data == nil {
data = []string{}
}
response := Response{
ErrCode: errCode,
ErrMsg: errMsg,
Data: data,
}
//if errCode >= 100 {
// //SearchApiLogger(ctx,errCode, errMsg)
//}
if ctx.DefaultQuery("callback", "") != "" {
ctx.JSONP(200, response)
} else {
referer := ctx.Request.Referer()
referer = strings.TrimRight(referer, "/")
ctx.Header("Access-Control-Allow-Origin", referer)
ctx.Header("Access-Control-Allow-Credentials", "true")
//允许跨站访问的站点域名
//跨域请求头设置
ctx.JSON(200, response)
}
}
//简单的返回数据方法
func ReturnData(ctx *gin.Context,errCode int, errMsg string, data interface{}) {
if data == nil {
data = []string{}
if errCode == 0 {
errCode = 1
}
}
response := Response{
ErrCode: errCode,
ErrMsg: errMsg,
Data: data,
}
ctx.JSON(200, response)
}
//错误的搜索日志记录
func SearchApiLogger(ctx *gin.Context,code int, msg string) {
pc, file, line, _ := runtime.Caller(2)
f := runtime.FuncForPC(pc)
errMsg := "提示信息:" + msg + ",请求url:" + ctx.Request.URL.String()
lineNo := strconv.Itoa(line)
searchLog := AnalyzeSearchError(ctx,code, ctx.ClientIP(), errMsg, file, lineNo, f.Name())
searchLogByte, _ := json.Marshal(searchLog)
WriteSearchErrorLog(string(searchLogByte))
}
package common
import (
"fmt"
"github.com/syyongx/php2go"
"time"
)
/**
构造SPU和SKU的ID
*/
func StructureNumber(SpuType string) (string) {
var Prefix int; //前缀数字1代表 sku,2代表spu
var db int; //数据库0-9
var table int; //表0-9
random6:=php2go.Rand(100000,999999)//随机6位数
time:=time.Now().Unix();//时间戳
if(SpuType=="sku"){
Prefix=1;
db=Rand(0,9)
table=Rand(0,9)
}else{
Prefix=2;
db=0;
table=(Rand(0,9));
}
spuId:=fmt.Sprintf("%d%d%d%d%d",Prefix,time,random6,db,table)
//retugconv.Int(spuIdS)
return spuId
}
/**
解析SPU和SKU
*/
func ResolveSpu(SpuId string) (db string,table string) {
var Prefix string; //前缀数字1代表 sku,2代表spu
var dbCode string; //数据库0-9
var tableCode string; //表0-9
Prefix=SpuId[0:1]
dbCode=SpuId[len(SpuId)-2:len(SpuId)-1]
tableCode=SpuId[len(SpuId)-1:len(SpuId)]
if(Prefix=="1"){
dbCode="sku_"+dbCode
tableCode="lie_sku_"+tableCode
}else if(Prefix=="2"){
dbCode="spu"
tableCode="lie_spu_"+tableCode
}
return dbCode,tableCode
}
\ No newline at end of file
package common
import (
"reflect"
"sort"
)
func getCommon(array interface{}) (reflect.Type, reflect.Value, int) {
t := reflect.TypeOf(array)
v := reflect.ValueOf(array)
l := v.Len()
return t, v, l
}
func SortSlice(array interface{}) {
t, v, _ := getCommon(array)
// res := make([]interface{}, l)
if t.Kind() == reflect.Slice {
switch v.Index(0).Kind() {
case reflect.Int:
array := array.([]int)
sort.Ints(array)
case reflect.String:
array := array.([]string)
sort.Strings(array)
case reflect.Float64:
array := array.([]float64)
sort.Float64s(array)
default:
panic("the param can only be int/string/float64 array")
}
} else {
panic("expects parameter 1 to be array")
}
}
package common
//与supplier相关的变量
var SearchSupplier = map[int]string{
1: "uture",
//2:"powerandsignal",
3: "rochester",
4: "tme",
5: "verical",
6: "element14",
7: "digikey",
8: "chip1stop",
9: "aipco",
10: "arrow",
//11:"bisco",
12: "alliedelec",
13: "avnet",
14: "mouser",
//15:"company",
//16:"liexin_lianying1"
17: "zhuanmai",
//18:"liexin_ti",
19: "peigenesis",
20: "powell",
21: "rs",
//22:"liexin_sell",
//100:"ziying",
//101:"liexin_jingdiao",
1676: "buerklin",
}
package config
import (
"github.com/go-ini/ini"
"strings"
)
var (
Cfg *ini.File
)
func SetUp(path string) (err error) {
//引入多个文件
Cfg, err = ini.LooseLoad(path+"/config.ini", path+"/database.ini", path+"/redis.ini", path+"/rabmq.ini", path+"/mongo.ini",path+"/log.ini")
//Cfg, err = ini.LooseLoad(path+"/config.ini", path+"/database.ini", path+"/redis.ini", path+"/rabmq.ini", path+"/mongo.ini",path+"/log.ini")
return
}
func Get(key string) *ini.Key {
if strings.Contains(key, ".") {
keys := strings.Split(key, ".")
return Cfg.Section(keys[0]).Key(keys[1])
}
return Cfg.Section("").Key(key)
}
func GetSectionValues(key string) (result []string) {
values := Cfg.Section(key).Keys()
for _, value := range values {
result = append(result, value.String())
}
return result
}
package config
func IsOpenDebug() bool{
return true
}
package config
type MongoDbDatabase struct {
Host string
UserName string
Password string
Database string
MaxPoolSize string
}
func BuildMongoDbConfgs () map[string]MongoDbDatabase{
return map[string]MongoDbDatabase{
"default" : {
Host:Get("mongo.host").String(),
UserName:Get("mongo.username").String(),
Password:Get("mongo.password").String(),
Database:Get("mongo.database").String(),
MaxPoolSize:Get("mongo.maxPoolSize").String(),
},
}
}
package config
type BaseDatabase struct {
UserName string
Password string
Host string
Database string
MaxIdleCons int
MaxOpenCons int
Prefix string
}
//多数据库配置
func BuildDatabaseList() (DatabaseList map[string]BaseDatabase) {
return map[string]BaseDatabase{
"spu": {
UserName: Get("spu.user_name").String(),
Password: Get("spu.password").String(),
Host: Get("spu.host").String(),
Database: Get("spu.database").String(),
Prefix: Get("spu.table_prefix").String(),
},
"supp": {
UserName: Get("supp.user_name").String(),
Password: Get("supp.password").String(),
Host: Get("supp.host").String(),
Database: Get("supp.database").String(),
Prefix: Get("supp.table_prefix").String(),
},
"cms": {
UserName: Get("cms.user_name").String(),
Password: Get("cms.password").String(),
Host: Get("cms.host").String(),
Database: Get("cms.database").String(),
Prefix: Get("cms.table_prefix").String(),
},
"liexin_data": {
UserName: Get("liexin_data.user_name").String(),
Password: Get("liexin_data.password").String(),
Host: Get("liexin_data.host").String(),
Database: Get("liexin_data.database").String(),
Prefix: Get("liexin_data.table_prefix").String(),
},
"wms": {
UserName: Get("wms.user_name").String(),
Password: Get("wms.password").String(),
Host: Get("wms.host").String(),
Database: Get("wms.database").String(),
Prefix: Get("wms.table_prefix").String(),
},
"szlc": {
UserName: Get("szlc.user_name").String(),
Password: Get("szlc.password").String(),
Host: Get("szlc.host").String(),
Database: Get("szlc.database").String(),
Prefix: Get("szlc.table_prefix").String(),
},
"sku_0": {
UserName: Get("sku_0.user_name").String(),
Password: Get("sku_0.password").String(),
Host: Get("sku_0.host").String(),
Database: Get("sku_0.database").String(),
Prefix: Get("sku_0.table_prefix").String(),
},
"sku_1": {
UserName: Get("sku_1.user_name").String(),
Password: Get("sku_1.password").String(),
Host: Get("sku_1.host").String(),
Database: Get("sku_1.database").String(),
Prefix: Get("sku_1.table_prefix").String(),
},
"sku_2": {
UserName: Get("sku_2.user_name").String(),
Password: Get("sku_2.password").String(),
Host: Get("sku_2.host").String(),
Database: Get("sku_2.database").String(),
Prefix: Get("sku_2.table_prefix").String(),
},
"sku_3": {
UserName: Get("sku_3.user_name").String(),
Password: Get("sku_3.password").String(),
Host: Get("sku_3.host").String(),
Database: Get("sku_3.database").String(),
Prefix: Get("sku_3.table_prefix").String(),
},
"sku_4": {
UserName: Get("sku_4.user_name").String(),
Password: Get("sku_4.password").String(),
Host: Get("sku_4.host").String(),
Database: Get("sku_4.database").String(),
Prefix: Get("sku_4.table_prefix").String(),
},
"sku_5": {
UserName: Get("sku_5.user_name").String(),
Password: Get("sku_5.password").String(),
Host: Get("sku_5.host").String(),
Database: Get("sku_5.database").String(),
Prefix: Get("sku_5.table_prefix").String(),
},
"sku_6": {
UserName: Get("sku_6.user_name").String(),
Password: Get("sku_6.password").String(),
Host: Get("sku_6.host").String(),
Database: Get("sku_6.database").String(),
Prefix: Get("sku_6.table_prefix").String(),
},
"sku_7": {
UserName: Get("sku_7.user_name").String(),
Password: Get("sku_7.password").String(),
Host: Get("sku_7.host").String(),
Database: Get("sku_7.database").String(),
Prefix: Get("sku_7.table_prefix").String(),
},
"sku_8": {
UserName: Get("sku_8.user_name").String(),
Password: Get("sku_8.password").String(),
Host: Get("sku_8.host").String(),
Database: Get("sku_8.database").String(),
Prefix: Get("sku_8.table_prefix").String(),
},
"sku_9": {
UserName: Get("sku_9.user_name").String(),
Password: Get("sku_9.password").String(),
Host: Get("sku_9.host").String(),
Database: Get("sku_9.database").String(),
Prefix: Get("sku_9.table_prefix").String(),
},
}
}
package config
type RedisDatabase struct {
Password string
Host string
Database string
MaxIdle int
MaxActive int
MaxAIdleTimeoutctive string
Prefix string
}
//多数据库配置
func BuildRedisConfigs() (RedisDatabaseMap map[string]RedisDatabase) {
redisReadMaxIdle,_ := Get("default_redis_read.max_idle").Int()
redisReadMaxActive,_ := Get("default_redis_read.max_active").Int()
redisWriteMaxIdle,_ := Get("default_redis_write.max_idle").Int()
redisWriteMaxActive,_ := Get("default_redis_write.max_active").Int()
return map[string]RedisDatabase{
"search_r": {
Host: Get("default_redis_read.host").String(),
Password: Get("default_redis_read.password").String(),
MaxIdle: redisReadMaxIdle,
MaxActive: redisReadMaxActive,
},
"default_r": {
Host: Get("default_redis_read.host").String(),
Password: Get("default_redis_read.password").String(),
MaxIdle: redisReadMaxIdle,
MaxActive: redisReadMaxActive,
},
"search_w": {
Host: Get("default_redis_write.host").String(),
Password: Get("default_redis_write.password").String(),
MaxIdle: redisWriteMaxIdle,
MaxActive: redisWriteMaxActive,
},
}
}
package e
type ApiError struct {
ErrMsg string
Code int
}
func (err *ApiError) Error() string {
return err.ErrMsg
}
/**
参数1 对应 ApiError ErrMsg
参数2 对应 ApiError Code 默认1001
*/
func NewApiError(opts ...interface{}) * ApiError{
var ErrMsg string;
Code:=500;
for num,opt:=range opts{
if(num>1){
break;
}
switch num {
case 0:
ErrMsg=opt.(string)
case 1:
Code=opt.(int)
break
}
}
return &ApiError{ ErrMsg: ErrMsg,Code:Code}
}
/**
检测异常,并抛出异常
*/
func CheckError(err error) {
if err != nil {
if apiError,ok:=err.(*ApiError);ok{
panic(apiError)
}else{
panic(NewApiError(err.Error()))
}
}
}
package e
type FatalError struct {
ErrMsg string
}
func (err *FatalError) Error() string {
return err.ErrMsg
}
func NewFatalError( msg string) *FatalError {
return &FatalError{ ErrMsg: msg}
}
func IsFatalError(err error) bool {
if _,ok:=err.(*FatalError);ok{
return true
}
return false
}
\ No newline at end of file
package es
import (
"github.com/imroc/req"
"math/rand"
"net/http"
"golang_open_platform/pkg/config"
)
type Response struct {
Took int `json:"took"`
TimeOut bool `json:"time_out"`
Shard map[string]int `json:"_shard"`
HitsResult HitsResult `json:"hits"`
}
type HitsResult struct {
Total int `json:"total"`
MaxScore float64 `json:"max_score"`
Hits string `json:"hits"`
}
func CurlES(index string, param string) (result string, err error) {
endpoints := config.Get("es.urls").Strings(",")
//随机获取一个节点进行请求
esUrl := endpoints[rand.Intn(len(endpoints))]
params := req.BodyJSON(param)
resp, err := req.Get(esUrl+"/"+index+"/_search", params)
if err != nil {
return
}
result = resp.String()
return
}
/*
主分片查询
*/
func CurlEsPrimary(index string, param string) (result string, err error) {
endpoints := config.Get("es.urls").Strings(",")
//随机获取一个节点进行请求
esUrl := endpoints[rand.Intn(len(endpoints))]
params := req.BodyJSON(param)
resp, err := req.Get(esUrl+"/"+index+"/_search?preference=_primary_first", params)
if err != nil {
return
}
result = resp.String()
return
}
/*
批量插入 或者 更新 es
POST /_bulk
{ "index":{"_index":"hcy1","_type":"goods","_id":"333333"} }
{ "name":"john doe","age":25 }
{ "index":{"_index":"hcy1","_type":"goods","_id":"6666"} }
{ "name":"mary smith","age":32 }
eg:
方法一:
param := `{"index":{"_index":"hcy1","_type":"goods","_id":"s1"} }`+"\n"+`{"name":"john doe","age":25 }`+"\n"+`{"index":{"_index":"hcy1","_type":"goods","_id":"s2"} }`+"\n"+`{"name":"john doe","age":25 }`+"\n"
result,err := es.BulkES(param)
println(result,err)
方法二:
lines := []string{
`{"index":{"_index":"hcy1","_type":"goods","_id":"s1"} }`,
`{"name":"john doe","age":25 }`,
`{"index":{"_index":"hcy1","_type":"goods","_id":"s2"} }`,
`{"name":"mary smith","age":32 }`,
}
param := strings.Join(lines, "\n")+"\n"
*/
func BulkES(param string) (result string, err error) {
endpoints := config.Get("es.urls").Strings(",")
esUrl := endpoints[rand.Intn(len(endpoints))] //随机获取一个节点进行请求
//params := req.BodyJSON(param)
//req.Debug = true
header := make(http.Header)
header.Set("Content-Type", "application/x-ndjson")
resp, err := req.Post(esUrl+"/_bulk?refresh=true", header, param) //refresh=true 立刻插入或修改 立刻生效,不需要等待es缓存写入文档
if err != nil {
return
}
result = resp.String()
return
}
//多条件多索引查询,需要查询的索引和条件自己拼接成queryJson里面
func CurlESMSearch(queryJson string) (result string, err error) {
endpoints := config.Get("es.urls").Strings(",")
//随机获取一个节点进行请求
req.Debug = false
esUrl := endpoints[rand.Intn(len(endpoints))]
params := req.BodyJSON(queryJson)
resp, err := req.Post(esUrl+"/_msearch", params)
if err != nil {
return
}
result = resp.String()
return
}
//滚动搜索和索引之间的文档重索引
func ScrollES(param string) (result string, err error) {
endpoints := config.Get("es.urls").Strings(",")
//随机获取一个节点进行请求
req.Debug = false
esUrl := endpoints[rand.Intn(len(endpoints))]
params := req.BodyJSON(param)
resp, err := req.Post(esUrl+"/_search/scroll", params)
if err != nil {
return
}
result = resp.String()
return
}
package gredis
import (
"fmt"
"github.com/gomodule/redigo/redis"
"golang_open_platform/pkg/config"
"time"
)
type ichuntRedis struct {
RedisList map[string]*redis.Pool
}
var ichuntRedis_ = &ichuntRedis{}
func Conn(connection string) redis.Conn {
return ichuntRedis_.RedisList[connection].Get()
}
var writeConn, readConn *redis.Pool
func Setup() (err error) {
ichuntRedis_.RedisList = make(map[string]*redis.Pool, 0)
RedisDatabaseMap := config.BuildRedisConfigs()
for redisKey, redisConfig := range RedisDatabaseMap {
ichuntRedis_.RedisList[redisKey], err = getConn(redisConfig.Host, redisConfig.Password, redisConfig.MaxIdle, redisConfig.MaxActive)
if err != nil {
panic(err)
}
}
return nil
}
//格式化成字符串
func String(a interface{}, err error) (string, error) {
return redis.String(a, err)
}
func getConn(writeHost, password string, maxIdle, maxActive int) (pool *redis.Pool, err error) {
//maxIdle, _ := config.Get("redis.max_idle").Int()
//maxActive, _ := config.Get("redis.max_active").Int()
pool = &redis.Pool{
MaxIdle: maxIdle,
MaxActive: maxActive,
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", writeHost)
if err != nil {
return nil, err
}
if password != "" {
if _, err := c.Do("AUTH", password); err != nil {
c.Close()
return nil, err
}
}
return c, err
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
Wait: true,
}
return
}
/*
批量或者单个查询redis数据,统一返回map[string]string
@param hkey string 集合键值,如sku
@param targetIds string 查询的id 切片
eg:
redisConn := gredis.Conn("search_r")
skuArr := gredis.HgetPi(&redisConn,"Self_SelfGoods",[]string{"1001","10005"})
*/
func Hmget(redisCon string, hkey string, targetIds []string) map[string]string {
if len(targetIds) == 0 {
fmt.Println("批量查询不得传入空")
return nil
}
redisConn := Conn(redisCon)
defer redisConn.Close()
skuArr := make(map[string]string, 0)
if len(targetIds) == 1 {
goods_id := targetIds[0]
info, err := String(redisConn.Do("HGET", hkey, goods_id))
if err != nil && err != redis.ErrNil {
fmt.Print("连接redis错误991:", err)
}
if info == "" {
skuArr[goods_id] = ""
} else {
skuArr[goods_id] = info
}
//fmt.Println("单个查询")
return skuArr
} else { //多个查询
param := []interface{}{hkey}
for _, goods_id := range targetIds {
param = append(param, goods_id)
}
res1, err1 := redisConn.Do("hmget", param...)
reply, _ := redis.Strings(res1, err1)
if err1 != nil {
fmt.Println(err1)
}
for k, goodsInfo := range reply {
skuArr[targetIds[k]] = goodsInfo
}
//fmt.Println(skuArr)
return skuArr
}
}
/*
批量或者单个查询redis数据,统一返回map[string]string
@param hkey string 集合键值,如sku
@param targetIds string 查询的id 切片
eg:
redisConn := gredis.Conn("search_r")
skuArr := gredis.HgetPi(&redisConn,"Self_SelfGoods",[]string{"1001","10005"})
*/
func HgetPi(redisCon string, hkey string, targetIds []string) map[string]string {
redisConn := Conn(redisCon)
defer redisConn.Close()
skuArr := make(map[string]string, 0)
if len(targetIds) == 1 {
oneId := targetIds[0]
info, err := String(redisConn.Do("HGET", hkey, oneId))
if err != nil {
fmt.Print(err)
}
if info == "" {
skuArr[oneId] = ""
} else {
skuArr[oneId] = info
}
return skuArr
}
for _, v := range targetIds {
redisConn.Send("HGet", hkey, v)
}
redisConn.Flush()
for _, goods_id := range targetIds {
info, _ := redisConn.Receive()
if info == nil {
skuArr[goods_id] = ""
continue
}
oneInfo := string(info.([]byte))
skuArr[goods_id] = oneInfo
}
return skuArr
}
/*
批量或者单个查询redis数据,统一返回map[string]string
@param hkey string 集合键值,如sku
@param targetIds string 查询的id 切片
eg:
redisConn := gredis.Conn("search_r")
skuArr := gredis.HgetPi(&redisConn,"Self_SelfGoods",[]string{"1001","10005"})
*/
func HgetPi2(redisCon string, hkey string, targetIds []string) map[string]string {
redisConn := Conn(redisCon)
defer redisConn.Close()
skuArr := make(map[string]string, 0)
for _, goods_id := range targetIds {
info, err := String(redisConn.Do("HGET", hkey, goods_id))
if err != nil {
fmt.Print("连接redis错误991:", err)
}
if info == "" {
skuArr[goods_id] = ""
} else {
skuArr[goods_id] = info
}
}
return skuArr
}
package logger
import (
"fmt"
"github.com/ichunt2019/log"
"golang_open_platform/pkg/config"
"strings"
"sync"
)
const (
LogLevelDebug = iota
LogLevelTrace
LogLevelInfo
LogLevelWarn
LogLevelError
LogLevelFatal
)
var (
logD map[string]logger.LogInterface
)
var once sync.Once
func Loginit() {
once.Do(func() {
fmt.Print("日志初始化开始")
configs:=config.GetSectionValues("log_config")
logD=make(map[string]logger.LogInterface)
for _,configOne:=range configs{
sliceConfig:=strings.Split(configOne, ",")
log:=getLog(sliceConfig[0],sliceConfig[1],"0")
logD[sliceConfig[0]]=log
}
})
}
/**
@param path 路径 比如sku_save 即文件夹是 sku 文件名类似 是save_2020-12-10.log
@param msg 错误文本
@Level 默认是 logger.LogLevelInfo
示例
*/
func SyncInsert(path string,msg string,Levels ...int) {
log:=getLog(path,"10","1")
level:=LogLevelInfo
if(len(Levels)>0){
level=Levels[0]
}
switch level {
case LogLevelDebug:
log.Debug(msg)
case LogLevelTrace:
log.Trace(msg)
case LogLevelInfo:
log.Info(msg)
case LogLevelWarn:
log.Warn(msg)
case LogLevelFatal:
log.Fatal(msg)
case LogLevelError:
log.Error(msg)
default:
log.Info(msg)
}
log.SyncWait()
}
//入口
func Select(logFiled string) logger.LogInterface {
log, ok := logD[logFiled]
if(!ok){
panic("log配置:"+logFiled+"不存在")
}
if log,ok:=log.(logger.LogInterface);ok{
return log
}
panic("type not logger.LogInterface")
}
//内部方法,获取log
func getLog(path string,chan_size string,openSync string) logger.LogInterface {
logConfig := make(map[string]string)
logConfig["log_chan_size"]="10"
slicePath:=strings.Split(path, "_")
if(slicePath[0]=="" || slicePath[1]==""){
panic("配置文件出错:文件/路径配置出错;提示:第一个参数格式应为A_B")
}
logConfig["log_path"] = "logs/"+slicePath[0]
logConfig["log_name"] = slicePath[1]
if(openSync=="1"){
logConfig["open_sync"] = "1"
}
if(chan_size!=""){
logConfig["log_chan_size"]=chan_size
}
log,err:= logger.InitLogger("file", logConfig)
if err != nil {
panic("初始化log包出错:")
}
log.Init()
return log
}
func SetUp() (err error) {
logConfig := make(map[string]string)
logConfig["log_path"] = "logs"
logConfig["log_chan_size"] = "5"
//err = logger.InitLogger("file", logConfig)
if err != nil {
return err
}
logger.Init()
return
}
package logger
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
)
func check(e error) {
if e != nil {
panic(e)
}
}
/**
* 判断文件是否存在 存在返回 true 不存在返回false
*/
func checkFileIsExist(filename string) bool {
var exist = true
if _, err := os.Stat(filename); os.IsNotExist(err) {
exist = false
}
return exist
}
/*
@param writeString 写入文件字符串
@param file_pre 附加文件前缀
eg: logic.Loginfo("逾期统计开始","_yuqilv_");
*/
func Log(writeString string,log_file_pre string,types int) {
log_file_pre = "_"+log_file_pre
date := time.Now().Format("2006-01-02")
date2 := time.Now().Format("2006-01-02 15:04:05")
path,_:=os.Getwd()
CreateDateDir(path,"logs")
var filename = path+"/logs/"+date+log_file_pre+".txt"
var f *os.File
var err1 error
if checkFileIsExist(filename) { //如果文件存在
f, err1 = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE,0755)
} else {
f, err1 = os.Create(filename) //创建文件
}
check(err1)
ss := StrReplace("\r\n","",writeString,1) //替换多余换行
io.WriteString(f,"\r\n"+date2+"----"+ss) //写入文件(字符串)
if types == 1 {
fmt.Print(writeString)
}
defer f.Close()
//fmt.Print(err)
}
// CreateDateDir 根据当前日期来创建文件夹
func CreateDateDir(Path string,folderName string) string {
if folderName == "" {
folderName = time.Now().Format("20060102")
}
folderPath := filepath.Join(Path, folderName)
if _, err := os.Stat(folderPath); os.IsNotExist(err) {
// 必须分成两步:先创建文件夹、再修改权限
os.Mkdir(folderPath, 0755) //0777也可以os.ModePerm
os.Chmod(folderPath, 0755)
}
return folderPath
}
// StrReplace str_replace()
func StrReplace(search, replace, subject string, count int) string {
return strings.Replace(subject, search, replace, count)
}
package message
import (
"encoding/json"
"github.com/imroc/req"
"github.com/tidwall/gjson"
"golang_open_platform/pkg/config"
)
//发送钉钉消息的包
type DingDingRequest struct {
MsgType string `json:"msgtype"`
Text map[string]string `json:"text"`
IsAtAll bool `json:"isAtAll"`
}
type DingDingResponse struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}
func DingDingPush(content string) (result DingDingResponse, err error) {
accessToken := config.Get("DINGDING.SEARCH_API_MONITOR").String()
webhook := "https://oapi.dingtalk.com/robot/send?access_token=" + accessToken
data := make(map[string]interface{})
data["msgtype"] = "text"
data["text"] = map[string]string{
"content": content,
}
req.Debug = false
dataStrByte, _ := json.Marshal(data)
dataStr := string(dataStrByte)
//dataStr = strings.Replace(dataStr, "\\", "\\\\", -1)
params := req.BodyJSON(dataStr)
resp, err := req.Post(webhook, params, req.Header{
"Content-Type": "application/json",
"charset": "UTF-8",
})
if resp == nil {
return
}
result.Errcode = int(gjson.Get(resp.String(), "errcode").Int())
result.Errmsg = gjson.Get(resp.String(), "errmsg").String()
return
}
package message
import (
"encoding/json"
"fmt"
"github.com/syyongx/php2go"
"net/http"
"net/url"
"golang_open_platform/pkg/config"
"strconv"
"time"
)
//普通发短信
func SendMessage(mobile int64, keyWord string, content map[string]interface{}) {
//es连接告警的keyword是 es_connect_monitor
if mobile != 0 {
timeNow := time.Now().Unix()
requestContent, _ := json.Marshal(content)
requestTel, _ := json.Marshal([]int64{mobile})
apiDomain := config.Get("message.api_domain").String()
apiMd5Str := config.Get("message.api_md5_str").String()
resp, err := http.PostForm(apiDomain, url.Values{
"data": {string(requestContent)},
"touser": {string(requestTel)},
"keyword": {keyWord},
"k1": {strconv.FormatInt(timeNow, 10)},
"k2": {php2go.Md5(php2go.Md5(strconv.FormatInt(timeNow, 10)) + apiMd5Str)},
"is_ignore": {},
})
if err != nil {
fmt.Print(err)
}
defer resp.Body.Close()
}
}
package mongo
import (
"golang_open_platform/pkg/config"
"gopkg.in/mgo.v2"
"strconv"
)
var session *mgo.Session
type ichuntMongo struct {
MongoList map[string]*mgo.Session
}
var ichuntMongo_ = &ichuntMongo{}
func getconn(mongoConfig config.MongoDbDatabase) (*mgo.Session ,error){
url := mongoConfig.Host
maxPoolSize := mongoConfig.MaxPoolSize
maxPoolSizeInt,err := strconv.Atoi(maxPoolSize)
if err != nil{
maxPoolSizeInt = 100
}
url += "?maxPoolSize="+maxPoolSize
session, err = mgo.Dial(url)
if err != nil {
return nil ,err
}
//fmt.Println("url",url)
//fmt.Println("maxPoolSizeInt",maxPoolSizeInt)
session.SetPoolLimit(maxPoolSizeInt)
session.SetMode(mgo.Monotonic, true)
myDB :=session.DB(mongoConfig.Database)
err = myDB.Login(mongoConfig.UserName,mongoConfig.Password)
if err != nil {
return nil ,err
}
return session,nil
}
func SetUp() (err error) {
err = nil
ichuntMongo_.MongoList = make(map[string]*mgo.Session,0)
mongodbList := config.BuildMongoDbConfgs()
if len(mongodbList) > 0{
for mongoName,mongoConfig := range mongodbList{
ichuntMongo_.MongoList[mongoName],err = getconn(mongoConfig)
if err != nil{
break
}
}
}
return err
}
func Conn(connection string) (*mgo.Session){
return ichuntMongo_.MongoList[connection].Copy()
}
package mq
import (
"github.com/ichunt2019/go-rabbitmq/utils/rabbitmq"
"golang_open_platform/pkg/config"
)
func PushMsg(listName string, data string) {
queueExchange := rabbitmq.QueueExchange{
listName,
listName,
"",
"direct",
config.Get("rabmq.url").String(),
}
rabbitmq.Send(queueExchange, data)
}
package mysql
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
"golang_open_platform/pkg/config"
"golang_open_platform/pkg/e"
)
var DatabaseConMap map[string]*xorm.Engine
func Setup() error {
DatabaseConMap = make(map[string]*xorm.Engine, 0)
DatabaseList := config.BuildDatabaseList()
var err error
//循环生成数据库链接
for conName, db := range DatabaseList {
userName := db.UserName
password := db.Password
host := db.Host
database := db.Database
dataSourceName := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", userName, password, host, database)
DatabaseConMap[conName], err = xorm.NewEngine("mysql", dataSourceName)
if err != nil {
return e.NewFatalError(err.Error()) //这里返回致命异常
}
//日志打印SQL
ShowSql,_ := config.Get("xorm.ShowSQL").Bool()
DatabaseConMap[conName].ShowSQL(ShowSql)
//设置连接池的空闲数大小
DatabaseConMap[conName].SetMaxIdleConns(db.MaxIdleCons)
//设置最大打开连接数
DatabaseConMap[conName].SetMaxOpenConns(db.MaxOpenCons)
}
return nil
}
func Conn(conName string) *xorm.Engine {
return DatabaseConMap[conName]
}
package validator
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/gogf/gf/util/gconv"
"reflect"
)
const (
Required = "required"
RequiredMsgKey="requiredMsg"
RequiredCodeKey="requiredCode"
)
type errorResult struct {
ErrMsg string
Code int
}
func (e *errorResult) Error() string{
return e.ErrMsg
}
type c_validator struct {
errDefaultTagMsg map[string]errorTagMsg//主动设置的tag提示
}
//触发限制标签 提示语 比如触发 required 就提示 缺少必要参数(defaultMsg)
type errorTagMsg struct {
defaultMsg string
defaultCode int
}
func NewValidator() *c_validator{
return &c_validator{map[string]errorTagMsg{}}
}
func (v *c_validator)SetErrMsg(msgType string,msg string)*c_validator {
s:=v.errDefaultTagMsg[msgType]
s.defaultMsg=msg
v.errDefaultTagMsg[msgType]=s
return v
}
func (v *c_validator)SetErrCode(msgType string,code int) *c_validator {
s:=v.errDefaultTagMsg[msgType]
s.defaultCode=code
v.errDefaultTagMsg[msgType]=s
return v
}
func (v *c_validator)GinShouldBind(ctx *gin.Context,obj interface{})*errorResult {
typeOfObj:=reflect.TypeOf(obj).Elem()
if err:= ctx.ShouldBind(obj); err != nil {
if fieldError,ok:=err.(validator.ValidationErrors);ok{
fieldName:=fieldError[0].StructField()
err1:=v.getErrorResult(fieldError,typeOfObj,fieldName)
if(err1.Code==4008){
err1.ErrMsg=err.Error()
}
return err1
}
}
return nil
}
/**
比如需要增新的tag required_with
就switch 多一个 required_with,加一行如下代码
return v.returnErrResult(field,fieldName,Required,RequiredMsgKey,RequiredCodeKey)
并且 const增加相应的常量
*/
func (v *c_validator) getErrorResult(fieldError validator.ValidationErrors, typeOfObj reflect.Type, fieldName string) (*errorResult) {
field,_:=typeOfObj.FieldByName(fieldName);
switch fieldError[0].Tag() {
case "required":
return v.returnErrResult(field,fieldName,Required,RequiredMsgKey,RequiredCodeKey)
break
default:
return &errorResult{"",4008}
}
return nil
}
func (v *c_validator) returnErrResult(field reflect.StructField ,fieldName string,tagName string,tagErrmsgKey string,tagErrCodeKey string) *errorResult {
if msg:=v.errDefaultTagMsg[tagName].defaultMsg;msg!=""{
code:=v.errDefaultTagMsg[tagName].defaultCode
fmt.Printf(msg+";field is "+fieldName)
//return &errorResult{msg+";field is "+fieldName,code}
return &errorResult{msg,code}
}else{
msg=field.Tag.Get(tagErrmsgKey)
code:=gconv.Int(field.Tag.Get(tagErrCodeKey))
fieldName=field.Tag.Get("form")
if(fieldName==""){
fieldName=field.Tag.Get("json")
}
//return &errorResult{msg+";field is "+fieldName,code}
fmt.Printf(msg+";field is "+fieldName)
return &errorResult{msg,code}
}
}
package vars
const (
Table_CourseMain="course_main"
Table_CourseCounts="course_counts"
)
package vars
//数字转class字符串,按顺序0-9
var NumberToClass = [][]string{
{"sfgdqwer", "asfgdtyhg", "asfgdpolk", "asfgdpoqw"},
{"sfgdrfdf","asfgderfd","asfgdwdsa","asfgdpoer"},
{"asfgdasde","asfgdqwsz","asfgdrtgd","asfgdpovv"},
{"asfgdwsxc","asfgdwsxz","asfgdrfvb","asfgdpoee"},
{"asfgdqazs","asfgdqasd","asfgdqwag","asfgdpogh"},
{"asfgdrtyh","asfgdyutr","asfgdeews","asfgdpotg"},
{"asfgdpluj","asfgdikjf","asfgdesgj","asfgdpfff"},
{"asfgdtrdb","asfgdiksf","asfgdsgkp","asfgdprty"},
{"asfgdpehl","asfgdstgb","asfgderll","asfgdpokf"},
{"asfgdpehg","asfgdstgf","asfgderlf","asfgdpogk"},
}
## golang 商品微服务
### 项目启动
一:启动http 对外服务:
cd cmd/http
go build -o ./http ./http_server.go
chmod -R 755 http
chmod +x http
运行-线上: ./http -config=../../conf/prod
运行-开发: ./http -config=../../conf/dev
二:增加定时任务:
cd cmd/cron
go build -o ./cron ./cron_server.go
chmod -R 755 cron
chmod +x cron
运行-线上: ./cron -config=../../conf/prod
运行-开发: ./cron -config=../../conf/dev
一共两个任务,最好都配置supervisor
### 目录结构
├── bat //运行脚本或者protoc生成脚本
├── boot //启动文件,比如配置加载,数据库加载等等
├── cmd //服务的主入口,可直接go run执行
├── conf //配置文件目录
├── framework //gin框架接入
├── controller //顾名思义
├── dao //数据访问层
├── model //模型目录
├── pkg //公用包目录,比如配置读取,获取数据库实例,通用函数或者变量等都可以从这里获取
├── protopb //生成的protos.pb.go文件
├── protos //原始的protos文件
├── service //业务逻辑操作所在目录
### 已经封装的模块以及用法
#### 1.配置读取
接在```conf/config.ini```文件添加自己的配置项
然后使用```config.Get("xxx.xxx").String()```或者```config.Get("xxx.xxx").Int()```即可
如果想要获取某个节点下的所有值,可以用```config.GetSectionValues("xxx")```
如果想要去获取更多其它的值,可以用暴露出来的配置变量自己去获取,比如: ```config.Cfg.Section("xxx").Key("xxx")```
配置包的文档 : https://ini.unknwon.io/docs
#### 2.mysql连接实例
使用```mysql.GetDB()```即可获取对应的实例,不同数据库的连接可以自己定义不同的GetDB方法
gorm包的文档 : https://gorm.io/zh_CN/docs/index.html
#### 3.redis使用
可以直接调用```gredis.Hset(),gredis.Hget()```等方法,具体使用请参考代码,其中返回的结果是一个redis对象,需要自己转成自己需要的类型,比如 : ```redis.String(gredis.HGet("test",1))``` .
redis包文档 : https://pkg.go.dev/gopkg.in/DataDog/dd-trace-go.v1/contrib/garyburd/redigo?tab=doc
#### 4.mgo连接实例
使用```mongo.GetDB()```即可,不同数据库的连接可以自己定义不同的GetDB方法
然后用C方法指定集合进行操作即可: ```mongo.GetDB().C("xxx").Find(bson.M{}).Count()``` .
mgo包的文档 : https://godoc.org/gopkg.in/mgo.v2
文档2 : https://zhuanlan.zhihu.com/p/99731299
#### 5.钉钉群消息以及短信
直接在pkg/message包调用对应的函数即可:
钉钉的为```message.DingDingPush```
#### 6.商品信息获取
直接调用```service.GetGoodsInfo```函数即可,传入goods_id的字符串,多个goods_id用逗号隔开(商品服务的请求本来亦是如此),会返回json字符串,然后可以根据自己的需求用gjson获取对应的信息即可
#### 7.ES的基本请求
直接调用```es.CurlES```函数即可,传入索引名称和请求的json参数即可,得到的返回结果是ES的原生结果,可以根据自己的需求用gjson获取对应的信息
#### 7.队列推送
直接调用```mq.PushMsg("xian_test","test")```函数即可(调用方式保持和PHP一致),第一个参数队列名同时也是路由名,第二个是要传的值
package routes
import (
"github.com/gin-gonic/gin"
"golang_open_platform/controller"
)
//初始化路由
func InitRouter() *gin.Engine {
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
//心跳
r.GET("hbsdata", controller.Hbsdata)
r.POST("hbsdata",controller.Hbsdata)
//用classId获取sku列表
r.GET("GetSkuListByClass",controller.Error_Middleware("sku_query"),
controller.Open_Middleware("GetSkuListByClass"),controller.GetSkuListByClass)
//获取sku列表完整字段
r.GET("GetSkuListFull",controller.Error_Middleware("sku_query"),
controller.Open_Middleware("GetSkuListFull"),controller.GetSkuListFull)
//获取sku列表 价格库存相关字段
r.GET("GetSkuListPrice",controller.Error_Middleware("sku_query"),
controller.Open_Middleware("GetSkuListPrice"),controller.GetSkuListPrice)
//
return r
}
#!/bin/bash
cd /data/gocode/golang_open_platform
git pull origin dev
go build -o ./cmd/http/http_server ./cmd/http/http_server.go
go build -o ./cmd/http/cron ./cmd/http/cron_server.go
supervisorctl restart golang_open_platform_http
\ No newline at end of file
package service
import (
"encoding/json"
"fmt"
"github.com/guonaihong/gout"
"github.com/tidwall/gjson"
"golang_open_platform/dao"
"golang_open_platform/model"
"golang_open_platform/open"
"golang_open_platform/pkg/common"
"golang_open_platform/pkg/config"
"golang_open_platform/pkg/e"
"math"
"strings"
)
type SkuService struct {
}
func NewSkuService() *SkuService{
return &SkuService{}
}
/*
用classId获取sku列表
*/
func (this *SkuService)GetSkuListByClass(req *model.QuerySkuCreq) (rsp *model.QuerySkuCrsp,err error ){
pageData,err:=dao.GetMongoOpenSku(req.ClassId,0)
if(err!=nil){
return nil,e.NewApiError("service err",open.OTHERERROR)
}
rsp=&model.QuerySkuCrsp{Count:pageData.Count,Limit:10,Page:req.Page}
if(pageData.Count<=0){//没数据
return
}
if(req.Page>0){
//验证下page是否有效
if(math.Ceil(float64(pageData.Count/rsp.Limit))<float64(pageData.Page)){
return nil,e.NewApiError("page invalid",open.PARAM1)
}
mongoOpenSku,err:=dao.GetMongoOpenSku(req.ClassId,req.Page)
if(err!=nil){
return nil,e.NewApiError("service err",open.OTHERERROR)
}
if(mongoOpenSku.SkuIds==""){
if(rsp.Count>0){
common.PrintStdout().Printf("classID:%d count 大于0 ids 却为空",req.ClassId)
}
return rsp,nil
}
skuIds:=strings.Replace(mongoOpenSku.SkuIds," ",",",-1)
//skuIds
remoteData,err:=this.getRemoteSku(skuIds)
if(err!=nil){
common.PrintStdout().Printf("获取远程商详接口报错 msg:"+err.Error())
return nil,e.NewApiError("service err",open.REMOTESKUINFO)
}
field:=[]string{"goods_id","spu_id","brand_id","brand_name","goods_name", "stock","moq","mpq","class_id1","class_name","attrs","ladder_price",}
//field:=[]string{"spu_id","attrs","supplier_name","goods_images","hk_delivery_time"}
rsp.SkuData=model.SkuFilter(*remoteData,field)
}
return rsp,nil
}
//获取sku列表完整字段
func (this *SkuService) GetSkuListFull(req * model.QuerySkuReq)(rsp *model.QuerySkuRsp,err error) {
//skuIds:=strings.Replace(req.GoodsIds," ",",",-1)
//skuIds
rsp=&model.QuerySkuRsp{}
remoteData,err:=this.getRemoteSku(req.GoodsIds)
field:=[]string{"goods_id","spu_id","brand_id","brand_name","goods_name", "stock","moq","mpq","class_id1","class_name","attrs","ladder_price",}
rsp.SkuData=model.SkuFilter(*remoteData,field)
return
}
//获取sku列表 价格库存相关字段
func (this *SkuService) GetSkuListPrice(req * model.QuerySkuReq)(rsp model.QuerySkuRsp,err error) {
remoteData,err:=this.getRemoteSku(req.GoodsIds)
field:=[]string{"goods_id","stock","ladder_price"}
rsp.SkuData=model.SkuFilter(*remoteData,field)
return
}
//获取远端商详接口
func (this *SkuService) getRemoteSku(ids string) (remoteData *model.RemoteSkuData,err error){
resStr:=""
err=gout.POST(config.Get("sku_server.api_domain").String()+"/synchronization")/*.Debug(true)*/.SetForm(gout.H{"goods_id": ids}).BindBody(&resStr).Do()
if(err!=nil){
return remoteData,err
}
gjsonRes:=gjson.Parse(resStr)
if(gjsonRes.Exists()==false){
return remoteData,fmt.Errorf("返回的参数不能被json解析")
}
if(gjsonRes.Get("errcode").Int()!=0){
msg:=gjsonRes.Get("errmsg").String()
return remoteData,fmt.Errorf(msg)
}
dataGjsonObj:=gjsonRes.Get("data")
if(dataGjsonObj.IsObject()==false){
return remoteData,fmt.Errorf("商详返回的 data数据格式不对")
}
remoteData=&model.RemoteSkuData{}
err=json.Unmarshal([]byte(dataGjsonObj.String()),remoteData)
if(err!=nil){
return remoteData,fmt.Errorf("data 不能被json.Unmarshal解析")
}
return remoteData,nil
}
package main
import (
"fmt"
"github.com/syyongx/php2go"
)
func main() {
var a = []string{"3923"}
var b = 3923
fmt.Println(php2go.InArray(b,a))
}
#适用于更新GOSKU代码服务,并且重启代码
#!/bin/bash
Cur_Dir=$(pwd)
echo $Cur_Dir
cd $Cur_Dir
git reset --hard HEAD
git pull origin dev
#rm -f ${Cur_Dir}"/cmd/http/http"
export GO111MODULE=on
export CGO_ENABLED=0
go env -w GOPROXY=https://goproxy.cn,direct
go build -o ${Cur_Dir}"/cmd/http/http" ${Cur_Dir}"/cmd/http/http_server.go"
chmod +x ${Cur_Dir}"/cmd/http/http"
chmod +x ${Cur_Dir}"/update.sh"
supervisorctl restart go_sku_server_60014:*
echo "更新执行成功"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment