commit 2ace0bade5920b43b0d3e0d793cb34ad3dde4439 Author: xiexiaojun <346944475@qq.com> Date: Thu Mar 7 21:30:01 2019 +0800 new new diff --git a/file/file.go b/file/file.go new file mode 100644 index 0000000..1597940 --- /dev/null +++ b/file/file.go @@ -0,0 +1,49 @@ +package file + +import ( + "fmt" + "io" + "net/http" + "os" + "public/tools" + "time" +) + +//上传单个文件 +func UploadFile(r *http.Request, w http.ResponseWriter, field, file_type string) (result bool, file_name string) { + //接受post请求 + if r.Method == "POST" { + r.ParseMultipartForm(32 << 20) + file_name = GetFileName(file_type) + //开始存储文件 + { + file, _, err := r.FormFile(field) //文件name + defer file.Close() + if err != nil { + result = false + } + + if !tools.CheckFileIsExist(tools.GetModelPath() + "/file/" + field + "/") { + err1 := os.Mkdir(tools.GetModelPath()+"/file/"+field+"/", os.ModePerm) //创建文件夹 + if err1 != nil { + result = false + } + } + + f, err := os.OpenFile(tools.GetModelPath()+"/file/"+field+"/"+file_name, os.O_WRONLY|os.O_CREATE, 0666) + defer f.Close() + if err != nil { + result = false + } + io.Copy(f, file) + } + + } else { + result = false + } + return +} + +func GetFileName(exp string) string { + return fmt.Sprintf("%d%s.%s", tools.GetUtcTime(time.Now()), tools.GetRandomString(4), exp) +} diff --git a/goleveldb/goleveldb.go b/goleveldb/goleveldb.go new file mode 100644 index 0000000..05a1cbc --- /dev/null +++ b/goleveldb/goleveldb.go @@ -0,0 +1,95 @@ +package goleveldb + +import ( + "os" + + "../../data/config" + "../log" + "github.com/syndtr/goleveldb/leveldb" +) + +var m_db *leveldb.DB = nil + +func init() { + Clear(); + creat() +} + +func creat(){ + if m_db == nil{ + var err error + m_db, err = leveldb.OpenFile(config.GetLevelDbDir(), nil) + if err != nil { + log.Print(log.Log_Error, err.Error()) + m_db.Close() + m_db = nil + } + } +} + +/* + 清空数据 +*/ +func Clear() bool { + Close(); + os.RemoveAll(config.GetLevelDbDir()); + return true; +} + + +/* + 关闭 +*/ +func Close(){ + if m_db != nil{ + m_db.Close() + m_db = nil; + } +} + +/* +*获取 + */ +func Get(key []byte) (data []byte, err error) { + data = nil + data, err = m_db.Get(key, nil) + return +} + +/* +设置 +*/ +func Set(key, value []byte) error { + err := m_db.Put(key, value, nil) + return err +} + +/* + 删除 +*/ +func Delete(key []byte) error { + return m_db.Delete(key, nil) +} + +/* +*获取 + */ +func Getkv(key string) (data []byte, err error) { + return Get([]byte(key)) +} + +/* +*获取 + */ +func Setkv(key string, value []byte) error { + return Set([]byte(key), value) +} + +/* + 删除 +*/ +func Deletekv(key string) error { + return Delete([]byte(key)) +} + + diff --git a/message/message.go b/message/message.go new file mode 100644 index 0000000..aabff01 --- /dev/null +++ b/message/message.go @@ -0,0 +1,274 @@ +/* + 消息定义接口 +*/ +package message + +import ( + "data/config" + "encoding/json" + "fmt" + "log" + "public/tools" + + "github.com/bitly/go-simplejson" +) + +const ( //消息id定义 + NormalMessageId = 0 ////默认的返回值,为0,自增 + ServerMaintenance = 1 //服务器维护中 请稍后再试,服务器出错会回这个信息,暂时不用 + AccountDisabled = 2 //帐号被禁用 + AppidOverdue = 3 //appid过期 + + UnknownError = 101 //未知错误 + TokenFailure = 102 //token失效 + HTMLSuccess = 200 //成功 + BlockingAcess = 405 //禁止访问( 禁用请求中所指定的方法) + + NewReport = 2001 //新消息 + NewHeart = 2002 //心跳 + + ParameterInvalid = 1001 //参数无效 + AppidParameterInvalid = 1002 //appid参数无效 + EncryptCheckError = 1003 //密文校验失败 + UserNameDoNotExist = 1004 //用户名不存在或密码错误 + DuplicateKeyError = 1005 //键值对重复 + InValidOp = 1007 //无效操作 + NotFindError = 1006 //未找到 + InValidAuthorize = 1008 //授权码错误 + HasusedError = 1009 //已被使用 + HasActvError = 1010 //已被激活 + ActvFailure = 1011 //|激活码被禁止使用 + UserExisted = 1012 //用户已存在 + VerifyTimeError = 1013 //验证码请求过于平凡 + MailSendFaild = 1014 //邮箱发送失败 + SMSSendFaild = 1015 //手机发送失败 + PhoneParameterError = 1016 //手机号格式有问题 + VerifyError = 1017 //验证码错误 + UserNotExisted = 1018 //用户不存在 + TopicExisted = 1019 //topic已经存在 + TopicNotExisted = 1020 //topic不存在 + BundleIdNotExisted = 1021 //bundle_id不存在 + TopicStartFail = 1022 //topic开启处理失败 + TopicTypeNotExisted = 1023 //topic处理类型不存在 + TopicIsNotNull = 1024 //topic不能为空 + DeviceNotExisted = 1025 //设备不存在 + StateExisted = 1027 //状态已存在 + LastMenuNotExisted = 1028 //上级菜单不存在 + MenuNotExisted = 1029 //菜单不存在 + UserMenuNotExisted = 1030 //用户权限不存在 + DeviceIdNotExisted = 1031 //设备ID不存在 + GoodsDealTypeNotExisted = 1032 //商品处理类型不存在 + GoodsIdNotExisted = 1033 //商品不存在 + GoodsBeInDiscount = 1034 //商品正在打折 + GoodsPayTypeNotExisted = 1035 //商品可支付类型不存在 + GoodsIdExisted = 1036 //商品已存在 + OrderIdNotExisted = 1043 //订单不存在 + GoodsBeNotInDiscount = 1044 //商品未打折 + NotifyIsNotMatch = 1045 //会话不匹配 + GoodsIsDiscountRecovery = 1046 //商品已恢复原价 + InvitationUserNotExisted = 1047 //邀请用户不存在 + // InvitationUserLevelIsFull = 1048 //邀请用户级数已满 + UserNotAuthorize = 1049 //用户未授权 + ApplicantIsExisted = 1050 //申请人已存在 + ApplicantNotExisted = 1051 //申请人不存在 + CreditOrderNotVaild = 1052 //订单无效 + RepeatWxWithdraw = 1053 //微信零钱重复提现 + WxWithdrawAmountError = 1054 //提现金额错误 + WxWithdrawError = 1055 //微信提现失败 + RepeatSubmission = 1056 //重复提交 + BundleExisted = 1057 //bundle已存在 + AuthExisted = 1058 //权限已存在 + AuthNotExisted = 1059 //权限不存在 + RoomTypeNotExisted = 1060 //房型不存在 + RoomTypeExisted = 1061 //房型已存在 + RoomNoNotExisted = 1062 //房间不存在 + RoomNoExisted = 1063 //房间已存在 + RateTypeExisted = 1064 //房价代码或房价名称已存在 + RateTypeNotExisted = 1065 //房价代码不存在 + FileNotExisted = 1066 //文件不存在 + RoomNoInvaild = 1067 //房间未启用 + ClassExisted = 1068 //班次已存在 + ClassNotExisted = 1069 //班次不存在 + CheckTimeError = 1070 //系统时间与营业时间不匹配 + CurrentClassIsShift = 1071 //当前班次已交班 + PayPriceError = 1072 //支付金额错误 + StockNotEnough = 1073 //存量不足 + DBSaveError = 1074 //数据存储错误 + DBAddError = 1075 //数据添加错误 + DBUpdateError = 1076 //数据更新错误 + DBDeleteError = 1077 //数据删除错误 + TimeError = 1078 //时间错误 + OrderInfoError = 1079 //预定信息错误 + NotVaildError = 1080 //不允许 + Overdue = 1081 //已过期 + MaxOverError = 1082 //超过最大值 + MinOverError = 1083 //低于最小值 + ExistedError = 1084 //已存在 + NotBindError = 1085 //未绑定 + BindError = 1086 //绑定失败 +) + +//消息翻译 +var MessageMap = map[int]string{ + NormalMessageId: "", + ServerMaintenance: "服务器维护中 请稍后再试", + AccountDisabled: "帐号被禁用", + AppidOverdue: "appid过期", + + UnknownError: "未知错误", + TokenFailure: "Token失效", + HTMLSuccess: "成功", + BlockingAcess: "禁止访问", + + NewReport: "新消息返回", + NewHeart: "心跳消息", + + ParameterInvalid: "参数无效", + AppidParameterInvalid: "授权参数无效", + EncryptCheckError: "密文校验失败", + UserNameDoNotExist: "用户名不存在或密码错误", + DuplicateKeyError: "键值对重复", + NotFindError: "未找到", + InValidOp: "无效操作", + InValidAuthorize: "授权码错误或未找到", + HasusedError: "已被使用", + HasActvError: "已被激活", + ActvFailure: "激活码被禁止使用", + UserExisted: "用户已存在", + VerifyTimeError: "验证码请求过于平凡", + MailSendFaild: "邮箱发送失败", + SMSSendFaild: "手机发送失败", + PhoneParameterError: "手机号格式有问题", + VerifyError: "验证码错误", + UserNotExisted: "用户不存在", + TopicExisted: "Topic已存在", + TopicNotExisted: "Topic不存在", + BundleIdNotExisted: "Bundle_id不存在", + TopicStartFail: "Topic开启处理失败", + TopicTypeNotExisted: "Topic处理类型不存在", + TopicIsNotNull: "Topic不能为空", + DeviceNotExisted: "设备不存在", + StateExisted: "状态已存在", + LastMenuNotExisted: "父菜单或根菜单不存在", + MenuNotExisted: "菜单不存在", + UserMenuNotExisted: "用户权限不存在", + DeviceIdNotExisted: "设备ID不存在", + GoodsDealTypeNotExisted: "商品处理类型不存在", + GoodsIdNotExisted: "商品不存在", + GoodsBeInDiscount: "商品正在折扣期", + GoodsPayTypeNotExisted: "商品可支付类型不存在", + GoodsIdExisted: "商品已存在", + OrderIdNotExisted: "订单不存在", + GoodsBeNotInDiscount: "商品未打折", + NotifyIsNotMatch: "会话不匹配", + GoodsIsDiscountRecovery: "商品已恢复原价", + InvitationUserNotExisted: "邀请用户不存在", + // InvitationUserLevelIsFull: "邀请用户级数已满", + UserNotAuthorize: "用户未授权", + ApplicantIsExisted: "申请人已存在", + ApplicantNotExisted: "申请人不存在", + CreditOrderNotVaild: "订单无效", + RepeatWxWithdraw: "微信零钱一天内多次提现", + WxWithdrawAmountError: "微信零钱提现金额错误", + WxWithdrawError: "微信零钱提现失败", + RepeatSubmission: "重复提交", + BundleExisted: "bundle已存在", + AuthExisted: "权限已存在", + AuthNotExisted: "权限不存在", + RoomTypeNotExisted: "房型不存在", + RoomTypeExisted: "房型已存在", + RoomNoNotExisted: "房间不存在", + RoomNoExisted: "房间已存在", + RateTypeExisted: "房价代码或房价名称已存在", + RateTypeNotExisted: "房价代码不存在", + FileNotExisted: "文件不存在", + RoomNoInvaild: "房间未启用", + ClassExisted: "班次已存在", + ClassNotExisted: "班次不存在", + CheckTimeError: "系统时间与营业时间不匹配", + CurrentClassIsShift: "当前班次已完成交班", + PayPriceError: "支付金额错误", + StockNotEnough: "存量不足", + DBSaveError: "数据存储错误", + DBAddError: "数据添加错误", + DBUpdateError: "数据更新错误", + DBDeleteError: "数据删除错误", + TimeError: "时间错误", + OrderInfoError: "预定信息有误", + NotVaildError: "不允许", + Overdue: "已过期", + MaxOverError: "超过最大值", + MinOverError: "低于最小值", + ExistedError: "已存在", + NotBindError: "未绑定", + BindError: "绑定失败", +} + +// +type MessageBody struct { + State bool `json:"state"` + Code int `json:"code,omitempty"` + Error string `json:"error,omitempty"` + Data interface{} `json:"data,omitempty"` +} + +//获取错误消息 参数(int,string) +func GetErrorMsg(errorCode ...interface{}) (msg MessageBody) { + if len(errorCode) == 0 { + log.Println("未知") + msg.State = false + msg.Code = -1 + return + } + msg.State = false + for _, e := range errorCode { + switch v := e.(type) { + case int: + msg.Code = int(int(v)) + msg.Error = MessageMap[msg.Code] + case string: + msg.Error = string(v) + fmt.Println(v) + case interface{}: + { + if config.OnIsDev() { + msg.Error = onCheckParam(v) + } + } + } + } + + return +} + +func onCheckParam(op interface{}) string { + //过滤可不填项 + b, _ := json.Marshal(op) + + js, _ := simplejson.NewJson(b) + + mp, _ := js.Map() + for k, v := range mp { + tmp := tools.AsString(v) + if len(tmp) > 0 && tmp != "0" { //过滤 + delete(mp, k) + } + } + //----------------------end + + b, _ = json.Marshal(mp) + return string(b) +} + +//成功消息 +func GetSuccessMsg(errorCode ...int) (msg MessageBody) { + msg.State = true + if len(errorCode) == 0 { + msg.Code = NormalMessageId + } else { + msg.Code = errorCode[0] + } + + msg.Error = MessageMap[msg.Code] + return +} diff --git a/message/msg_test.go b/message/msg_test.go new file mode 100644 index 0000000..c12315a --- /dev/null +++ b/message/msg_test.go @@ -0,0 +1,20 @@ +package message + +import ( + "fmt" + "testing" +) + +type Test struct { + State bool `json:"state"` + Code int `json:"code"` + Error string `json:"error"` +} + +func Test_sing(t *testing.T) { + var test Test + test.State = true + test.Error = "" + + fmt.Println(onCheckParam(test)) +} diff --git a/myaes/myaes.go b/myaes/myaes.go new file mode 100644 index 0000000..eca29fc --- /dev/null +++ b/myaes/myaes.go @@ -0,0 +1,53 @@ +package myaes + +import "crypto/cipher" + +type Tobytes struct { + Cip cipher.Block + Pdgtext []byte +} + +//使用AES加密文本,加密的文本不能为空 +func (a *Tobytes) Encrypt(src []byte) (dst []byte) { + src = a.padding(src) + dst = make([]byte, len(src)) + var index int = 0 + for len(src) > 0 { + a.Cip.Encrypt(dst[index:index+a.Cip.BlockSize()], src) + index += a.Cip.BlockSize() + src = src[a.Cip.BlockSize():] + } + return dst +} + +//使用AES解密文本 +func (a *Tobytes) Decrypt(src []byte) (dst []byte) { + if len(src)%a.Cip.BlockSize() != 0 { + return src + } + dst = make([]byte, len(src)) + var index int = 0 + for len(src) > 0 { + a.Cip.Decrypt(dst[index:index+a.Cip.BlockSize()], src) + index += a.Cip.BlockSize() + src = src[a.Cip.BlockSize():] + } + return a.unpadding(dst) +} + +//使用AES加密文本的时候文本必须定长,即必须是16,24,32的整数倍, +func (a *Tobytes) padding(src []byte) (dst []byte) { + pdg := a.Cip.BlockSize() - len(src)%a.Cip.BlockSize() + p := a.Pdgtext[:pdg] + p[pdg-1] = byte(pdg) + return append(src, p...) +} + +//使用AES解密文本,解密收删除padding的文本 +func (a *Tobytes) unpadding(src []byte) (dst []byte) { + length := len(src) + if length <= 0 { + return src + } + return src[:(length - int(src[length-1]))] +} diff --git a/mycache/my_test.go b/mycache/my_test.go new file mode 100644 index 0000000..0b4c7e2 --- /dev/null +++ b/mycache/my_test.go @@ -0,0 +1,35 @@ +package mycache + +import ( + "fmt" + "testing" + "time" +) + +// Tweet is a structure used for serializing/deserializing data in Elasticsearch. +type Tweet struct { + User string `json:"user"` + Message string `json:"message"` + Retweets int `json:"retweets"` + Image string `json:"image,omitempty"` + Created time.Time `json:"created,omitempty"` + Tags []string `json:"tags,omitempty"` + Location string `json:"location,omitempty"` +} + +func Test_cache(t *testing.T) { + //获取 + cache := OnGetCache("_cache") + var tp interface{} + tp, b := cache.Value("key") + if b { + tmp := tp.(Tweet) + fmt.Println(tmp) + } else { + var tmp Tweet + //添加 + cache.Add("key", tmp, 24*time.Hour) + } + + return +} diff --git a/mycache/mycache.go b/mycache/mycache.go new file mode 100644 index 0000000..a31d089 --- /dev/null +++ b/mycache/mycache.go @@ -0,0 +1,77 @@ +/* + key/value 内存缓存,支持基于超时的自动无效功能 +*/ +package mycache + +import ( + "time" + + "github.com/muesli/cache2go" +) + +type MyCache struct { + cache *cache2go.CacheTable +} + +/* + 初始化一个cache + cachename 缓存名字 +*/ +func OnGetCache(cachename string) (mc MyCache) { + mc.cache = cache2go.Cache(cachename) + return +} + +/* + 添加一个缓存 + lifeSpan:缓存时间,0表示永不超时 +*/ +func (mc *MyCache) Add(key interface{}, value interface{}, lifeSpan time.Duration) *cache2go.CacheItem { + return mc.cache.Add(key, lifeSpan, value) +} + +/* + 查找一个cache + value 返回的值 +*/ + +func (mc *MyCache) Value(key interface{}) (value interface{}, b bool) { + b = false + res, err := mc.cache.Value(key) + if err == nil { + value = res.Data() + b = true + return + } + return +} + +/* + 判断key是否存在 +*/ +func (mc *MyCache) IsExist(key interface{}) bool { + return mc.cache.Exists(key) +} + +/* + 删除一个cache +*/ +func (mc *MyCache) Delete(key interface{}) error { + _, err := mc.cache.Delete(key) + return err +} + +/* + 获取原始cache2go操作类 +*/ +func (mc *MyCache) GetCache2go() *cache2go.CacheTable { + return mc.cache +} + +/* + 清空表內容 +*/ +func (mc *MyCache) Clear() bool { + mc.cache.Flush() + return true +} diff --git a/mycatch/mycatch.go b/mycatch/mycatch.go new file mode 100644 index 0000000..c209658 --- /dev/null +++ b/mycatch/mycatch.go @@ -0,0 +1,54 @@ +/* + 错误信息日志记录(panic信息,不可度量的) + 注意:每个 goroutine 开始部分都需要写上:defer mycatch.Dmp() + 类似于 + go func(){ + defer mycatch.Dmp() + }() +*/ +package mycatch + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime/debug" + "time" +) + +func Dmp() { + errstr := "" + if err := recover(); err != nil { + errstr += (fmt.Sprintf("%v\r\n", err)) //输出panic信息 + errstr += ("--------------------------------------------\r\n") + } + + errstr += (string(debug.Stack())) //输出堆栈信息 + OnPrintErr(errstr) +} + +func OnPrintErr(errstring string) { + file, _ := exec.LookPath(os.Args[0]) + path, _ := filepath.Abs(file) + path = filepath.Dir(path) + + now := time.Now() //获取当前时间 + pid := os.Getpid() //获取进程ID + + os.MkdirAll(path+"/err", os.ModePerm) //生成多级目录 + + time_str := now.Format("2006-01-02") //设定时间格式 + fname := fmt.Sprintf("%s/err/panic_%s-%x.log", path, time_str, pid) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒) + fmt.Println("panic to file ", fname) + + f, err := os.OpenFile(fname, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) + if err != nil { + return + } + defer f.Close() + + f.WriteString("=========================" + now.Format("2006-01-02 15:04:05 ========================= \r\n")) + f.WriteString(errstring) //输出堆栈信息 + f.WriteString("=========================end=========================") +} diff --git a/mydef/mydef.go b/mydef/mydef.go new file mode 100644 index 0000000..acd48db --- /dev/null +++ b/mydef/mydef.go @@ -0,0 +1,3 @@ +package mydef + +type ParamsCallFunc func(...interface{}) diff --git a/mydraw/my_test.go b/mydraw/my_test.go new file mode 100644 index 0000000..16676fc --- /dev/null +++ b/mydraw/my_test.go @@ -0,0 +1,59 @@ +package mydraw + +import ( + "fmt" + "public/mydraw" + "testing" +) + +func TestMytest(t *testing.T) { + pen, b := mydraw.OnGetPen("./luximr.ttf", 0, 0, 0, 255) + if b { + var hdc mydraw.HDC + hdc.SetBg("./src.png") + pen.Dpi = 200 + pen.FontSize = 16 + pen.StartPoint.X = 150 + pen.StartPoint.Y = 78 + hdc.DrawText(pen, "哈哈") + pen.FontSize = 12 + pen.StartPoint.X = 150 + pen.StartPoint.Y = 160 + hdc.DrawText(pen, "男") + pen.StartPoint.X = 350 + pen.StartPoint.Y = 160 + hdc.DrawText(pen, "汉") + pen.StartPoint.X = 150 + pen.StartPoint.Y = 240 + hdc.DrawText(pen, "1996") + pen.StartPoint.X = 300 + pen.StartPoint.Y = 240 + hdc.DrawText(pen, "6") + pen.StartPoint.X = 370 + pen.StartPoint.Y = 240 + hdc.DrawText(pen, "26") + + pen.StartPoint.X = 150 + pen.StartPoint.Y = 275 + + str := []rune("北京市海淀区西北旺东路100号中关村科技园") + //str := "北京市海淀区西北旺东路100号中关村科技园" + + for i := 0; i < len(str); i += 11 { + var end = i + 11 + if end > len(str) { + end = len(str) + } + pen.StartPoint.Y += 40 + tmp := str[i:end] + hdc.DrawText(pen, string(tmp)) + } + + pen.StartPoint.X = 300 + pen.StartPoint.Y = 520 + hdc.DrawText(pen, "310666196606266666") + + b = hdc.Save("./out.png") + fmt.Println(b) + } +} diff --git a/mydraw/mydraw.go b/mydraw/mydraw.go new file mode 100644 index 0000000..4d7073d --- /dev/null +++ b/mydraw/mydraw.go @@ -0,0 +1,126 @@ +package mydraw + +import ( + "fmt" + "image" + "image/color" + "image/draw" + "image/jpeg" + "image/png" + "io/ioutil" + "log" + "os" + "public/mylog" + "strings" + + "github.com/golang/freetype" + "github.com/golang/freetype/truetype" +) + +type Pen struct { + FontSize float64 + Dpi float64 + Font *truetype.Font + StartPoint image.Point + Color *image.Uniform +} + +type HDC struct { + //Bg image.Image + Rgba *image.RGBA +} + +//获取画笔 +func OnGetPen(fontPath string, R, G, B, A uint8) (pen Pen, b bool) { + b = false + pen.Color = image.NewUniform(color.RGBA{R: R, G: G, B: B, A: A}) + pen.Dpi = 72 + pen.FontSize = 10 + pen.StartPoint = image.Point{0, 0} + // 读字体数据 + fontBytes, err := ioutil.ReadFile(fontPath) + if err != nil { + log.Println(err) + return + } + pen.Font, err = freetype.ParseFont(fontBytes) + if err != nil { + log.Println(err) + return + } + b = true + return +} + +func (this *HDC) SetBg(imagePath string) bool { + file, _ := os.Open(imagePath) + defer file.Close() + //var err error + img, _, err := image.Decode(file) + if err != nil { + fmt.Println("err = ", err) + return false + } + + this.Rgba = image.NewRGBA(img.Bounds()) + draw.Draw(this.Rgba, this.Rgba.Bounds(), img, image.ZP, draw.Src) + return true +} + +func (this *HDC) GetBgSize() (w, h int) { + b := this.Rgba.Bounds() + w = b.Max.X + h = b.Max.Y + return +} + +//图片上画文字 +func (this *HDC) DrawText(pen Pen, text string) bool { + if this.Rgba == nil { + return false + } + + c := freetype.NewContext() + c.SetDPI(pen.Dpi) + c.SetFont(pen.Font) + c.SetFontSize(pen.FontSize) + c.SetClip(this.Rgba.Bounds()) + c.SetDst(this.Rgba) + //c.SetSrc(image.NewUniform(color.RGBA{255, 255, 255, 255})) + c.SetSrc(pen.Color) + + // Draw the text. + pt := freetype.Pt(pen.StartPoint.X, pen.StartPoint.Y+int(c.PointToFixed(pen.FontSize)>>6)) + for _, s := range strings.Split(text, "\r\n") { + _, err := c.DrawString(s, pt) + if err != nil { + mylog.Println("c.DrawString(%s) error(%v)", s, err) + return false + } + pt.Y += c.PointToFixed(pen.FontSize * 1.5) + } + return false +} + +//保存图片 +func (this *HDC) Save(imagePath string) bool { + output, err := os.OpenFile(imagePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + if err != nil { + mylog.Error(err) + return false + } + defer output.Close() + + if strings.HasSuffix(imagePath, ".png") || strings.HasSuffix(imagePath, ".PNG") { + err = png.Encode(output, this.Rgba) + } else { + err = jpeg.Encode(output, this.Rgba, nil) + } + if err != nil { + + mylog.Println("image encode error(%v)", err) + //mylog.Error(err) + return false + } + return true +} diff --git a/myecharts/common.go b/myecharts/common.go new file mode 100644 index 0000000..4946608 --- /dev/null +++ b/myecharts/common.go @@ -0,0 +1,50 @@ +package myecharts + +func getDefaltOption() EcOption { + return EcOption{ + XAxis: EcxAxis{Type: "category"}, + YAxis: EcxAxis{Type: "value"}, + Legend: EcLegend{Show: true}, + } +} + +// 设置显示x轴还是y轴,且设置数据内容 +//data 为轴坐标内容 +func (ep *EcOption) OnSetAxis(data []string, axisLabel AxisLabel, isy bool) { + V := EcxAxis{Type: "value", AxisLabel: axisLabel} + k := EcxAxis{Type: "category", Data: data} + if isy { + ep.YAxis = k + ep.XAxis = V + } else { + ep.XAxis = k + ep.YAxis = V + } +} + +//图例组件。(顶部分类器) +func (ep *EcOption) OnSetLegend(data []string) { + ep.Legend.Data = data + ep.Legend.Show = true +} + +//添加一个数据 +func (ep *EcOption) OnAddOneSeries(name string, data interface{}) { + var tmp = EcSeries{Name: name, Type: ep._type, Data: data} + ep.Series = append(ep.Series, tmp) +} + +//添加一个数据 +func (ep *EcOption) OnAddOneSeriesInt(name string, data []int) { + var tmp []interface{} + for _, v := range data { + tmp = append(tmp, float32(v)) + } + ep.OnAddOneSeries(name, tmp) +} + +// +func (ep *EcOption) SetEcToolTip(ecToolTip EcToolTip) { + //EcToolTip{Show: true, Trigger: "axis", AxisPointer: EcAxisPointer{Type: "shadow"}} + ep.Tooltip = ecToolTip +} diff --git a/myecharts/def.go b/myecharts/def.go new file mode 100644 index 0000000..1a5db7f --- /dev/null +++ b/myecharts/def.go @@ -0,0 +1,90 @@ +package myecharts + +//echarts option 根数据 +type EcOption struct { + Color []string `json:"color,omitempty"` //调色盘颜色列表。如果系列没有设置颜色,则会依次循环从该列表中取颜色作为系列颜色。 + //标题组件,包含主标题和副标题。 + Title EcTitle `json:"title,omitempty"` + Tooltip EcToolTip `json:"tooltip,omitempty"` + Grid EcGrid `json:"grid,omitempty"` + XAxis EcxAxis `json:"xAxis,omitempty"` //直角坐标系 grid 中的 x 轴 + YAxis EcxAxis `json:"yAxis,omitempty"` //直角坐标系 grid 中的 y 轴 + Legend EcLegend `json:"legend,omitempty"` //图例组件。 + Series []EcSeries `json:"series,omitempty"` //数据内容 + Toolbox Toolbox `json:"toolbox,omitempty"` // + _type string +} + +//系列列表。每个系列通过 type 决定自己的图表类型 +type EcSeries struct { + Name string `json:"name,omitempty"` //系列名称 + Type string `json:"type,omitempty"` //line:线 bar:柱状图 + Data interface{} `json:"data,omitempty"` //系列中的数据内容数组 +} + +//图例组件。 +type EcLegend struct { + Show bool `json:"show,omitempty"` + Data []string `json:"data,omitempty"` //图例的数据数组 +} + +//直角坐标系内绘图网格 +type EcxAxis struct { + Type string `json:"type,omitempty"` + Data []string `json:"data,omitempty"` + AxisLabel AxisLabel `json:"axisLabel,omitempty"` +} + +// +type AxisLabel struct { + Formatter string `json:"formatter,omitempty"` +} + +//直角坐标系内绘图网格 +type EcGrid struct { + Left string `json:"left,omitempty"` //组件离容器左侧的距离。 + Right string `json:"right,omitempty"` + Bottom string `json:"bottom,omitempty"` + ContainLabel bool `json:"containLabel,omitempty"` //grid 区域是否包含坐标轴的刻度标签 +} + +//提示框组件。 +type EcToolTip struct { + Show bool `json:"show,omitempty"` + /* + 触发类型。 + 'item' 数据项图形触发,主要在散点图,饼图等无类目轴的图表中使用。 + 'axis' 坐标轴触发,主要在柱状图,折线图等会使用类目轴的图表中使用。 + 'none' 什么都不触发。 + */ + Trigger string `json:"trigger,omitempty"` + //坐标轴指示器配置项。 + AxisPointer EcAxisPointer `json:"axisPointer,omitempty"` +} + +//坐标轴指示器配置项。 +type EcAxisPointer struct { + /* + 'line' 直线指示器 + 'shadow' 阴影指示器 + 'none' 无指示器 + 'cross' 十字准星指示器 + */ + Type string `json:"type,omitempty"` +} + +//标题组件,包含主标题和副标题。 +type EcTitle struct { + Text string `json:"text,omitempty"` //主标题文本,支持使用 \n 换行。 + SubText string `json:"subtext,omitempty"` //副标题文本,支持使用 \n 换行。 +} + +// +type Toolbox struct { + Show bool `json:"show,omitempty"` // + Feature Feature `json:"feature,omitempty"` // +} + +// +type Feature struct { +} diff --git a/myecharts/myecharts.go b/myecharts/myecharts.go new file mode 100644 index 0000000..5c6af07 --- /dev/null +++ b/myecharts/myecharts.go @@ -0,0 +1,26 @@ +package myecharts + +//获取线图基础数据 +/* + t_str + line:线 + bar:柱 + pie:饼 + scatter:散点(气泡) effectScatter:带有涟漪特效动画的散点 radar:雷达 + tree:树 treemap:面积 sunburst:旭日图 boxplot:箱形图 candlestick:K线图 sankey:桑基图 + heatmap:热力图 map:地图 parallel: 平行坐标系的系列 lines:线图 graph:关系图 + funnel:漏斗图 gauge:仪表盘 pictorialBar:象形柱图 themeRiver:主题河流 custom:自定义系列 +*/ +func OnGetBaseInfo(t_str string) (ec EcOption) { + ec = getDefaltOption() + ec._type = t_str + return ec +} + +/* + 获取基础柱状图 +*/ +func OnGetBarInfo(ecToolTip EcToolTip) (ec EcOption) { + ec = OnGetBaseInfo("bar") + return ec +} diff --git a/myelastic/myelastic.go b/myelastic/myelastic.go new file mode 100644 index 0000000..6d5da26 --- /dev/null +++ b/myelastic/myelastic.go @@ -0,0 +1,391 @@ +package myelastic + +import ( + "context" + "encoding/json" + "errors" + "log" + + "reflect" + "strings" + "time" + + "github.com/olivere/elastic" +) + +// +type MyElastic struct { + Client *elastic.Client + Err error + Ctx context.Context +} + +// +func OnInitES(url string) MyElastic { + var es MyElastic + es.Ctx = context.Background() + es.Client, es.Err = elastic.NewClient(elastic.SetURL(url)) + if es.Err != nil { + log.Println(es.Err) + //mylog.Error(es.Err) + //panic(es.Err) + } + + return es +} + +//func (es *MyElastic) Model(refs interface{}) *MyElastic { +// if reflect.ValueOf(refs).Type().Kind() != reflect.Ptr { +// mylog.Println("Model: attempt to Model into a pointer") +// panic(0) +// } + +// es.Element = refs +// return es +//} + +/* +创建索引(相当于数据库) +mapping 如果为空("")则表示不创建模型 +*/ +func (es *MyElastic) CreateIndex(index_name, mapping string) (result bool) { + es.Err = nil + exists, err := es.Client.IndexExists(index_name).Do(es.Ctx) + if err != nil { + es.Err = err + log.Println(es.Err) + return false + } + + if !exists { + var re *elastic.IndicesCreateResult + if len(mapping) == 0 { + re, es.Err = es.Client.CreateIndex(index_name).Do(es.Ctx) + } else { + re, es.Err = es.Client.CreateIndex(index_name).BodyString(mapping).Do(es.Ctx) + } + + if es.Err != nil { + log.Println(es.Err) + return false + } + + return re.Acknowledged + } + + return false +} + +/* + 排序查询 + 返回json数据集合 +*/ +func (es *MyElastic) SortQuery(index_name string, builder []elastic.Sorter, query []elastic.Query) (bool, []string) { + + searchResult := es.Client.Search().Index(index_name) + + if len(builder) > 0 { + for _, v := range builder { + searchResult = searchResult.SortBy(v) + } + } + if len(query) > 0 { + for _, v := range query { + searchResult = searchResult.Query(v) + } + } + es_result, err := searchResult.Do(es.Ctx) // execute + if err != nil { + log.Println(es.Err) + return false, nil + } + //log.Println("Found a total of %d entity\n", es_result.TotalHits()) + + if es_result.Hits.TotalHits > 0 { + var result []string + //log.Println("Found a total of %d entity\n", searchResult.Hits.TotalHits) + for _, hit := range es_result.Hits.Hits { + + result = append(result, string(*hit.Source)) + + } + return true, result + } else { + // No hits + return true, nil + } +} + +/* + 排序查询 + 返回原始Hit + builder:排序 + agg:聚合 类似group_by sum + query:查询 +*/ +func (es *MyElastic) SortQueryReturnHits(index_name string, from, size int, builder []elastic.Sorter, query []elastic.Query) (bool, []*elastic.SearchHit) { + + searchResult := es.Client.Search().Index(index_name) + + if len(builder) > 0 { + for _, v := range builder { + searchResult = searchResult.SortBy(v) + } + } + if len(query) > 0 { + for _, v := range query { + searchResult = searchResult.Query(v) + } + } + if size > 0 { + searchResult = searchResult.From(from) + searchResult = searchResult.Size(size) + } + es_result, err := searchResult.Do(es.Ctx) // execute + if err != nil { + log.Println(es.Err) + return false, nil + } + + // log.Println("wwwwww", es_result.Aggregations) + if es_result.Hits.TotalHits > 0 { + + return true, es_result.Hits.Hits + } else { + + return true, nil + } +} + +/* +添加记录,覆盖添加 +*/ +func (es *MyElastic) Add(index_name, type_name, id string, data interface{}) (result bool) { + result = false + // Index a tweet (using JSON serialization) + if len(id) > 0 { + _, es.Err = es.Client.Index(). + Index(index_name). + Type(type_name). + Id(id). + BodyJson(data). + Do(es.Ctx) + } else { + _, es.Err = es.Client.Index(). + Index(index_name). + Type(type_name). + BodyJson(data). + Do(es.Ctx) + } + + if es.Err != nil { + log.Println(es.Err) + return false + } + _, es.Err = es.Client.Flush().Index(index_name).Do(es.Ctx) + if es.Err != nil { + log.Println(es.Err) + return false + } + return true +} + +/* +添加记录,覆盖添加 +index_name +type_name +query interface{} //查询条件 +out *[]Param //查询结果 +*/ +func (es *MyElastic) SearchMap(index_name, type_name string, query interface{}, out *[]map[string]interface{}) (result bool) { + es_search := es.Client.Search() + if len(type_name) > 0 { + es_search = es_search.Type(type_name) + } + if len(index_name) > 0 { + es_search = es_search.Index(index_name) + } + var es_result *elastic.SearchResult + es_result, es.Err = es_search.Source(query).Do(es.Ctx) + if es.Err != nil { + log.Println(es.Err) + return false + } + if es_result.Hits == nil { + log.Println(errors.New("expected SearchResult.Hits != nil; got nil")) + return false + } + + for _, hit := range es_result.Hits.Hits { + tmp := make(map[string]interface{}) + err := json.Unmarshal(*hit.Source, &tmp) + if err != nil { + log.Println(es.Err) + } else { + *out = append(*out, tmp) + } + } + + return true +} + +/* +添加记录,覆盖添加 +index_name +type_name +query interface{} //查询条件 +out *[]Param //查询结果 +*/ +func (es *MyElastic) Search(index_name, type_name string, query interface{}, out interface{}) (result bool) { + + sliceValue := reflect.Indirect(reflect.ValueOf(out)) + + if sliceValue.Kind() != reflect.Slice { + log.Println(errors.New("needs a pointer to a slice")) + return false + } + + sliceElementType := sliceValue.Type().Elem() + es_search := es.Client.Search() + if len(type_name) > 0 { + es_search = es_search.Type(type_name) + } + if len(index_name) > 0 { + es_search = es_search.Index(index_name) + } + var es_result *elastic.SearchResult + es_result, es.Err = es_search.Source(query).Do(es.Ctx) + if es.Err != nil { + log.Println(es.Err) + return false + } + if es_result.Hits == nil { + log.Println(errors.New("expected SearchResult.Hits != nil; got nil")) + return false + } + + for _, hit := range es_result.Hits.Hits { + newValue := reflect.New(sliceElementType) + + item := make(map[string]interface{}) + err := json.Unmarshal(*hit.Source, &item) + //fmt.Println(string(*hit.Source)) + + err = scanMapIntoStruct(newValue.Interface(), item) + if err != nil { + log.Println(err) + } + + if err != nil { + log.Println(err) + } else { + sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(newValue.Interface())))) + //out = append(out, tmp) + } + } + + return true +} + +func scanMapIntoStruct(obj interface{}, objMap map[string]interface{}) error { + dataStruct := reflect.Indirect(reflect.ValueOf(obj)) + if dataStruct.Kind() != reflect.Struct { + return errors.New("expected a pointer to a struct") + } + + dataStructType := dataStruct.Type() + + for i := 0; i < dataStructType.NumField(); i++ { + field := dataStructType.Field(i) + fieldv := dataStruct.Field(i) + + err := scanMapElement(fieldv, field, objMap) + if err != nil { + return err + } + } + + return nil +} + +func scanMapElement(fieldv reflect.Value, field reflect.StructField, objMap map[string]interface{}) error { + + objFieldName := field.Name + bb := field.Tag + sqlTag := bb.Get("json") + + if bb.Get("json") == "-" || sqlTag == "-" || reflect.ValueOf(bb).String() == "-" { + return nil + } + + sqlTags := strings.Split(sqlTag, ",") + sqlFieldName := objFieldName + if len(sqlTags[0]) > 0 { + sqlFieldName = sqlTags[0] + } + + data, ok := objMap[sqlFieldName] + if !ok || data == nil { + return nil + } + + // fmt.Println("================") + // fmt.Println(field.Type.Kind()) + // fmt.Println(sqlFieldName) + var v interface{} + switch field.Type.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x := int(data.(float64)) + v = x + case reflect.Slice: + if fieldv.Type().String() == "[]uint8" { + x := []byte(data.(string)) + v = x + } else if fieldv.Type().String() == "[]string" { + mp := data.([]interface{}) + var ss []string + for _, v := range mp { + ss = append(ss, v.(string)) + } + v = ss + } else if fieldv.Type().String() == "[]int" { + mp := data.([]interface{}) + var ss []int + for _, v := range mp { + ss = append(ss, int(v.(float64))) + } + v = ss + + } else { + v = data + } + + case reflect.Struct: + if fieldv.Type().String() == "time.Time" { + x, err := time.Parse("2006-01-02 15:04:05", data.(string)) + if err != nil { + x, err = time.Parse("2006-01-02 15:04:05.000 -0700", data.(string)) + if err != nil { + if err != nil { + x, err = time.Parse("2006-01-02T15:04:05.999999999Z07:00", data.(string)) + if err != nil { + return errors.New("unsupported time format: " + data.(string)) + } + } + } + } + + v = x + } else { + v = data + } + + default: + v = data + } + + fieldv.Set(reflect.ValueOf(v)) + // fmt.Println("================") + + return nil +} diff --git a/myelastic/test.rar b/myelastic/test.rar new file mode 100644 index 0000000..d09e3d9 Binary files /dev/null and b/myelastic/test.rar differ diff --git a/myfile/myfile.go b/myfile/myfile.go new file mode 100644 index 0000000..9a9b025 --- /dev/null +++ b/myfile/myfile.go @@ -0,0 +1,87 @@ +package myfile + +import ( + "fmt" + "io" + "net/http" + "os" + "public/tools" + "time" +) + +//上传单个文件 +func UploadOneFile(r *http.Request, field, file_type, dir string) (result bool, file_name string) { + //接受post请求 + if r.Method == "POST" { + r.ParseMultipartForm(32 << 20) + file_name = getFileName(file_type) + //开始存储文件 + { + file, _, err := r.FormFile(field) //文件name + defer file.Close() + if err != nil { + result = false + } + + if !tools.CheckFileIsExist(tools.GetModelPath() + "/file/" + dir + "/") { + err1 := os.Mkdir(tools.GetModelPath()+"/file/"+dir+"/", os.ModePerm) //创建文件夹 + if err1 != nil { + result = false + } + } + + f, err := os.OpenFile(tools.GetModelPath()+"/file/"+dir+"/"+file_name, os.O_WRONLY|os.O_CREATE, 0666) + defer f.Close() + if err != nil { + result = false + } + io.Copy(f, file) + result = true + } + + } else { + result = false + } + return +} + +//多文件上传 +func UploadMoreFile(r *http.Request, field, file_type, dir string) (result bool, optionDirs []string) { + //接受post请求 + if r.Method == "POST" { + r.ParseMultipartForm(32 << 20) + files := r.MultipartForm.File[field] + l := len(files) + // optionDirs := make([]string, l) + for i := 0; i < l; i++ { + file, err := files[i].Open() + defer file.Close() + if err != nil { + result = false + } + file_name := getFileName(file_type) + + if !tools.CheckFileIsExist(tools.GetModelPath() + "/file/" + dir + "/") { + err1 := os.Mkdir(tools.GetModelPath()+"/file/"+dir+"/", os.ModePerm) //创建文件夹 + if err1 != nil { + result = false + } + } + f, err := os.OpenFile(tools.GetModelPath()+"/file/"+dir+"/"+file_name, os.O_WRONLY|os.O_CREATE, 0666) + defer f.Close() + if err != nil { + result = false + } + io.Copy(f, file) + optionDirs = append(optionDirs, file_name) + result = true + } + + } else { + result = false + } + return +} +func getFileName(exp string) string { + return fmt.Sprintf("%d%s.%s", tools.GetUtcTime(time.Now()), tools.GetRandomString(4), exp) +} diff --git a/myhttp/myfile.go b/myhttp/myfile.go new file mode 100644 index 0000000..81c2cc6 --- /dev/null +++ b/myhttp/myfile.go @@ -0,0 +1,132 @@ +package myhttp + +import ( + "bytes" + "data/config" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + "path" + "public/mylog" + "public/tools" + "time" +) + +/* +多文件上传 +dir:空则使用文件后缀做dir +*/ +func UploadMoreFile(r *http.Request, dir string) (result bool, optionDirs []string) { + //接受post请求 + if r.Method == "POST" { + r.ParseMultipartForm(32 << 20) + if r.MultipartForm == nil { + result = false + } else { + for _, files := range r.MultipartForm.File { + for _, v := range files { + var _dir = dir + file, _ := v.Open() + defer file.Close() + ext := getFileType(v.Filename) + if len(ext) == 0 { + continue + } + if len(_dir) == 0 { + _dir = ext + } + + abs_dir := tools.GetModelPath() + config.File_host + "/" + _dir + "/" + file_name := getFileName(ext) + if !tools.CheckFileIsExist(abs_dir) { + tools.BuildDir(abs_dir) + //err := os.MkdirAll(tools.GetModelPath()+config.File_host+"/"+_dir+"/", os.ModePerm) //生成多级目录 + } + + //存在则覆盖 + f, err := os.OpenFile(abs_dir+file_name, + os.O_WRONLY|os.O_CREATE, 0666) + defer f.Close() + if err != nil { + mylog.Error(err) + result = false + return + } + + io.Copy(f, file) + optionDirs = append(optionDirs, config.Url_host+config.File_host+"/"+_dir+"/"+file_name) + result = true + } + } + } + } else { + result = false + } + return +} + +func getFileName(exp string) string { + return fmt.Sprintf("%d%s.%s", tools.GetUtcTime(time.Now()), tools.GetRandomString(4), exp) +} + +//获取文件后缀 +func getFileType(exp string) string { + fileSuffix := path.Ext(exp) //获取文件后缀 + if len(fileSuffix) > 1 { + return fileSuffix[1:] + } + return "" +} + +//模拟客戶端文件上传 +//fieldname注意与服务器端保持一致 +func PostFile(filename, fieldname string, targetUrl string) (e error, result string) { + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + + //关键的一步操作 + fileWriter, err := bodyWriter.CreateFormFile(fieldname, filename) + if err != nil { + fmt.Println("error writing to buffer") + e = err + return + } + + //打开文件句柄操作 + fh, err := os.Open(filename) + if err != nil { + fmt.Println("error opening file") + e = err + return + } + defer fh.Close() + + //iocopy + _, err = io.Copy(fileWriter, fh) + if err != nil { + e = err + return + } + + contentType := bodyWriter.FormDataContentType() + bodyWriter.Close() + + resp, err := http.Post(targetUrl, contentType, bodyBuf) + if err != nil { + e = err + return + } + defer resp.Body.Close() + resp_body, err := ioutil.ReadAll(resp.Body) + if err != nil { + e = err + return + } + fmt.Println(resp.Status) + fmt.Println(string(resp_body)) + result = string(resp_body) + return +} diff --git a/myhttp/myhttp.go b/myhttp/myhttp.go new file mode 100644 index 0000000..69d22dc --- /dev/null +++ b/myhttp/myhttp.go @@ -0,0 +1,143 @@ +package myhttp + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "public/mylog" +) + +//发送修改密码 +func OnPostJson(url, jsonstr string) []byte { + //解析这个 URL 并确保解析没有出错。 + body := bytes.NewBuffer([]byte(jsonstr)) + resp, err := http.Post(url, "application/json;charset=utf-8", body) + if err != nil { + return []byte("") + } + defer resp.Body.Close() + body1, err1 := ioutil.ReadAll(resp.Body) + if err1 != nil { + return []byte("") + } + + return body1 +} + +//发送get 请求 +func OnGetJson(url, params string) string { + //解析这个 URL 并确保解析没有出错。 + var urls = url + if len(params) > 0 { + urls += "?" + params + } + resp, err := http.Get(urls) + if err != nil { + return "" + } + defer resp.Body.Close() + body1, err1 := ioutil.ReadAll(resp.Body) + if err1 != nil { + return "" + } + + return string(body1) +} + +//发送get 请求 返回对象 +func SendGet(url, params string, obj interface{}) bool { + //解析这个 URL 并确保解析没有出错。 + var urls = url + if len(params) > 0 { + urls += "?" + params + } + resp, err := http.Get(urls) + if err != nil { + mylog.Error(err) + return false + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + mylog.Error(err) + return false + } + log.Println((string(body))) + err = json.Unmarshal([]byte(body), &obj) + return err == nil +} + +//发送GET请求 +func SendGetEx(url string, reponse interface{}) bool { + resp, e := http.Get(url) + if e != nil { + mylog.Error(e) + return false + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + mylog.Error(e) + return false + } + mylog.Debug(string(body)) + err = json.Unmarshal(body, &reponse) + return err == nil +} + +//form 方式发送post请求 +func OnPostForm(url string, data url.Values) (body []byte) { + resp, err := http.PostForm(url, data) + if err != nil { + return + } + defer resp.Body.Close() + body, err = ioutil.ReadAll(resp.Body) + if err != nil { + return + } + + return +} + +//发送POST请求 +func SendPost(requestBody interface{}, responseBody interface{}, url string) bool { + postData, err := json.Marshal(requestBody) + client := &http.Client{} + req, _ := http.NewRequest("POST", url, bytes.NewReader(postData)) + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json;charset=utf-8") + // req.Header.Add("Authorization", authorization) + resp, e := client.Do(req) + if e != nil { + mylog.Error(e) + return false + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + mylog.Error(e) + return false + } + // result := string(body) + mylog.Debug(string(body)) + + err = json.Unmarshal(body, &responseBody) + return err == nil +} + +//像指定client 发送json 包 +//msg message.MessageBody +func WriteJson(w http.ResponseWriter, msg interface{}) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + js, err := json.Marshal(msg) + if err != nil { + panic(err) + } + fmt.Fprintf(w, string(js)) +} diff --git a/myleveldb/my_test.go b/myleveldb/my_test.go new file mode 100644 index 0000000..e85431e --- /dev/null +++ b/myleveldb/my_test.go @@ -0,0 +1,64 @@ +package myleveldb + +import ( + "fmt" + "public/myleveldb" + "testing" + "time" +) + +// Tweet is a structure used for serializing/deserializing data in Elasticsearch. +type Tweet struct { + User string `json:"user"` + Message string `json:"message"` + Retweets int `json:"retweets"` + Image string `json:"image,omitempty"` + Created time.Time `json:"created,omitempty"` + Tags []string `json:"tags,omitempty"` + Location string `json:"location,omitempty"` +} + +func Test_order(t *testing.T) { + fmt.Println("ssss") + //初始化db + ldb := myleveldb.OnInitDB("./database") + defer ldb.OnDestoryDB() + + // var www Tweet + // www.Location = "xiexiaojun" + // www.Created = time.Now() + // www.Tags = append(www.Tags, "12334444", "ssss", "wwwww") + // b := ldb.Add([]byte("wwww"), www) + // fmt.Println(b) + // fmt.Println(www.Tags) + + var eee Tweet + bb := ldb.Get("wwww", &eee) + fmt.Println(bb) + fmt.Println(eee.Location) + fmt.Println(eee.Created) + fmt.Println(eee.Tags) + + // var temp []myleveldb.Param + // for i := 0; i < 10; i++ { + // var www Tweet + // www.Location = "xiexiaojun" + strconv.Itoa(i) + // www.Created = time.Now() + // temp = append(temp, myleveldb.Param{"key" + strconv.Itoa(i), www}) + // } + //bbb := ldb.AddList(temp) + //fmt.Println(bbb) + + bb = ldb.Get("key9", &eee) + fmt.Println(bb) + fmt.Println(eee.Location) + fmt.Println(eee.Created) + fmt.Println(eee.Tags) + + var tmp []myleveldb.Param + bb = ldb.Model(&Tweet{}).Find(&tmp, "key3", "key7") //查找 + + fmt.Println(tmp) + + return +} diff --git a/myleveldb/myleveldb.go b/myleveldb/myleveldb.go new file mode 100644 index 0000000..906d2b3 --- /dev/null +++ b/myleveldb/myleveldb.go @@ -0,0 +1,172 @@ +package myleveldb + +import ( + "public/mylog" + "public/tools" + "reflect" + + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/util" +) + +type Param struct { + Key string + Value interface{} +} + +func OnInitDB(dataSourceName string) MyLevelDB { + var L MyLevelDB + L.DB, L.E = leveldb.OpenFile(dataSourceName, nil) + if L.E != nil { + mylog.Error(L.E) + } + // L.op = &opt.ReadOptions{ + // false, + // opt.NoStrict, + // } + return L +} + +type MyLevelDB struct { + DB *leveldb.DB + E error + //op *opt.ReadOptions + Value interface{} +} + +func (L *MyLevelDB) OnDestoryDB() { + if L.DB != nil { + L.DB.Close() + L.DB = nil + } +} + +//获取数据 +func (L *MyLevelDB) Get(key string, value interface{}) (b bool) { + if L.DB != nil { + var err error + var by []byte + if by, err = L.DB.Get([]byte(key), nil /*L.op*/); err != nil { + //mylog.Error(err) + } else { + if err := tools.DecodeByte(by, value); err != nil { + //错误处理 + mylog.Error(err) + } else { + return true + } + } + } + + return false +} + +func (L *MyLevelDB) Model(refs interface{}) *MyLevelDB { + if reflect.ValueOf(refs).Type().Kind() == reflect.Ptr { + mylog.Println("Model: attempt to Model into a non-pointer") + panic(0) + } + L.Value = refs + return L +} + +//模糊查找 +/* + t value的类型 + values 为返回结果 + args 传一个参数:表示模糊搜索 + args 传2个参数:表示范围搜索 +*/ +func (L *MyLevelDB) Find(values *[]Param, args ...string) (b bool) { + if L.DB != nil && L.Value != nil { + n := len(args) + var it iterator.Iterator + + if n == 1 { //模糊查找 + it = L.DB.NewIterator(util.BytesPrefix([]byte(args[0])), nil) + } else { + it = L.DB.NewIterator(&util.Range{Start: []byte(args[0]), Limit: []byte(args[1])}, nil) + } + + for it.Next() { + tmp := L.Value + if err := tools.DecodeByte(it.Value(), tmp); err != nil { + //错误处理 + mylog.Error(err) + } + *values = append(*values, Param{string(it.Key()), tmp}) + } + + it.Release() + //iter := L.DB.NewIterator(nil, nil) + } else { + if L.Value == nil { + panic("not call Model()") + } + mylog.Print(mylog.Log_Error, "not init.") + } + + return false +} + +//添加数据 +//注意:只支持基础类型 +func (L *MyLevelDB) Add(key string, value interface{}) bool { + if L.DB != nil { + by, err := tools.EncodeByte(value) + if err != nil { + //错误处理 + mylog.Error(err) + return false + } + if err = L.DB.Put([]byte(key), by, nil); err != nil { + mylog.Error(err) + } else { + return true + } + } + return false +} + +//添加一组数据(比一个一个添加速度快很多) +//注意:只支持基础类型 +func (L *MyLevelDB) AddList(array []Param) bool { + if L.DB != nil { + batch := new(leveldb.Batch) + for _, p := range array { + by, err := tools.EncodeByte(p.Value) + if err != nil { + //错误处理 + mylog.Error(err) + return false + } + batch.Put([]byte(p.Key), by) + } + err := L.DB.Write(batch, nil) + if err != nil { + //错误处理 + mylog.Error(err) + return false + } else { + return true + } + } + return false +} + +/* + 删除 +*/ +func (L *MyLevelDB) Delete(key string) bool { + if L.DB != nil { + err := L.DB.Delete([]byte(key), nil) + if err != nil { + mylog.Error(err) + return false + } + + return true + } + return false +} diff --git a/myleveldb/test2018-03-29.rar b/myleveldb/test2018-03-29.rar new file mode 100644 index 0000000..fa078d4 Binary files /dev/null and b/myleveldb/test2018-03-29.rar differ diff --git a/mylog/def.go b/mylog/def.go new file mode 100644 index 0000000..08f5daa --- /dev/null +++ b/mylog/def.go @@ -0,0 +1,83 @@ +package mylog + +import ( + "time" +) + +/* + 日志详细信息 +*/ +type LogInfo struct { + Service string `json:"service"` //服务名 + Group string `json:"group"` //服务组 + Type string `json:"type"` //日志的类型() + Action string `json:"action"` //动作 + Path string `json:"path"` //路径,地址,深度 + Ip string `json:"ip"` //ip地址 + Topic string `json:"topic"` + Bundle string `json:"bundle"` + Pid string `json:"pid"` + Data interface{} `json:"data"` + Creat_time time.Time `json:"created"` +} + +/* +向es发送数据结构信息 +*/ +type EsLogInfo struct { + Info LogInfo `json:"loginfo"` //信息 + Es_index string `json:"es_index"` //索引 + Es_type string `json:"es_type"` //类型 + Es_id string `json:"es_id"` //id +} + +const ( + Http_log_index = "http_log" +) + +const mapping = ` +{ + "settings":{ + "number_of_shards": 1, + "number_of_replicas": 5 + }, + "mappings":{ + "` + Http_log_index + `":{ + "properties":{ + "service":{ + "type":"keyword" + }, + "group":{ + "type":"keyword" + }, + "type":{ + "type":"text" + }, + "action":{ + "type":"keyword" + }, + "path":{ + "type":"text" + }, + "ip":{ + "type":"text" + }, + "topic":{ + "type":"keyword" + }, + "bundle":{ + "type":"keyword" + }, + "pid":{ + "type":"keyword" + }, + "data":{ + "type":"text" + }, + "created":{ + "type":"date" + } + } + } + } +}` diff --git a/mylog/myeslog.go b/mylog/myeslog.go new file mode 100644 index 0000000..1f081cd --- /dev/null +++ b/mylog/myeslog.go @@ -0,0 +1,122 @@ +package mylog + +import ( + "data/config" + "encoding/json" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "public/myelastic" + "public/myqueue" + "time" +) + +var ptr_que *myqueue.MyQueue = nil +var elastic myelastic.MyElastic +var isSaveFile bool = true //默认存文件 +var isSaveToEs bool = false //默认不保存 +var local_Log_file string = "log" //默认存放文件的目录 +var exe_path string + +func init() { + if config.IsRunTesting() { //测试时候不创建 + return + } + + file, _ := exec.LookPath(os.Args[0]) + path, _ := filepath.Abs(file) + exe_path = filepath.Dir(path) + + BuildDir(local_Log_file) + ptr_que = myqueue.NewSyncQueue() + es_path := config.GetEsAddrUrl() + if len(es_path) > 0 { + elastic = myelastic.OnInitES(es_path) + elastic.CreateIndex(Http_log_index, mapping) + } + go onConsumerLog() +} + +/* + 发送日志请求 +*/ +func OnLog(es_index, es_type, es_id string, data LogInfo) { + // local, _ := time.LoadLocation("Local") + // data.Creat_time = time.Now().In(local) + b, err := json.Marshal(data.Data) + if err != nil { + log.Println("OnLog error:", err) + } + data.Data = string(b) + var info EsLogInfo + info.Es_index = es_index + info.Es_type = es_type + info.Es_id = es_id + info.Info = data + + ptr_que.Push(info) //加入日志队列 +} + +/* + 更新本地存储文件地址 + isSave:是否本地存储 + LogFile:本地存储相对程序位置(log/ ==> 当前可执行文件的 log/目录) +*/ +func InitLogFileInfo(isSave, isSaveEs bool, LogFile string) { + isSaveFile = isSave + isSaveToEs = isSaveEs + local_Log_file = LogFile + if isSave { + BuildDir(local_Log_file) + } +} + +func BuildDir(logfile string) { + os.MkdirAll(exe_path+"/"+logfile, os.ModePerm) //生成多级目录 +} + +/* + 消费者 消费日志 +*/ +func onConsumerLog() { + for { + var info EsLogInfo + info = ptr_que.Pop().(EsLogInfo) + + if isSaveToEs && elastic.Client != nil { + if !elastic.Add(info.Es_index, info.Es_type, info.Es_id, info.Info) { + log.Println("elastic add error ") + } + } + + if isSaveFile { + saveLogTofile(info) + } + Debug(info) + } +} + +var _f *os.File +var _err error +var saveFaile string + +func saveLogTofile(info EsLogInfo) { + + time_str := time.Now().Format("2006-01-02-15") //设定时间格式 + fname := fmt.Sprintf("%s/%s/%s.log", exe_path, local_Log_file, time_str) + if saveFaile != fname { + if _f != nil { + _f.Close() + } + _f, _err = os.OpenFile(fname, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) + if _err != nil { + log.Println(_err) + return + } + } + + b, _ := json.Marshal(info) + _f.WriteString(string(b) + "\r\n") //输出堆栈信息 +} diff --git a/mylog/mylog.go b/mylog/mylog.go new file mode 100644 index 0000000..b5a4db6 --- /dev/null +++ b/mylog/mylog.go @@ -0,0 +1,101 @@ +package mylog + +import ( + "data/config" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "runtime/debug" + "time" +) + +func init() { + file, _ := exec.LookPath(os.Args[0]) + path, _ := filepath.Abs(file) + path = filepath.Dir(path) + os.MkdirAll(path+"/err", os.ModePerm) //生成多级目录 +} + +const ( // + Log_Error = iota //打印 Error 及以上级别 + Log_warning //打印 warning 及以上级别 + Log_Info //默认的返回值,为0,自增 //打印 Info 及以上级别 +) + +// +func Print(log_level int, describ string) { + log.Println(describ) + return +} + +// +func Println(describ ...interface{}) { + for _, e := range describ { + switch v := e.(type) { + case string: + log.Println(v) + case []byte: + log.Println(string(v)) + default: + log.Println(v) + } + } + return +} + +// +func Info(describ string) { + log.Println(describ) + return +} + +// +func Error(err error) { + log.Println(err) + SaveError(err.Error(), "err") +} + +//保存错误信息 +func SaveError(errstring, flag string) { + file, _ := exec.LookPath(os.Args[0]) + path, _ := filepath.Abs(file) + path = filepath.Dir(path) + + now := time.Now() //获取当前时间 + time_str := now.Format("2006-01-02_15") //设定时间格式 + fname := fmt.Sprintf("%s/err/%s_%s.log", path, flag, time_str) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒) + + f, err := os.OpenFile(fname, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) + if err != nil { + return + } + defer f.Close() + + f.WriteString("=========================" + now.Format("2006-01-02 15:04:05 ========================= \r\n")) + f.WriteString(errstring + "\r\n") //输出堆栈信息 + f.WriteString(string(debug.Stack()) + "\r\n") //输出堆栈信息) + f.WriteString("=========================end=========================\r\n") +} + +// +func Debug(describ ...interface{}) { + if config.OnIsDev() { + for _, e := range describ { + switch v := e.(type) { + case string: + log.Println(v) + case []byte: + log.Println(string(v)) + default: + log.Println(v) + } + } + } +} + +//刷新 +func Flush() { + +} diff --git a/mynsq/mynsq.go b/mynsq/mynsq.go new file mode 100644 index 0000000..1802e44 --- /dev/null +++ b/mynsq/mynsq.go @@ -0,0 +1,144 @@ +package mynsq + +import ( + "data/config" + "log" + "public/mylog" + + "github.com/nsqio/go-nsq" +) + +var producer *nsq.Producer = nil +var consumerMap map[int]*nsq.Consumer = nil +var cosumerTopics map[string][]int = nil + +func init() { + consumerMap = make(map[int]*nsq.Consumer) + cosumerTopics = make(map[string][]int) + cnf := nsq.NewConfig() + var err error + producer, err = nsq.NewProducer(config.GetNsqAddr(), cnf) + if err != nil { + mylog.Print(mylog.Log_Error, err.Error()) + panic(err) + } + +} + +//发消息 +func ProduceMsg(topic string, message []byte) bool { + + if producer == nil { + //channel 锁住 + cnf := nsq.NewConfig() + var err error + producer, err = nsq.NewProducer(config.GetNsqAddr(), cnf) + if err != nil { + mylog.Print(mylog.Log_Error, err.Error()) + return false + } + } + + if producer != nil { + err := producer.Publish(topic, message) + if err != nil { + mylog.Print(mylog.Log_Error, err.Error()) + return false + } else { + return true + } + } + + return false +} + +//单个nsqd处理消息 +//index 表示consumer 索引(用于开关使用) +func StartConsumeMsg(index int, topic, channel, nsqd string, handler nsq.Handler) bool { + StopConsumeMsgByIndex(index) + //第一次初始化 进入 + if consumerMap[index] == nil { + conf := nsq.NewConfig() + //最大允许向两台NSQD服务器接受消息,默认是1 + //config.MaxInFlight = 2 + var err error + consumerMap[index], err = nsq.NewConsumer(topic, channel, conf) + if nil != err { + log.Println(err) + mylog.Print(mylog.Log_Error, err.Error()) + return false + } + + //开始正式启动(后台,非阻塞方式) + consumerMap[index].AddHandler(handler) + err = consumerMap[index].ConnectToNSQD(nsqd) + if nil != err { + log.Println(err) + mylog.Print(mylog.Log_Error, err.Error()) + return false + } + + cosumerTopics[topic] = append(cosumerTopics[topic], index) + return true + } + + return false +} + +func GetConsumeSize() int { + return len(consumerMap) +} + +//停止消费 +func StopConsumeMsgByIndex(index int) { + if consumerMap[index] != nil { + consumerMap[index].Stop() + consumerMap[index] = nil + + for k, v := range cosumerTopics { + for i := range v { + if v[i] == index { + cosumerTopics[k] = append(v[:i], v[i+1:]...) + break + } + } + } + } +} + +//停止某个topic下全部消费 +func StopConsumeMsgByTopic(topic string) { + v, ok := cosumerTopics[topic] + if ok { + for i := range v { + if consumerMap[v[i]] != nil { + consumerMap[v[i]].Stop() + consumerMap[v[i]] = nil + } + } + } + + var temp []int + cosumerTopics[topic] = temp +} + +//停止所有topic的全部消费 +func StopAllConsumeMsg() { + cosumerTopics = make(map[string][]int) + size := GetConsumeSize() + for i := 0; i < size; i++ { + StopConsumeMsgByIndex(i) + } + + consumerMap = make(map[int]*nsq.Consumer) +} + +//func TestNSQ() { +// for k, v := range cosumerTopics { +// log.Println("topic is", k) +// for _, vv := range v { +// log.Println("consumer index is", vv) +// log.Println("consumer is ", consumerMap[vv]) +// } +// } +//} diff --git a/myqueue/myqueue.go b/myqueue/myqueue.go new file mode 100644 index 0000000..8e3ec65 --- /dev/null +++ b/myqueue/myqueue.go @@ -0,0 +1,89 @@ +package myqueue + +import ( + "sync" + + "gopkg.in/eapache/queue.v1" +) + +type MyQueue struct { + lock sync.Mutex + popable *sync.Cond + buffer *queue.Queue + closed bool +} + +// 创建 +func NewSyncQueue() *MyQueue { + ch := &MyQueue{ + buffer: queue.New(), + } + ch.popable = sync.NewCond(&ch.lock) + return ch +} + +// 取出队列,(阻塞模式) +func (q *MyQueue) Pop() (v interface{}) { + c := q.popable + buffer := q.buffer + + q.lock.Lock() + for buffer.Length() == 0 && !q.closed { + c.Wait() + } + + if buffer.Length() > 0 { + v = buffer.Peek() + buffer.Remove() + } + + q.lock.Unlock() + return +} + +//试着取出队列(非阻塞模式)返回ok == false 表示空 +func (q *MyQueue) TryPop() (v interface{}, ok bool) { + buffer := q.buffer + + q.lock.Lock() + + if buffer.Length() > 0 { + v = buffer.Peek() + buffer.Remove() + ok = true + } else if q.closed { + ok = true + } + + q.lock.Unlock() + return +} + +// 插入队列,非阻塞 +func (q *MyQueue) Push(v interface{}) { + q.lock.Lock() + if !q.closed { + q.buffer.Add(v) + q.popable.Signal() + } + q.lock.Unlock() +} + +// 获取队列长度 +func (q *MyQueue) Len() (l int) { + q.lock.Lock() + l = q.buffer.Length() + q.lock.Unlock() + return +} + +// Close MyQueue +// After close, Pop will return nil without block, and TryPop will return v=nil, ok=True +func (q *MyQueue) Close() { + q.lock.Lock() + if !q.closed { + q.closed = true + q.popable.Signal() + } + q.lock.Unlock() +} diff --git a/myrunner/error.go b/myrunner/error.go new file mode 100644 index 0000000..11a82de --- /dev/null +++ b/myrunner/error.go @@ -0,0 +1,9 @@ +package myrunner + +import "errors" + +//任务执行超时 +var ErrTimeOut = errors.New("run time out") + +//任务执行中断 +var ErrInterruput = errors.New("run interruput") diff --git a/myrunner/myrunner.go b/myrunner/myrunner.go new file mode 100644 index 0000000..4a37fc7 --- /dev/null +++ b/myrunner/myrunner.go @@ -0,0 +1,102 @@ +package myrunner + +import ( + "os" + "os/signal" + "time" +) + +//后台执行任何限时任务,而且我们还可以控制这个执行者,比如强制终止它等 +type Runner struct { + tasks []func() //要执行的任务 + complete chan error //用于通知任务全部完成 + timeout <-chan time.Time //这些任务在多久内完成 只能接收 + interrupt chan os.Signal //可以控制强制终止的信号 +} + +//工厂方法 +func New(tm time.Duration) *Runner { + return &Runner{ + complete: make(chan error), //同步通道,main routine等待,一致要任务完成或者被强制终止 + timeout: time.After(tm), + interrupt: make(chan os.Signal, 1), //至少接收到一个操作系统的中断信息 + } +} + +// +func (r *Runner) Add(tasks ...func()) { + r.tasks = append(r.tasks, tasks...) +} + +// +func (r *Runner) run() error { + for _, task := range r.tasks { + if r.isInterrupt() { + return ErrInterruput + } + task() + } + return nil +} + +//检查是否接收到了中断信号 +func (r *Runner) isInterrupt() bool { + select { + case <-r.interrupt: + signal.Stop(r.interrupt) + return true + default: + return false + } +} + +//开始执行所有任务,并且监视通道事件 +func (r *Runner) Start() error { + //希望接收哪些系统信号 + signal.Notify(r.interrupt, os.Interrupt) //如果有系统中断的信号,发给r.interrupt + + go func() { + r.complete <- r.run() + }() + + select { + case err := <-r.complete: + return err + case <-r.timeout: + return ErrTimeOut + } +} + +/* +调用示例: +func main() { + log.Println("...开始执行任务...") + + timeout := 2 * time.Second + r := New(timeout) + + r.Add(createTask(0), createTask(1), createTask(2)) + + if err := r.Start(); err != nil { + switch err { + case ErrTimeOut: + log.Println(err) + os.Exit(1) //退出 + case ErrInterruput: + log.Println(err) + os.Exit(2) + default: + break + } + } + log.Println("...任务执行结束...") +} + +func createTask(param int) func() { + return func() { + log.Printf("正在执行任务%d", param) + time.Sleep(time.Duration(param) * time.Second) + } +} + +*/ diff --git a/mysign/def.go b/mysign/def.go new file mode 100644 index 0000000..60c247d --- /dev/null +++ b/mysign/def.go @@ -0,0 +1,24 @@ +package mysign + +import "time" + +const ( + _sign_data = "_sign_data" +) + +//签名地址 +type Sign_client_tbl struct { + Id int `gorm:"primary_key"` + App_key string //key + App_secret string //secret + Expire_time time.Time //超时时间 + Strict_sign int //是否强制验签:0:用户自定义,1:强制 + Strict_verify int //是否强制验证:0:用户自定义,1:强制 + Token_expire_time int //token过期时间 +} + +//签名必须带的头标记 +type Sing_head struct { + Appid string `json:"appid,omitempty"` //appid + Signature string `json:"signature,omitempty"` //签名 +} diff --git a/mysign/sign.go b/mysign/sign.go new file mode 100644 index 0000000..2cacbcf --- /dev/null +++ b/mysign/sign.go @@ -0,0 +1,96 @@ +package mysign + +import ( + "data/config" + "public/mycache" + "public/mylog" + "public/mysqldb" + "public/tools" + "strings" + "time" +) + +func init() { + OnInit() +} + +func OnInit() { + str_db := config.GetDbUrl() + //fmt.Println("dddddddddddddd:", str_db) + if len(str_db) > 0 { + var db mysqldb.MySqlDB + defer db.OnDestoryDB() + orm := db.OnGetDBOrm(str_db) + if orm.HasTable(&Sign_client_tbl{}) { //有这个表 + now := time.Now() + var list []Sign_client_tbl + err := orm.Where("expire_time > ?", now).Find(&list).Error + if err != nil { + mylog.Error(err) + return + } + cache := mycache.OnGetCache(_sign_data) + for _, v := range list { //保存数据到缓存 + cache.Add(v.App_key, v, v.Expire_time.Sub(now)) + } + } + } +} + +func GetOne(appKey string) (sign Sign_client_tbl) { + cache := mycache.OnGetCache(_sign_data) + tp, b := cache.Value(appKey) + if b { + sign = tp.(Sign_client_tbl) + } else { + str_db := config.GetDbUrl() + if len(str_db) > 0 { + var db mysqldb.MySqlDB + defer db.OnDestoryDB() + orm := db.OnGetDBOrm(str_db) + if orm.HasTable(&Sign_client_tbl{}) { //有这个表 + now := time.Now() + err := orm.Where("app_key = ? and expire_time > ?", appKey, now).Find(&sign).Error + if err != nil { + mylog.Error(err) + return + } + cache := mycache.OnGetCache(_sign_data) + cache.Add(sign.App_key, sign, sign.Expire_time.Sub(now)) + } + } + } + + return +} + +/* + 生成验签 +*/ +func OnGetSign(appkey string, parm ...interface{}) string { + var sign Sign_client_tbl + if len(appkey) > 0 { + sign = GetOne(appkey) + } + //是否强制验证码 + if sign.Id == 0 || sign.Strict_sign == 0 { + return "" + } + + //开始验签 + var strKey string + for _, v := range parm { + strKey += tools.AsString(v) + } + + mylog.Debug("strKey:" + strKey) + mylog.Debug("md5:", tools.Md5Encoder(strKey)) + return tools.Md5Encoder(strKey) +} + +/* + 开始验签 +*/ +func OnCheckSign(appkey, signature string, parm ...interface{}) bool { + return strings.EqualFold(signature, OnGetSign(appkey, parm...)) +} diff --git a/mysign/sign_test.go b/mysign/sign_test.go new file mode 100644 index 0000000..133f6bc --- /dev/null +++ b/mysign/sign_test.go @@ -0,0 +1,20 @@ +package mysign + +import ( + "fmt" + "public/message" + "public/tools" + "testing" + "time" +) + +func Test_sing(t *testing.T) { + now := time.Now() + str := "1" + tools.GetTimeString(now) + str += "1.0001" + fmt.Println(str) + ttt := tools.Md5Encoder(str) + fmt.Println(ttt) + fmt.Println(OnCheckSign("wwwthings", ttt, 1, now, 1.0001)) + fmt.Println(message.GetSuccessMsg()) +} diff --git a/mysqlbeedb/mysqlbeedb.go b/mysqlbeedb/mysqlbeedb.go new file mode 100644 index 0000000..aa202b9 --- /dev/null +++ b/mysqlbeedb/mysqlbeedb.go @@ -0,0 +1,59 @@ +/* + orm := db.OnCreatDB() + var sum int64 = 0 + for { + sum++ + var user User_account_tbl + user.Id = sum + + orm.SetTable("user_account_tbls") + err := orm.Where("id=?", sum).Find(&user) + if err != nil { + log.Println("-----------:", err) + } else { + log.Println(user) + } + + time.Sleep(time.Second * 2) + } +*/ +package mysqlbeedb + +import ( + "database/sql" + "fmt" + "public/mylog" + + "data/config" + + "github.com/astaxie/beedb" + _ "github.com/go-sql-driver/mysql" +) + +type MySqlDB struct { + DB *sql.DB +} + +func (i *MySqlDB) OnGetDBOrm(dataSourceName string) (orm beedb.Model) { + if i.DB == nil { + var err error + i.DB, err = sql.Open("mysql", dataSourceName) + if err != nil { + mylog.Print(mylog.Log_Error, fmt.Sprintf("Got error when connect database, the error is '%v'", err)) + } + } + + orm = beedb.New(i.DB) + + if config.OnIsDev() { + beedb.OnDebug = true + } + return +} + +func (i *MySqlDB) OnDestoryDB() { + if i.DB != nil { + i.DB.Close() + i.DB = nil + } +} diff --git a/mysqldb/log.go b/mysqldb/log.go new file mode 100644 index 0000000..fb1c731 --- /dev/null +++ b/mysqldb/log.go @@ -0,0 +1,36 @@ +package mysqldb + +import ( + "fmt" + "public/mylog" + + "github.com/jinzhu/gorm" +) + +var LogFormatter = func(values ...interface{}) (messages []interface{}) { + if len(values) > 1 { + var ( + currentTime = "\t[" + gorm.NowFunc().Format("2006-01-02 15:04:05") + "]" + source = fmt.Sprintf("(%v)\t", values[1]) + ) + messages = []interface{}{source, currentTime} + messages = append(messages, "\t [") + messages = append(messages, values[2:]...) + messages = append(messages, "]") + + } + + return +} + +// +type DbLog struct { + gorm.Logger +} + +// +func (db DbLog) Print(values ...interface{}) { + msg := LogFormatter(values...) + str := fmt.Sprint(msg...) + mylog.SaveError(str, "sql") +} diff --git a/mysqldb/mysqldb.go b/mysqldb/mysqldb.go new file mode 100644 index 0000000..e895e17 --- /dev/null +++ b/mysqldb/mysqldb.go @@ -0,0 +1,66 @@ +/* + orm := db.OnCreatDB() + var sum int64 = 0 + for { + sum++ + var user User_account_tbl + user.Id = sum + + orm.SetTable("user_account_tbls") + err := orm.Where("id=?", sum).Find(&user) + if err != nil { + log.Println("-----------:", err) + } else { + log.Println(user) + } + + time.Sleep(time.Second * 2) + } +*/ +package mysqldb + +import ( + "fmt" + "public/mylog" + + "data/config" + + _ "github.com/go-sql-driver/mysql" + "github.com/jinzhu/gorm" +) + +type MySqlDB struct { + DB *gorm.DB +} + +func (i *MySqlDB) OnGetDBOrm(dataSourceName string) (orm *gorm.DB) { + if i.DB == nil { + var err error + i.DB, err = gorm.Open("mysql", dataSourceName) + if err != nil { + mylog.Print(mylog.Log_Error, fmt.Sprintf("Got error when connect database, the error is '%v'", err)) + } + } + + i.DB.SingularTable(true) //全局禁用表名复数 + orm = i.DB + + if config.OnIsDev() { + orm.LogMode(true) + //beedb.OnDebug = true + } else { + orm.SetLogger(DbLog{}) + } + return +} + +func (i *MySqlDB) OnDestoryDB() { + if i.DB != nil { + i.DB.Close() + i.DB = nil + } +} + +func init() { + +} diff --git a/mysqldb/mysqldb2018-05-04.rar b/mysqldb/mysqldb2018-05-04.rar new file mode 100644 index 0000000..e093a56 Binary files /dev/null and b/mysqldb/mysqldb2018-05-04.rar differ diff --git a/mywebsocket/mywebsocket.go b/mywebsocket/mywebsocket.go new file mode 100644 index 0000000..dbb2646 --- /dev/null +++ b/mywebsocket/mywebsocket.go @@ -0,0 +1,208 @@ +package mywebsocket + +/* + 说明:第一个包 初始化client唯一id。消息id为100 +*/ +import ( + "encoding/json" + "net/http" + "public/mycache" + "public/mylog" + "sync" + "time" + + "github.com/ant0ine/go-json-rest/rest" + + "golang.org/x/net/websocket" +) + +type ClientBody struct { + Code int `json:"code,omitempty"` + Data interface{} `json:"data,omitempty"` +} + +type WSMessageType uint8 + +const ( + WS_Login WSMessageType = 1 // 链接 + WS_Close WSMessageType = 2 // 断线 +) + +type HandlerReadFunc func(string, string, ClientBody, *websocket.Conn) //ID , MESSAGEID,内容 +type HandlerStateFunc func(string, string, WSMessageType) //状态发声改变回调 + +var mutex sync.Mutex + +/* + 写数据 + pathExp:请求的根路径 + clientid:发送人id + body:发送内容 +*/ +func WriteData(pathExp string, clientid string, body ClientBody) bool { + + wb, err := json.Marshal(body) + if err != nil { + mylog.Debug("error:", err.Error()) + return false + } + + cache := mycache.OnGetCache("websocket" + pathExp) + tp, b := cache.Value(clientid) + + if b { + b_r := false + tmp := tp.([]*websocket.Conn) + for i := 0; i < len(tmp); i++ { + if _, err = tmp[i].Write(wb); err != nil { + mylog.Debug("Can't send", err.Error()) + defer tmp[i].Close() + } else { + b_r = true + } + } + return b_r + } else { + mylog.Debug("not find client:" + clientid) + return false + } +} + +//websocket 初始化接口 +/* +pathExp 域, +handlerFunc 读到的消息回调 +stateFunc 连接消息回调 +timeOut 读取超时回调(0则永不超时) +isMult 是否多用户登录 +*/ +func InitWebSocket(pathExp string, handlerFunc HandlerReadFunc, stateFunc HandlerStateFunc, timeOut time.Duration, isMult bool) *rest.Route { + wsHandler := websocket.Handler(func(ws *websocket.Conn) { + mylog.Debug("enter") + var err error + var msg = make([]byte, 1024) + var n int + n, err = ws.Read(msg) + + if err != nil { + mylog.Debug("ws:close") + return + } + + var clientid string + var clientBody ClientBody + + err = json.Unmarshal(msg[:n], &clientBody) + if err != nil { + mylog.Debug("Unmarshal:" + err.Error()) + return + } else { + if clientBody.Code != 100 { + mylog.Debug("messageid error") + return + } + clientid = clientBody.Data.(string) + //保存缓存 + cache := mycache.OnGetCache("websocket" + pathExp) + var tmp []*websocket.Conn + + mutex.Lock() + + tp, b := cache.Value(clientid) + if b && isMult { //多用户 + tmp = tp.([]*websocket.Conn) + } + + tmp = append(tmp, ws) + cache.Add(clientid, tmp, 2*time.Hour) //2小时过期 + mutex.Unlock() + + //------------------end + mylog.Debug("init success:" + clientid) + if stateFunc != nil { + stateFunc(pathExp, clientid, WS_Login) + } + } + + ch := make(chan bool, 1) + if timeOut > 0 { + go func(ws *websocket.Conn) { + var after <-chan time.Time + loop: + after = time.After(timeOut) + for { + select { + case b := <-ch: //继续下一个等待 + if !b { + break + } else { + goto loop + } + case <-after: //超时处理 + mylog.Print(mylog.Log_Info, "time out:"+clientid) + ws.Close() + break + } + } + }(ws) + } + + for { + n, err = ws.Read(msg) + if err != nil { + if timeOut > 0 { + ch <- false + } + mylog.Debug("ws:close") + break + } else if timeOut > 0 { + ch <- true + } + var body ClientBody + err = json.Unmarshal(msg[:n], &body) + if err != nil { + mylog.Debug("r:" + err.Error()) + } else { + if handlerFunc != nil { + handlerFunc(pathExp, clientid, body, ws) + } + } + } + + //删除缓存 + cache := mycache.OnGetCache("websocket" + pathExp) + var tmp []*websocket.Conn + + mutex.Lock() + tp, b := cache.Value(clientid) + if b { + tmp = tp.([]*websocket.Conn) + } + i := 0 + for ; i < len(tmp); i++ { + if tmp[i] == ws { + tmp = append(tmp[:i], tmp[i+1:]...) // 最后面的“...”不能省略 + break + } + } + if i == 0 || len(tmp) == 0 || !isMult { + cache.Delete(clientid) + mylog.Debug("delete all: " + clientid) + } else { + cache.Add(clientid, tmp, 2*time.Hour) //2小时过期 + mylog.Debug("delete one: " + clientid) + } + + mutex.Unlock() + if stateFunc != nil { + stateFunc(pathExp, clientid, WS_Close) + } + //------------------end + + }) + + return rest.Get(pathExp, func(w rest.ResponseWriter, r *rest.Request) { + //mylog.Debug("-------------") + wsHandler.ServeHTTP(w.(http.ResponseWriter), r.Request) + }) +} diff --git a/serializing/serializing.go b/serializing/serializing.go new file mode 100644 index 0000000..448fef8 --- /dev/null +++ b/serializing/serializing.go @@ -0,0 +1,41 @@ +package serializing + +import( + "bytes" + "encoding/gob" +) +/* +适用类型:二进制到struct相互转换 + +使用方法: + b, err := serializing.Encode(data) + if err != nil { + //错误处理 + } + if err := serializing.Decode(b, &to); err != nil { + //错误处理 + } + +*/ + +/* + 功能:序列化 +*/ +func Encode(data interface{})([]byte, error){ + buf := bytes.NewBuffer(nil) + enc := gob.NewEncoder(buf) + err := enc.Encode(data) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +/* + 功能:反序列化 +*/ +func Decode(data []byte,to interface{}) error{ + buf := bytes.NewBuffer(data) + dec := gob.NewDecoder(buf) + return dec.Decode(to) +} \ No newline at end of file diff --git a/server/def.go b/server/def.go new file mode 100644 index 0000000..eb512bf --- /dev/null +++ b/server/def.go @@ -0,0 +1,12 @@ +package server + +const ( + Stopped = 1 + StartPending = 2 + StopPending = 3 + Running = 4 + ContinuePending = 5 + PausePending = 6 + Paused = 7 + NOTFIND = 8 +) diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..ba5904f --- /dev/null +++ b/server/server.go @@ -0,0 +1,137 @@ +package server + +import ( + "fmt" + "os" + "time" + + "data/config" + + "github.com/jander/golog/logger" + "github.com/kardianos/service" +) + +func OnStart(callBack func()) { + name, displayName, desc := config.GetServiceConfig() + p := &program{callBack} + sc := &service.Config{ + Name: name, + DisplayName: displayName, + Description: desc, + } + s, err := service.New(p, sc) + //var s, err = service.NewService(name, displayName, desc) + if err != nil { + fmt.Printf("%s unable to start: %s", displayName, err) + return + } + + fmt.Printf("Service \"%s\" do.\n", displayName) + + if len(os.Args) > 1 { + var err error + verb := os.Args[1] + switch verb { + case "install": + { + err = s.Install() + if err != nil { + fmt.Printf("Failed to install: %s\n", err) + return + } + fmt.Printf("Service \"%s\" installed.\n", displayName) + } + case "remove": + { + err = s.Uninstall() + if err != nil { + fmt.Printf("Failed to remove: %s\n", err) + return + } + fmt.Printf("Service \"%s\" removed.\n", displayName) + } + case "run": + { + err = s.Run() + if err != nil { + fmt.Printf("Failed to run: %s\n", err) + return + } + fmt.Printf("Service \"%s\" run.\n", displayName) + } + case "start": + { + err = s.Start() + if err != nil { + fmt.Printf("Failed to start: %s\n", err) + return + } + fmt.Println("starting check service:", displayName) + + ticker := time.NewTicker(1 * time.Second) + <-ticker.C + + st, err := IsStart(name) + if err != nil { + fmt.Println(err) + return + } else { + if st == Stopped || st == StopPending { + fmt.Printf("Service \"%s\" is Stopped.\n", displayName) + fmt.Println("can't to start service.") + return + } + } + fmt.Printf("Service \"%s\" started.\n", displayName) + } + case "stop": + { + err = s.Stop() + if err != nil { + fmt.Printf("Failed to stop: %s\n", err) + return + } + + st, err := IsStart(name) + if err != nil { + fmt.Println(err) + return + } else { + if st == Running || st == StartPending { + fmt.Printf("Service \"%s\" is Started.\n", displayName) + fmt.Println("can't to stop service.") + return + } + } + fmt.Printf("Service \"%s\" stopped.\n", displayName) + } + } + return + } else { + fmt.Print("Failed to read args\n") + //return + } + + if err = s.Run(); err != nil { + logger.Error(err) + } +} + +type program struct { + callBack func() +} + +func (p *program) Start(s service.Service) error { + go p.run() + return nil +} +func (p *program) run() { + p.callBack() +} +func (p *program) Stop(s service.Service) error { + return nil +} + +type ServiceTools interface { + IsStart(name string) (status int, err error) +} diff --git a/server/server_linux.go b/server/server_linux.go new file mode 100644 index 0000000..2e7ca2a --- /dev/null +++ b/server/server_linux.go @@ -0,0 +1,32 @@ +package server + +import ( + "fmt" + "os/exec" + "strings" +) + +type WindowsServiceTools struct { + i ServiceTools +} + +func IsStart(name string) (st int, err error) { + f, _ := exec.Command("service", name, "status").Output() + + st = NOTFIND + str := string(f) + a := strings.Split(str, "\n") + for _, v := range a { + if strings.Index(v, "Active:") > 0 { + fmt.Println("====info===:", v) + if strings.Index(v, "inactive") > 0 { //不活动的 + st = Stopped + } else if strings.Index(v, "activating") > 0 { //活动的 + st = Running + } + break + } + } + + return +} diff --git a/server/server_windows.go b/server/server_windows.go new file mode 100644 index 0000000..675ea16 --- /dev/null +++ b/server/server_windows.go @@ -0,0 +1,30 @@ +package server + +import ( + "github.com/btcsuite/winsvc/mgr" + "github.com/btcsuite/winsvc/svc" +) + +type WindowsServiceTools struct { + i ServiceTools +} + +func IsStart(name string) (st int, err error) { + var m *mgr.Mgr + m, err = mgr.Connect() + if err != nil { + return 0, err + } + defer m.Disconnect() + + s, err := m.OpenService(name) + if err != nil { + return 0, err + } + defer s.Close() + + var ss svc.Status + ss, err = s.Query() + st = int(ss.State) + return +} diff --git a/sign/def.go b/sign/def.go new file mode 100644 index 0000000..4255ec8 --- /dev/null +++ b/sign/def.go @@ -0,0 +1,15 @@ +package sign + +import "time" + +const ( + _sign_data = "_sign_data" +) + +type Sign_client_tbl struct { + Id int `gorm:"primary_key"` + App_key string //key + App_secret string //secret + Expire_time time.Time //超时时间 + Strict_verify int //是否强制验证:0:用户自定义,1:强制 +} diff --git a/sign/sign.go b/sign/sign.go new file mode 100644 index 0000000..8d244fe --- /dev/null +++ b/sign/sign.go @@ -0,0 +1,93 @@ +package sign + +import ( + "data/config" + "public/mycache" + "public/mylog" + "public/mysqldb" + "public/tools" + "strings" + "time" +) + +func init() { + OnInit() +} + +func OnInit() { + str_db := config.GetDbUrl() + if len(str_db) > 0 { + var db mysqldb.MySqlDB + defer db.OnDestoryDB() + orm := db.OnGetDBOrm(str_db) + if orm.HasTable(&Sign_client_tbl{}) { //有这个表 + now := time.Now() + var list []Sign_client_tbl + err := orm.Where("expire_time > ?", now).Find(&list).Error + if err != nil { + mylog.Error(err) + return + } + cache := mycache.OnGetCache(_sign_data) + for _, v := range list { //保存数据到缓存 + cache.Add(v.App_key, v, v.Expire_time.Sub(now)) + } + } + } +} + +func getOne(appKey string) (sign Sign_client_tbl) { + str_db := config.GetDbUrl() + if len(str_db) > 0 { + var db mysqldb.MySqlDB + defer db.OnDestoryDB() + orm := db.OnGetDBOrm(str_db) + if orm.HasTable(&Sign_client_tbl{}) { //有这个表 + now := time.Now() + err := orm.Where("app_key = ? and expire_time > ?", appKey, now).Find(&sign).Error + if err != nil { + mylog.Error(err) + return + } + cache := mycache.OnGetCache(_sign_data) + cache.Add(sign.App_key, sign, sign.Expire_time.Sub(now)) + } + } + + return +} + +/* + 生成验签 +*/ +func OnGetSign(appkey string, parm ...interface{}) string { + var sign Sign_client_tbl + if len(appkey) > 0 { + cache := mycache.OnGetCache(_sign_data) + tp, b := cache.Value(appkey) + if b { + sign = tp.(Sign_client_tbl) + } else { + sign = getOne(appkey) + } + } + + if sign.Id == 0 { + return "" + } + + //开始验签 + var strKey string + for _, v := range parm { + strKey += tools.AsString(v) + } + + return tools.Md5Encoder(strKey) +} + +/* + 开始验签 +*/ +func OnCheckSign(appkey, signature string, parm ...interface{}) bool { + return strings.EqualFold(signature, OnGetSign(appkey, parm)) +} diff --git a/sign/sign_test.go b/sign/sign_test.go new file mode 100644 index 0000000..2c37b8e --- /dev/null +++ b/sign/sign_test.go @@ -0,0 +1,20 @@ +package sign + +import ( + "fmt" + "public/message" + "public/tools" + "testing" + "time" +) + +func Test_sing(t *testing.T) { + now := time.Now() + str := "1" + tools.GetTimeString(now) + str += "1.0001" + fmt.Println(str) + ttt := tools.Md5Encoder(str) + fmt.Println(ttt) + fmt.Println(OnCheckSign("wwwthings", ttt, 1, now, 1.0001)) + fmt.Println(message.GetSuccessMsg()) +} diff --git a/timerDeal/cron.go b/timerDeal/cron.go new file mode 100644 index 0000000..d0d84ef --- /dev/null +++ b/timerDeal/cron.go @@ -0,0 +1,8 @@ +package timerDeal + +//cron +// c := cron.New() +// c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") }) +// c.AddFunc("@hourly", func() { fmt.Println("Every hour") }) +// c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") }) +// c.Start() diff --git a/timerDeal/timerDeal.go b/timerDeal/timerDeal.go new file mode 100644 index 0000000..4cc228d --- /dev/null +++ b/timerDeal/timerDeal.go @@ -0,0 +1,37 @@ +package timerDeal + +import ( + "log" + "time" +) + +type TimerDeal struct { + f_list []func() + f_d_timeout time.Duration +} + +//增加一个回调 +func (t *TimerDeal) AddOneCall(f func()) { + t.f_list = append(t.f_list, f) +} + +//设置超时时间 +func (t *TimerDeal) SetCallBackTimer(d time.Duration) { + t.f_d_timeout = d +} + +func (t *TimerDeal) OnSart() { + //time.Tick() + go t.onTick() +} + +func (t *TimerDeal) onTick() { + ticker := time.NewTicker(t.f_d_timeout) + for { + time := <-ticker.C + for _, v := range t.f_list { + v() + } + log.Println("timer callback:", time.String()) + } +} diff --git a/timerDeal/timetask.go b/timerDeal/timetask.go new file mode 100644 index 0000000..a9daec4 --- /dev/null +++ b/timerDeal/timetask.go @@ -0,0 +1,47 @@ +package timerDeal + +import ( + "log" + "public/mydef" + "public/mylog" + "time" +) + +/* + 主要为定时任务 +*/ + +/* +超时回调 +t:超时时间 +fun:回调接口 +args:回调接口传入的参数 +*/ +func OnDealTimeOut(t time.Duration, fun mydef.ParamsCallFunc, parms ...interface{}) { + go func() { + ticker := time.NewTicker(t) + <-ticker.C + mylog.Debug("timer 执行.....") + //以下为定时执行的操作 + fun(parms...) + }() +} + +/* + 每月事件 + day : 几号 + hour, min, sec : 几点(当天的0点偏移秒数) + callback : 时间回调 +*/ +func OnPeMonth(day int, hour, min, sec int, callback func()) { + go func() { + for { + next := time.Now().AddDate(0, 1, 0) + next = time.Date(next.Year(), next.Month(), day, hour, min, sec, 0, next.Location()) + t := time.NewTimer(next.Sub(time.Now())) + log.Println("next time callback:", next) + <-t.C + callback() + } + }() +} diff --git a/tools/check.go b/tools/check.go new file mode 100644 index 0000000..3fc7a5a --- /dev/null +++ b/tools/check.go @@ -0,0 +1,52 @@ +package tools + +import ( + "fmt" + "os" + "regexp" + "strings" +) + +//检测参数 +func CheckParam(params ...string) bool { + for _, value := range params { + if len(value) == 0 { + return false + } + } + return true +} + +//判断是否是手机号 +func IsPhone(mobileNum string) bool { + tmp := `^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$` + reg := regexp.MustCompile(tmp) + return reg.MatchString(mobileNum) +} + +//判断用户是否是邮件用户 +func IsMail(username string) (isMail bool) { + isMail = false + if strings.Contains(username, "@") { + isMail = true //是邮箱 + } + return +} + +//判断是否在测试环境下使用 +func IsRunTesting() bool { + if len(os.Args) > 1 { + fmt.Println(os.Args[1]) + return strings.HasPrefix(os.Args[1], "-test") + } + return false +} + +//判断是否是18或15位身份证 +func IsIdCard(cardNo string) bool { + //18位身份证 ^(\d{17})([0-9]|X)$ + if m, _ := regexp.MatchString(`(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)`, cardNo); !m { + return false + } + return true +} diff --git a/tools/convert.go b/tools/convert.go new file mode 100644 index 0000000..5309a8b --- /dev/null +++ b/tools/convert.go @@ -0,0 +1,139 @@ +package tools + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "errors" + "fmt" + "public/mylog" + "regexp" + "strconv" + "strings" + "time" +) + +type RawBytes []byte + +var errNilPtr = errors.New("destination pointer is nil") + +func cloneBytes(b []byte) []byte { + if b == nil { + return nil + } else { + c := make([]byte, len(b)) + copy(c, b) + return c + } +} + +func AsString(src interface{}) string { + switch v := src.(type) { + case string: + return v + case []byte: + return string(v) + case int: + return strconv.Itoa(v) + case int32: + return strconv.FormatInt(int64(v), 10) + case int64: + return strconv.FormatInt(v, 10) + case float32: + return strconv.FormatFloat(float64(v), 'f', -1, 64) + case float64: + return strconv.FormatFloat(v, 'f', -1, 64) + case time.Time: + return GetTimeStr(v) + case bool: + return strconv.FormatBool(v) + default: + { + b, _ := json.Marshal(v) + mylog.Println(string(b)) + return string(b) + } + } + return fmt.Sprintf("%v", src) +} + +//编码二进制 +func EncodeByte(data interface{}) ([]byte, error) { + buf := bytes.NewBuffer(nil) + enc := gob.NewEncoder(buf) + err := enc.Encode(data) + if err != nil { + mylog.Error(err) + return nil, err + } + return buf.Bytes(), nil +} + +//解码二进制 +func DecodeByte(data []byte, to interface{}) error { + buf := bytes.NewBuffer(data) + dec := gob.NewDecoder(buf) + return dec.Decode(to) +} + +//byte转16进制字符串 +func ByteToHex(data []byte) string { + buffer := new(bytes.Buffer) + for _, b := range data { + + s := strconv.FormatInt(int64(b&0xff), 16) + if len(s) == 1 { + buffer.WriteString("0") + } + buffer.WriteString(s) + } + + return buffer.String() +} + +//16进制字符串转[]byte +func HexToBye(hex string) []byte { + length := len(hex) / 2 + slice := make([]byte, length) + rs := []rune(hex) + + for i := 0; i < length; i++ { + s := string(rs[i*2 : i*2+2]) + value, _ := strconv.ParseInt(s, 16, 10) + slice[i] = byte(value & 0xFF) + } + return slice +} + +//Emoji表情解码 +func UnicodeEmojiDecode(s string) string { + //emoji表情的数据表达式 + re := regexp.MustCompile("\\[[\\\\u0-9a-zA-Z]+\\]") + //提取emoji数据表达式 + reg := regexp.MustCompile("\\[\\\\u|]") + src := re.FindAllString(s, -1) + for i := 0; i < len(src); i++ { + e := reg.ReplaceAllString(src[i], "") + p, err := strconv.ParseInt(e, 16, 32) + if err == nil { + s = strings.Replace(s, src[i], string(rune(p)), -1) + } + } + return s +} + +//Emoji表情转换 +func UnicodeEmojiCode(s string) string { + ret := "" + rs := []rune(s) + for i := 0; i < len(rs); i++ { + if len(string(rs[i])) == 4 { + u := `[\u` + strconv.FormatInt(int64(rs[i]), 16) + `]` + ret += u + + } else { + ret += string(rs[i]) + } + } + return ret +} diff --git a/tools/file.go b/tools/file.go new file mode 100644 index 0000000..0878973 --- /dev/null +++ b/tools/file.go @@ -0,0 +1,66 @@ +package tools + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "public/mylog" +) + +//检查目录是否存在 +func CheckFileIsExist(filename string) bool { + var exist = true + if _, err := os.Stat(filename); os.IsNotExist(err) { + mylog.Debug(filename + " not exist") + exist = false + } + return exist +} + +//创建目录 +func BuildDir(abs_dir string) error { + return os.MkdirAll(abs_dir, os.ModePerm) //生成多级目录 +} + +//删除文件或文件夹 +func DeleteFile(abs_dir string) error { + return os.RemoveAll(abs_dir) +} + +//获取目录所有文件夹 +func GetPathDirs(abs_dir string) (re []string) { + if CheckFileIsExist(abs_dir) { + files, _ := ioutil.ReadDir(abs_dir) + for _, f := range files { + if f.IsDir() { + re = append(re, f.Name()) + } + } + } + return +} + +//获取目录所有文件夹 +func GetPathFiles(abs_dir string) (re []string) { + if CheckFileIsExist(abs_dir) { + files, _ := ioutil.ReadDir(abs_dir) + for _, f := range files { + if !f.IsDir() { + re = append(re, f.Name()) + } + } + } + return +} + +//获取目录地址 +func GetModelPath() string { + file, _ := exec.LookPath(os.Args[0]) + path, _ := filepath.Abs(file) + // if len(path) > 0 { + // path += "/" + // } + path = filepath.Dir(path) + return path +} diff --git a/tools/ip.go b/tools/ip.go new file mode 100644 index 0000000..d812ae7 --- /dev/null +++ b/tools/ip.go @@ -0,0 +1,75 @@ +package tools + +import ( + "bytes" + "io/ioutil" + "net" + "net/http" + "public/mylog" + "strings" +) + +///* +// 获取外网ip +//*/ +//func GetWwwIP() (ip string) { +// ip = "" +// resp, err := http.Get("http://myexternalip.com/raw") +// if err != nil { +// mylog.Error(err) +// return +// } + +// defer resp.Body.Close() +// body, err := ioutil.ReadAll(resp.Body) +// if err != nil { +// return +// } + +// ip = string(body) +// ip = strings.Split(ip, "\n")[0] +// return +//} + +//获取公网IP地址 +func GetWwwIP() (exip string) { + resp, err := http.Get("http://myexternalip.com/raw") + if err != nil { + return "" + } + defer resp.Body.Close() + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "" + } + return string(bytes.TrimSpace(b)) +} + +/* + 获取内网ip +*/ +func GetLocalIP() (ip string) { + addrs, err := net.InterfaceAddrs() + if err != nil { + mylog.Error(err) + return + } + for _, a := range addrs { + if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + ip = ipnet.IP.String() + break + } + } + } + return +} + +//获取用户ip +func GetClientIp(r *http.Request) (ip string) { + ip = r.Header.Get("X-Real-Ip") + if ip == "" { + ip = strings.Split(r.RemoteAddr, ":")[0] + } + return +} diff --git a/tools/json.go b/tools/json.go new file mode 100644 index 0000000..3bc0883 --- /dev/null +++ b/tools/json.go @@ -0,0 +1,68 @@ +package tools + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/ant0ine/go-json-rest/rest" +) + +func JsonToForm(r *http.Request) { + //添加支持json 操作 + r.ParseForm() + if len(r.Form) == 1 { //可能是json 支持json + for key, value := range r.Form { + if len(value[0]) == 0 { + delete(r.Form, key) + var m map[string]string + if err := json.Unmarshal([]byte(key), &m); err == nil { + for k, v := range m { + r.Form[k] = []string{v} + } + } + } + } + } + + body, _ := ioutil.ReadAll(r.Body) + body_str := string(body) + if len(body_str) > 0 { + var m map[string]string + if err := json.Unmarshal(body, &m); err == nil { + for k, v := range m { + r.Form[k] = []string{v} + } + } + } + //-----------------------------end + return +} + +func GetRequestJsonObj(r *rest.Request, v interface{}) error { + + //添加支持json 操作 + body, err := ioutil.ReadAll(r.Body) + r.Body.Close() + json.Unmarshal(body, &v) + //-----------------------------end + return err +} + +func GetJsonStr(obj interface{}) string { + b, _ := json.Marshal(obj) + return string(b) +} + +func JsonDecode(obj interface{}) string { + return GetJsonStr(obj) +} + +func GetJsonObj(str string, out interface{}) { + json.Unmarshal([]byte(str), out) + return +} + +func JsonEncode(str string, out interface{}) { + GetJsonObj(str, out) +} diff --git a/tools/mybase64.go b/tools/mybase64.go new file mode 100644 index 0000000..bab185f --- /dev/null +++ b/tools/mybase64.go @@ -0,0 +1,13 @@ +package tools + +import ( + "encoding/base64" +) + +func Base64Encode(src []byte) string { + return base64.StdEncoding.EncodeToString(src) +} + +func Base64Decode(src []byte) ([]byte, error) { + return base64.StdEncoding.DecodeString(string(src)) +} diff --git a/tools/rand.go b/tools/rand.go new file mode 100644 index 0000000..94fb57f --- /dev/null +++ b/tools/rand.go @@ -0,0 +1,47 @@ +package tools + +import ( + "math/rand" + "time" +) + +//生成随机字符串 +var _bytes []byte = []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") +var r *rand.Rand + +func GetRandomString(n int) string { + result := []byte{} + if r == nil { + r = rand.New(rand.NewSource(time.Now().UnixNano())) + } + + for i := 0; i < n; i++ { + result = append(result, _bytes[r.Intn(len(_bytes))]) + } + return string(result) +} + +//生成随机整数 digit:位数 +func GenerateRangeNum(digit int) int { + var max, min int = 1, 1 + if digit > 0 { + for i := 0; i < digit; i++ { + max = max * 10 + } + for i := 0; i < digit-1; i++ { + min = min * 10 + } + } + rand.Seed(time.Now().Unix()) + return rand.Intn(max-min) + min +} + +//生成随机整数 digit:位数 +func GetGetRandInt(min int, max int) int { + if min > max { + min = 0 + max = 0 + } + rand.Seed(time.Now().Unix()) + return rand.Intn(max-min) + min +} diff --git a/tools/str.go b/tools/str.go new file mode 100644 index 0000000..ffdcd6a --- /dev/null +++ b/tools/str.go @@ -0,0 +1,97 @@ +package tools + +import ( + "strconv" + "strings" + + "github.com/axgle/mahonia" +) + +//int数组转字符串 +func FormatInt(list []int, seg string) string { + s := make([]string, len(list)) + for i := range list { + s[i] = strconv.Itoa(list[i]) + } + + return strings.Join(s, seg) +} + +//字符串去除特殊字符 +//func DealStr(str, replace string) string { +// var s string = strings.TrimSpace(str) //去除尾部空格 +// strings.Replace(s, replace, "", -1) +// return s +//} + +//截取字符串 不包括str +func Substr(str string, start, length int) string { + rs := []rune(str) + rl := len(rs) + end := 0 + + if start < 0 { + start = rl - 1 + start + } + end = start + length + + if start > end { + start, end = end, start + } + + if start < 0 { + start = 0 + } + if start > rl { + start = rl + } + if end < 0 { + end = 0 + } + if end > rl { + end = rl + } + return string(rs[start:end]) +} + +//查找字符串最小值 +func MinimumString(rest []string) string { + minimum := rest[0] + for _, v := range rest { + + if v := v; v < minimum { + minimum = v + } + + } + return minimum +} + +//字符集转换 +func ConvertToString(src string, srcCode string, tagCode string) string { + srcCoder := mahonia.NewDecoder(srcCode) + srcResult := srcCoder.ConvertString(src) + tagCoder := mahonia.NewDecoder(tagCode) + _, cdata, _ := tagCoder.Translate([]byte(srcResult), true) + return string(cdata) +} + +//系统转其他 +func ConvertString(src string, tagCode string) string { + enc := mahonia.NewEncoder(tagCode) + return enc.ConvertString(src) +} + +// +func GetGBK(src string) string { + return string(ConvertString(src, "gbK")) +} + +//反转字符串 +func Reverse(s string) string { + r := []rune(s) + for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { + r[i], r[j] = r[j], r[i] + } + return string(r) +} diff --git a/tools/timeTools.go b/tools/timeTools.go new file mode 100644 index 0000000..02ad1f5 --- /dev/null +++ b/tools/timeTools.go @@ -0,0 +1,186 @@ +package tools + +import ( + "fmt" + "strings" + "time" +) + +//获取本地时间戳 +func GetUtcTime(tm time.Time) int64 { + return tm.Unix() //- 8*60*60 +} + +//当前时间向上取整点 +func GetHour(timestamp int64) int { + // formaTime := time.Format("2006-01-02 15:04:05") + tm := time.Unix(timestamp, 0) + return tm.Hour() +} + +//获取offset天的现在时间:注意时区 +func GetLastDayCurrentTime(timestamp int64, offset int) time.Time { + tm := time.Unix(timestamp, 0) + yesDay := tm.AddDate(0, 0, 1*offset) + return yesDay +} + +//获取给定时间的星期 +func GetTimeWeek(timestamp int64) int { + tm := time.Unix(timestamp, 0) + weekDay := tm.Weekday().String() + var week int = 0 + switch weekDay { + case "Monday": + week = 1 + case "Tuesday": + week = 2 + case "Wednesday": + week = 3 + case "Thursday": + week = 4 + case "Friday": + week = 5 + case "Saturday": + week = 6 + default: + week = 0 + } + return week +} + +//获取向上整时时间 +func GetHour0(timestamp int64, timeZone *time.Location) time.Time { + tm := time.Unix(timestamp, 0) + tStr := tm.Format("2006-01-02 15") + ":00:00" + return StrToTime(tStr, "2006-01-02 15:04:05", timeZone) +} + +//获取给定日期的零点时间 +func GetDay0(timestamp int64, timeZone *time.Location) time.Time { + tm := time.Unix(timestamp, 0) + tStr := tm.Format("2006-01-02") + " 00:00:00" + + return StrToTime(tStr, "2006-01-02 15:04:05", timeZone) +} + +//获取offset 0点时间 +func GetUtcDay0(now time.Time, timeZone *time.Location) int64 { + tm := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) + return tm.Unix() +} + +//字符串转时间 +func StrToTime(tStr, format string, timeZone *time.Location) time.Time { + if len(format) == 0 { + format = "2006-01-02 15:04:05" + } + if timeZone == nil { + //chinaLocal, _ := time.LoadLocation("Local") + timeZone = time.Local + } + + ti, _ := time.ParseInLocation(format, tStr, timeZone) + return ti +} + +/* + 给定字符串时间转换成本地时间戳 +*/ +func StringTimetoUnix(timestr string) int64 { + return StrToTime(timestr, "2006-01-02 15:04:05", time.Local).Unix() +} + +//获取最近上个星期天的零点日期 +func GetTimeWeek0(timestamp int64) int64 { + weekday := GetTimeWeek(timestamp) + tm0 := GetDay0(timestamp, nil) + tm0 = tm0.AddDate(0, 0, -1*weekday) + + return tm0.Unix() +} + +/* + 获取给定时间的当月1号零点时间 +*/ +func GetMonth0(timestamp int64) time.Time { + + tm0 := GetDay0(timestamp, nil) + month0 := tm0.Day() - 1 + tm0 = tm0.AddDate(0, 0, -1*month0) //这个月1号 + return tm0 +} + +//整点执行操作 +func TimerByHour(f func()) { + for { + now := time.Now() + // 计算下一个整点 + next := now.Add(time.Hour * 1) + next = time.Date(next.Year(), next.Month(), next.Day(), next.Hour(), 0, 0, 0, next.Location()) + t := time.NewTimer(next.Sub(now)) + <-t.C + //以下为定时执行的操作 + f() + } +} + +//时间戳转换为time +func UnixToTime(timestamp int64) time.Time { + return time.Unix(timestamp, 0) +} + +//获取本地时间 +func GetLocalTime(tm time.Time) time.Time { + local, _ := time.LoadLocation("Local") + return tm.In(local) + //return tm.Add(8 * 60 * 60 * time.Second) +} + +//获取系统时间的格式 +func GetSysTimeLayout() string { + t := time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC) + strLayout := strings.Replace(t.String(), "+0000 UTC", "", -1) + return strings.TrimSpace(strLayout) +} + +func FormatTime(tm time.Time, for_str string) string { + return tm.Format(for_str) +} + +func GetTimeStr(tm time.Time) string { + return FormatTime(tm, "2006-01-02 15:04:05") +} + +func GetDayStr(tm time.Time) string { + return FormatTime(tm, "2006-01-02") +} + +//json marsh 重写 +type Time struct { + time.Time +} + +func (t *Time) UnmarshalJSON(data []byte) (err error) { + tmp := string(data) + str := `"2006-01-02 15:04:05"` + if len(tmp) <= 20 { + str = `"2006-01-02"` + } + if tmp != `""` { + now, err1 := time.ParseInLocation(str, tmp, time.Local) + err = err1 + *t = Time{now} + } + + return +} + +func (t Time) MarshalJSON() ([]byte, error) { + var stamp = fmt.Sprintf(`"%s"`, t.Format("2006-01-02 15:04:05")) + return []byte(stamp), nil +} + +func (t Time) String() string { + return t.Format("2006-01-02 15:04:05") +} diff --git a/tools/tools.go b/tools/tools.go new file mode 100644 index 0000000..89a7758 --- /dev/null +++ b/tools/tools.go @@ -0,0 +1,154 @@ +package tools + +import ( + "crypto/md5" + crand "crypto/rand" + "crypto/sha1" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "html/template" + "io" + "net/http" + "os" + "path/filepath" + "public/mylog" + "reflect" + "sort" + "strings" + + "data/config" + + "github.com/ant0ine/go-json-rest/rest" +) + +/* +获取程序运行路径 +*/ +func GetCurrentDirectory() string { + dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) + return strings.Replace(dir, "\\", "/", -1) +} + +func Md5Encoder(src string) string { + h := md5.New() + h.Write([]byte(src)) // 需要加密的字符串 + // fmt.Printf("%x\n", h.Sum(nil)) // 输出加密结 + ret := fmt.Sprintf("%x", h.Sum(nil)) + return strings.ToUpper(ret) +} + +//合并数组 +func Copy(dest []interface{}, src []interface{}) (result []interface{}) { + result = make([]interface{}, len(dest)+len(src)) + copy(result, dest) + copy(result[len(dest):], src) + return +} + +//删除数组 +func DeleteArray(src []interface{}, index int) (result []interface{}) { + result = append(src[:index], src[(index+1):]...) + return +} + +//生成32位md5字串 +func GetMd5String(s string) string { + h := md5.New() + h.Write([]byte(s)) + return hex.EncodeToString(h.Sum(nil)) +} + +//获取总页数 +func GetTotalPageNum(pageSize, totalCount int) int { + return (totalCount + pageSize - 1) / pageSize +} + +//生成32位guid +func UniqueId() string { + b := make([]byte, 48) + if _, err := io.ReadFull(crand.Reader, b); err != nil { + return "" + } + return GetMd5String(base64.URLEncoding.EncodeToString(b)) +} + +//删除切片index +func DeleteSlice(slice interface{}, index int) (interface{}, error) { + sliceValue := reflect.ValueOf(slice) + length := sliceValue.Len() + if slice == nil || length == 0 || (length-1) < index { + return nil, errors.New("error") + } + if length-1 == index { + return sliceValue.Slice(0, index).Interface(), nil + } else { + return reflect.AppendSlice(sliceValue.Slice(0, index), sliceValue.Slice(index+1, length)).Interface(), nil + } +} + +//查找int最小值 +func MinimumInt(rest []int) int { + minimum := rest[0] + for _, v := range rest { + if v < minimum { + minimum = v + } + } + return minimum +} + +func LoadTemplate(list ...string) *template.Template { + var tmp []string + for _, v := range list { + if CheckFileIsExist(GetModelPath() + config.Static_host[0] + v) { + tmp = append(tmp, GetModelPath()+config.Static_host[0]+v) + } else { + mylog.Debug("file does not exist:" + GetModelPath() + config.Static_host[0] + v) + panic(GetModelPath() + config.Static_host[0] + v) + } + } + return template.Must(template.ParseFiles(tmp...)) +} + +/* + 执行模版渲染, + name:模版名字,""则无名字 + data:传参列表 + list:模版列表 +*/ +func ExecuteTemplate(w rest.ResponseWriter, name string, data interface{}, list ...string) error { + t := LoadTemplate(list...) + w.(http.ResponseWriter).Header().Set("Content-Type", "text/html; charset=utf-8") + + if len(name) == 0 { + return t.Execute(w.(http.ResponseWriter), data) + } else { + return t.ExecuteTemplate(w.(http.ResponseWriter), name, data) + } +} + +//按字典顺序排序 +func DictSort(res []string) (str string) { + sort.Strings(res) + if len(res) > 0 { + for _, v := range res { + str += v + } + } + return +} + +//SHA1加密 +func Sha1Encrypt(str string) string { + h := sha1.New() + h.Write([]byte(str)) + bs := h.Sum(nil) + return fmt.Sprintf("%x", bs) +} + +//中文字符切割时有问题。采用此方式不会有问题 +func GetUtf8Str(str string) []rune { + return []rune(str) +} diff --git a/tools/zip.go b/tools/zip.go new file mode 100644 index 0000000..6ee9902 --- /dev/null +++ b/tools/zip.go @@ -0,0 +1,139 @@ +package tools + +import ( + "archive/zip" + "bytes" + "io" + "os" + "path/filepath" +) + +func IsZip(zipPath string) bool { + f, err := os.Open(zipPath) + if err != nil { + return false + } + defer f.Close() + + buf := make([]byte, 4) + if n, err := f.Read(buf); err != nil || n < 4 { + return false + } + + return bytes.Equal(buf, []byte("PK\x03\x04")) +} + +func Unzip(archive, target string) error { + reader, err := zip.OpenReader(archive) + if err != nil { + return err + } + + if err := os.MkdirAll(target, 0755); err != nil { + return err + } + + for _, file := range reader.File { + path := filepath.Join(target, file.Name) + if file.FileInfo().IsDir() { + os.MkdirAll(path, file.Mode()) + continue + } + //------------注入 + + dir := filepath.Dir(path) + if len(dir) > 0 { + if _, err = os.Stat(dir); os.IsNotExist(err) { + err = os.MkdirAll(dir, 0755) + if err != nil { + return err + } + } + } + + //---------------------end + + fileReader, err := file.Open() + if err != nil { + return err + } + defer fileReader.Close() + + targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) + if err != nil { + return err + } + defer targetFile.Close() + + if _, err := io.Copy(targetFile, fileReader); err != nil { + return err + } + } + + return nil +} + +//压缩文件 +//files 文件数组,可以是不同dir下的文件或者文件夹 +//dest 压缩文件存放地址 +func Compress(files []*os.File, dest string) error { + d, _ := os.Create(dest) + defer d.Close() + w := zip.NewWriter(d) + defer w.Close() + for _, file := range files { + err := compress(file, "", w) + if err != nil { + return err + } + } + return nil +} + +func compress(file *os.File, prefix string, zw *zip.Writer) error { + info, err := file.Stat() + if err != nil { + return err + } + if info.IsDir() { + if len(prefix) == 0 { + prefix = info.Name() + } else { + prefix = prefix + "/" + info.Name() + } + fileInfos, err := file.Readdir(-1) + if err != nil { + return err + } + for _, fi := range fileInfos { + f, err := os.Open(file.Name() + "/" + fi.Name()) + if err != nil { + return err + } + err = compress(f, prefix, zw) + if err != nil { + return err + } + } + } else { + header, err := zip.FileInfoHeader(info) + if len(prefix) == 0 { + header.Name = header.Name + } else { + header.Name = prefix + "/" + header.Name + } + if err != nil { + return err + } + writer, err := zw.CreateHeader(header) + if err != nil { + return err + } + _, err = io.Copy(writer, file) + file.Close() + if err != nil { + return err + } + } + return nil +} diff --git a/weixin/base.go b/weixin/base.go new file mode 100644 index 0000000..e466f5d --- /dev/null +++ b/weixin/base.go @@ -0,0 +1,112 @@ +package weixin + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "public/mycache" + "public/mylog" + "time" + + "github.com/silenceper/wechat" +) + +const ( + GETTICKETURL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card&access_token=" + GETJSURL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=" +) + +//获取微信accesstoken +func GetAccessToken() (access_token string, err error) { + //先从缓存中获取 + cache := mycache.OnGetCache("weixin_token") + var tp interface{} + tp, b := cache.Value("base") + if b { + access_token = tp.(string) + } else { + wc := wechat.NewWechat(&cfg) + access_token, err = wc.GetAccessToken() + //保存缓存 + cache.Add("base", access_token, 7000*time.Second) + } + return +} + +//获取微信卡券ticket +func GetApiTicket() (ticket string, err error) { + //先从缓存中获取 + cache := mycache.OnGetCache("weixin_card_ticket") + var tp interface{} + tp, b := cache.Value("base") + if b { + ticket = tp.(string) + } else { + access_token, e := GetAccessToken() + if e != nil { + mylog.Error(e) + err = e + return + } + var url = GETTICKETURL + access_token + + resp, e1 := http.Get(url) + if e1 != nil { + mylog.Error(e1) + err = e1 + return + } + defer resp.Body.Close() + body, e2 := ioutil.ReadAll(resp.Body) + if e2 != nil { + mylog.Error(e2) + err = e2 + return + } + var result ApiTicket + json.Unmarshal(body, &result) + ticket = result.Ticket + //保存缓存 + cache.Add("base", ticket, 7000*time.Second) + } + return +} + +//获取微信js ticket +func GetJsTicket() (ticket string, err error) { + //先从缓存中获取 + cache := mycache.OnGetCache("weixin_js_ticket") + var tp interface{} + tp, b := cache.Value("base") + if b { + ticket = tp.(string) + } else { + access_token, e := GetAccessToken() + if e != nil { + mylog.Error(e) + err = e + return + } + var url = GETJSURL + access_token + + resp, e1 := http.Get(url) + if e1 != nil { + mylog.Error(e1) + err = e1 + return + } + defer resp.Body.Close() + body, e2 := ioutil.ReadAll(resp.Body) + if e2 != nil { + mylog.Error(e2) + err = e2 + return + } + var result ApiTicket + json.Unmarshal(body, &result) + ticket = result.Ticket + //保存缓存 + cache.Add("base", ticket, 7000*time.Second) + } + return +} diff --git a/weixin/cache.go b/weixin/cache.go new file mode 100644 index 0000000..836deef --- /dev/null +++ b/weixin/cache.go @@ -0,0 +1,39 @@ +package weixin + +import ( + "public/mycache" + "time" +) + +//Memcache struct contains *memcache.Client +type Gocache struct { + mc *mycache.MyCache +} + +//NewGocache create new cache2go +func NewGocache(server string) *Gocache { + mc := mycache.OnGetCache(server) + return &Gocache{&mc} +} + +//Get return cached value +func (mem *Gocache) Get(key string) interface{} { + v, _ := mem.mc.Value(key) + return v +} + +// IsExist check value exists in memcache. +func (mem *Gocache) IsExist(key string) bool { + return mem.mc.IsExist(key) +} + +//Set cached value with key and expire time. +func (mem *Gocache) Set(key string, val interface{}, timeout time.Duration) (err error) { + mem.mc.Add(key, val, timeout) + return nil +} + +//Delete value in memcache. +func (mem *Gocache) Delete(key string) error { + return mem.mc.Delete(key) +} diff --git a/weixin/def.go b/weixin/def.go new file mode 100644 index 0000000..ff65aec --- /dev/null +++ b/weixin/def.go @@ -0,0 +1,19 @@ +package weixin + +type UserInfo struct { + OpenID string `json:"openid"` + Nickname string `json:"nickname"` + Sex int32 `json:"sex"` + Province string `json:"province"` + City string `json:"city"` + Country string `json:"country"` + HeadImgURL string `json:"headimgurl"` + Privilege []string `json:"privilege"` + Unionid string `json:"unionid"` +} +type ApiTicket struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + Ticket string `json:"ticket"` + Expires_in int `json:"expires_in"` +} \ No newline at end of file diff --git a/weixin/enterprisePay.go b/weixin/enterprisePay.go new file mode 100644 index 0000000..c0c7f49 --- /dev/null +++ b/weixin/enterprisePay.go @@ -0,0 +1,58 @@ +package weixin + +import ( + "log" + "public/tools" + + wxpay "gopkg.in/go-with/wxpay.v1" +) + +const ( + enterprisePayUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers" // 查询企业付款接口请求URL +) + +/* + 企业付款 + open_id:用户唯一标识 + trade_no : 商户订单号 + desc : 操作说明 + amount:付款金额 分 +*/ +func WxEnterprisePay(open_id, trade_no, desc, ipAddr string, amount int) bool { + c := wxpay.NewClient(pay_appId, mchId, apiKey) + + // 附着商户证书 + err := c.WithCert(certFile, keyFile, rootcaFile) + if err != nil { + log.Fatal(err) + } + + params := make(wxpay.Params) + nonce_str := tools.GetRandomString(16) + // 查询企业付款接口请求参数 + params.SetString("mch_appid", c.AppId) //商户账号appid + params.SetString("mchid", c.MchId) //商户号 + params.SetString("nonce_str", nonce_str) // 随机字符串 + params.SetString("partner_trade_no", trade_no) // 商户订单号 + params.SetString("openid", open_id) //用户openid + params.SetString("check_name", "NO_CHECK") //校验用户姓名选项 + params.SetInt64("amount", int64(amount)) //企业付款金额,单位为分 + params.SetString("desc", desc) //企业付款操作说明信息。必填。 + params.SetString("spbill_create_ip", ipAddr) + + params.SetString("sign", c.Sign(params)) // 签名 + + // 发送查询企业付款请求 + ret, err := c.Post(enterprisePayUrl, params, true) + if err != nil { + log.Fatal(err) + } + log.Print(ret) + returnCode := ret.GetString("return_code") + resultCode := ret.GetString("result_code") + if returnCode == "SUCCESS" && resultCode == "SUCCESS" { + return true + } else { + return false + } +} diff --git a/weixin/init.go b/weixin/init.go new file mode 100644 index 0000000..db7991c --- /dev/null +++ b/weixin/init.go @@ -0,0 +1,60 @@ +package weixin + +import ( + "data/config" + "public/tools" + + "github.com/silenceper/wechat" + wxpay "gopkg.in/go-with/wxpay.v1" +) + +const ( + // 微信支付商户平台证书路径 + certFileLoc = "/cert/apiclient_cert.pem" + keyFileLoc = "/cert/apiclient_key.pem" + rootcaFileLoc = "/cert/rootca.pem" +) + +var cfg wechat.Config +var client *wxpay.Client + +var pay_appId string // 微信公众平台应用ID +var mchId string // 微信支付商户平台商户号 +var apiKey string // 微信支付商户平台API密钥 +var secret string +var notify_url string +var token string +var encodingAESKey string + +var certFile string // 微信支付商户平台证书路径 +var keyFile string +var rootcaFile string + +func init() { + wx_info := config.GetWxInfo() + //配置微信支付参数 + pay_appId = wx_info.AppID + mchId = wx_info.MchId + apiKey = wx_info.Key + secret = wx_info.AppSecret + notify_url = wx_info.NotifyUrl + token = wx_info.Token + encodingAESKey = wx_info.EncodingAESKey + + certFile = tools.GetModelPath() + certFileLoc + keyFile = tools.GetModelPath() + keyFileLoc + rootcaFile = tools.GetModelPath() + rootcaFileLoc + + //使用memcache保存access_token,也可选择redis或自定义cache + memCache := NewGocache("_winxin_access") + //配置微信参数 + cfg = wechat.Config{ + AppID: pay_appId, + AppSecret: secret, + Token: token, + EncodingAESKey: encodingAESKey, + Cache: memCache, + } + client = wxpay.NewClient(pay_appId, mchId, apiKey) + client.WithCert(certFile, keyFile, rootcaFile) +} diff --git a/weixin/oauth.go b/weixin/oauth.go new file mode 100644 index 0000000..c221ef0 --- /dev/null +++ b/weixin/oauth.go @@ -0,0 +1,29 @@ +package weixin + +import ( + "io/ioutil" + "net/http" + "public/mylog" +) + +/* + 小程序授权 +*/ +func SmallAppOauth(jscode string) string { + var url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + pay_appId + "&secret=" + + secret + "&js_code=" + jscode + "&grant_type=authorization_code&trade_type=JSAPI" + + resp, e := http.Get(url) + if e != nil { + mylog.Error(e) + return "" + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + mylog.Error(e) + return "" + } + return string(body) +} diff --git a/weixin/pay.go b/weixin/pay.go new file mode 100644 index 0000000..df19a3a --- /dev/null +++ b/weixin/pay.go @@ -0,0 +1,210 @@ +package weixin + +import ( + "crypto/md5" + "fmt" + "log" + "public/message" + "public/mylog" + "public/tools" + "strconv" + "strings" + "time" + + wxpay "gopkg.in/go-with/wxpay.v1" +) + +const ( + unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder" // 统一下单请求URL + queryOrderUrl = "https://api.mch.weixin.qq.com/pay/orderquery" // 统一查询请求URL + refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund" //退款请求URL +) + +const ( + PAY_SUCCESS = 1 //支付成功 + PAY_REFUND = 2 //转入退款 + PAY_CLOSED = 3 //已关闭 + PAY_NOTPAY = 4 //未支付 + PAY_REVOKED = 5 //已撤销 + PAY_USERPAYING = 6 //支付中 + PAY_ERROR = -1 //支付失败 +) + +/* + 小程序统一下单接口 + open_id:用户唯一标识 + price : 预支付价钱 + price_body : 支付描述 + order_id : 商户订单号 +*/ +func SmallAppUnifiedorder(open_id string, price int, price_body, order_id, client_ip string) message.MessageBody { + if !tools.CheckParam(open_id, order_id) || price <= 0 { //参数检测 + return message.GetErrorMsg(message.ParameterInvalid) + } + + params := make(wxpay.Params) + // 查询企业付款接口请求参数 + params.SetString("appid", client.AppId) + params.SetString("mch_id", client.MchId) + params.SetString("body", price_body) + params.SetInt64("total_fee", int64(price*10)) + params.SetString("spbill_create_ip", client_ip) + params.SetString("notify_url", notify_url) + params.SetString("trade_type", "JSAPI") + params.SetString("openid", open_id) + params.SetString("nonce_str", tools.GetRandomString(32)) // 随机字符串 + params.SetString("out_trade_no", order_id) // 商户订单号 + params.SetString("sign", client.Sign(params)) // 签名 c.Sign(params) + + log.Println("paramsparams", params) + // 发送查询企业付款请求 + ret, err := client.Post(unifiedOrderUrl, params, true) + if err != nil { + mylog.Error(err) + msg := message.GetErrorMsg(message.UnknownError) + return msg + } + //-----------------------end + + //ret["order_id"] = order_tbl.Order_id + fmt.Println(ret) + + if ret["result_code"] == "SUCCESS" { //再次签名 + dd := make(map[string]string) + dd["timeStamp"] = strconv.FormatInt(tools.GetUtcTime(time.Now()), 10) + dd["nonceStr"] = tools.GetRandomString(32) + dd["package"] = "prepay_id=" + ret["prepay_id"] + dd["signType"] = "MD5" + dd["paySign"] = "MD5" + //appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111 + str := "appId=" + pay_appId + "&nonceStr=" + dd["nonceStr"] + "&package=" + dd["package"] + "&signType=MD5&timeStamp=" + dd["timeStamp"] + "&key=" + apiKey + by := md5.Sum([]byte(str)) + dd["paySign"] = strings.ToUpper(fmt.Sprintf("%x", by)) + dd["order_id"] = order_id + + msg := message.GetSuccessMsg() + msg.Data = dd + return msg + } + + msg := message.GetErrorMsg(message.InValidOp) + msg.Data = ret + return msg +} + +/* + 统一查询接口 + open_id:用户唯一标识 + order_id : 商户订单号 +*/ +func OnSelectData(open_id, order_id string) (int, message.MessageBody) { + if !tools.CheckParam(open_id, order_id) { //参数检测 + return 0, message.GetErrorMsg(message.ParameterInvalid) + } + + code := 0 + + params := make(wxpay.Params) + // 查询企业付款接口请求参数 + params.SetString("appid", client.AppId) + params.SetString("mch_id", client.MchId) + params.SetString("out_trade_no", order_id) //商户订单号 + params.SetString("nonce_str", tools.GetRandomString(32)) // 随机字符串 + params.SetString("sign", client.Sign(params)) // 签名 c.Sign(params) + + // 发送查询企业付款请求 + ret := make(wxpay.Params) + var err error + ret, err = client.Post(queryOrderUrl, params, true) + if err != nil { //做再次确认 + time.Sleep(time.Second * 1) + ret, err = client.Post(queryOrderUrl, params, true) + if err != nil { + mylog.Error(err) + msg := message.GetSuccessMsg() + return code, msg + } + } + //-----------------------end + + msg := message.GetSuccessMsg(message.NormalMessageId) + + /* + SUCCESS—支付成功 + REFUND—转入退款 + NOTPAY—未支付 + CLOSED—已关闭 + REVOKED—已撤销(刷卡支付) + USERPAYING--用户支付中 + PAYERROR--支付失败(其他原因,如银行返回失败) + */ + if ret["trade_state"] == "SUCCESS" { + code = PAY_SUCCESS + } else if ret["trade_state"] == "REFUND" { + code = PAY_REFUND + } else if ret["trade_state"] == "CLOSED" { + code = PAY_CLOSED + } else if ret["trade_state"] == "NOTPAY" { + code = PAY_NOTPAY + } else if ret["trade_state"] == "REVOKED" { + code = PAY_REVOKED + } else if ret["trade_state"] == "USERPAYING" { + code = PAY_USERPAYING + } else { + code = PAY_ERROR + } + + if ret["trade_state"] == "SUCCESS" { //支付成功 + msg.State = true + } else { + msg.State = false + } + + msg.Data = ret + return code, msg +} + +/* + 申请退款 + open_id:用户唯一标识 + order_id : 商户订单号 + refund_no:商户退款单号 + total_fee: 订单总金额 分 + refund_fee: 退款总金额 分 +*/ +func RefundPay(open_id, order_id, refund_no string, total_fee, refund_fee int) (bool, message.MessageBody) { + if !tools.CheckParam(open_id, order_id) { //参数检测 + return false, message.GetErrorMsg(message.ParameterInvalid) + } + code := false + params := make(wxpay.Params) + // 退款请求参数 + params.SetString("appid", client.AppId) + params.SetString("mch_id", client.MchId) + params.SetString("out_trade_no", order_id) //商户订单号 + params.SetString("out_refund_no", refund_no) //商户退款单号 + params.SetInt64("total_fee", int64(total_fee)) // 订单总金额(分) + params.SetInt64("refund_fee", int64(refund_fee)) // 退款金额(分) + params.SetString("nonce_str", tools.GetRandomString(32)) // 随机字符串 + params.SetString("sign", client.Sign(params)) // 签名 c.Sign(params) + + // 发送申请退款请求 + ret, err := client.Post(refundUrl, params, true) + if err != nil { + mylog.Error(err) + msg := message.GetErrorMsg(message.UnknownError) + return code, msg + } + //-----------------------end + + msg := message.GetSuccessMsg(message.NormalMessageId) + + if ret["result_code"] == "SUCCESS" { //申请成功 + msg.State = true + code = true + } else { + msg.State = false + } + msg.Data = ret + return code, msg +}