From 78929abeac06517d272ab5cd5eac1377f8902072 Mon Sep 17 00:00:00 2001 From: guzeng Date: Fri, 5 Feb 2021 17:35:27 +0800 Subject: [PATCH] init --- cache.go | 86 +++++++++++++++++++++++ cache_test.go | 26 +++++++ header.go | 63 +++++++++++++++++ master.go | 73 ++++++++++++++++++++ master_test.go | 15 ++++ order_sign.go | 41 +++++++++++ order_sn.go | 69 +++++++++++++++++++ order_sn_test.go | 30 ++++++++ order_status.go | 176 +++++++++++++++++++++++++++++++++++++++++++++++ redis.go | 14 ++++ sendmsg.go | 43 ++++++++++++ site.go | 101 +++++++++++++++++++++++++++ site_test.go | 23 +++++++ user.go | 25 +++++++ user_test.go | 14 ++++ var.go | 6 ++ 16 files changed, 805 insertions(+) create mode 100644 cache.go create mode 100644 cache_test.go create mode 100644 header.go create mode 100644 master.go create mode 100644 master_test.go create mode 100644 order_sign.go create mode 100644 order_sn.go create mode 100644 order_sn_test.go create mode 100644 order_status.go create mode 100644 redis.go create mode 100644 sendmsg.go create mode 100644 site.go create mode 100644 site_test.go create mode 100644 user.go create mode 100644 user_test.go create mode 100644 var.go diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..a0e80ad --- /dev/null +++ b/cache.go @@ -0,0 +1,86 @@ +package site + +import ( + "encoding/json" + "errors" + + "git.tetele.net/ttlpkg/tgo/redis" +) + +/** + * 从redis获取分站数据库及ID信息 + * 2020/06/08 + * gz + */ +func GetSiteFromRedis(host string) (map[string]string, error) { + + var site map[string]string = make(map[string]string) + info, err := redis.HGetString(SITE_KEY, host) + if err != nil { + return site, err + } + type siteStruct struct { + Database string `json:"database"` + SiteId string `json:"site_id"` + } + var siteInfo siteStruct + err = json.Unmarshal([]byte(info), &siteInfo) + if err != nil { + return site, err + } + + if siteInfo.Database != "" { + site["database"] = siteInfo.Database + } + if siteInfo.SiteId != "" { + site["site_id"] = siteInfo.SiteId + } + return site, nil +} + +/** + * 分站数据库及ID信息保存及redis + * 2020/06/08 + * gz + */ +func SetToRedis(host string, site map[string]string) error { + if host == "" { + return errors.New("没有域名") + } + if len(site) < 1 { + return errors.New("没有站点信息") + } + + siteStr, err := json.Marshal(site) + if err != nil { + return err + } + _, err = redis.HMSet(SITE_KEY, host, siteStr) + + return err +} + +func GetAll() ([]map[string]string, error) { + + list, err := redis.HGetAll(SITE_KEY) + if err != nil { + return nil, err + } + var siteInfo map[string]string + var all []map[string]string + if len(list) > 0 { + for key, item := range list { + if (key+1)%2 == 0 { //只处理偶数位 + siteInfo = make(map[string]string) + err = json.Unmarshal(item, &siteInfo) + if err != nil { + continue + } + siteInfo["domainname"] = string(list[key-1]) + all = append(all, siteInfo) + } + + } + } + return all, err +} diff --git a/cache_test.go b/cache_test.go new file mode 100644 index 0000000..d252002 --- /dev/null +++ b/cache_test.go @@ -0,0 +1,26 @@ +package site + +import ( + "testing" +) + +func Test_GetSiteFromRedis(t *testing.T) { + + host := "dev.tetele.net" + + ret, err := GetSiteFromRedis(host) + + t.Log(ret) + t.Log(err) + + // site := map[string]string{"database": "dev_testing", "site_id": "2000"} + + // err = SetToRedis(host, site) + + // t.Log(err) + + ret2, err := GetAll() + + t.Log(ret2) + t.Log(len(ret2)) +} diff --git a/header.go b/header.go new file mode 100644 index 0000000..017217a --- /dev/null +++ b/header.go @@ -0,0 +1,63 @@ +package site + +import ( + "net/http" + "strings" +) + +/** + * 取访问主机名 + */ +func GetHost(req *http.Request) string { + + var hostlist []string = req.Header.Values("X-Forwarded-Host") + var host string + if len(hostlist) > 1 { + host = hostlist[len(hostlist)-1] //取最后一次转发的 + } else if len(hostlist) == 1 { + host = hostlist[0] + } else { + host = "" + } + if host != "" { + hosts := strings.Split(host, ",") + host = strings.Trim(hosts[len(hosts)-1], " ") + } + + if host == "" { + host = req.Host + } + return host +} + +/** + * 取域名 + */ +func GetDomain(req *http.Request) string { + scheme := "http://" + if req.TLS != nil { + scheme = "https://" + } + if strings.Contains(req.Referer(), "https://") { + scheme = "https://" + } + var host string = GetHost(req) + + host = strings.Split(host, ":")[0] + + var w strings.Builder + w.WriteString(scheme) + w.WriteString(host) + + return w.String() + +} + +func SetHeader(w http.ResponseWriter, resp *http.Request) { + + w.Header().Set("Access-Control-Allow-Origin", "*") //允许访问所有域 + w.Header().Add("Access-Control-Allow-Headers", "Content-Type,x-csrf-token,x-requested-with,token") //header的类型 + w.Header().Set("Access-Control-Allow-Methods", "POST,GET,OPTIONS") + w.Header().Set("content-type", "application/json") //返回数据格式是json + // w.Header().Set("Content-Length", resp.Header.Get("Content-Length")) +} diff --git a/master.go b/master.go new file mode 100644 index 0000000..03a81a3 --- /dev/null +++ b/master.go @@ -0,0 +1,73 @@ +package site + +import ( + "encoding/json" + "errors" + "io/ioutil" + "net/http" + "strings" + + "git.tetele.net/ttlpkg/tgo/helper" +) + +/** + * 从总站获取数据库信息 + * master 总站域名,带协议 + * host 分站域名,带协议 + * 2020/06/05 + * gz + */ + +/** + * 从总站获取分站数据库及ID信息 + * 2020/06/06 + * gz + */ +func GetSiteFromMaster(master, host string) (map[string]string, error) { + + url := helper.StringJoin(master, "/api/gethostdb") + + var site map[string]string = make(map[string]string) + + resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader("host="+host)) + if err != nil { + return site, errors.New("获取站点数据失败," + err.Error()) + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return site, errors.New("获取站点数据失败," + err.Error()) + } + + type D struct { + Database interface{} `json:"database"` + SiteId interface{} `json:"site_id"` + } + + type Reply struct { + Code interface{} `json:"code"` + Data D `json:"data"` + Msg string `json:"msg"` + } + + var data Reply + err = json.Unmarshal(body, &data) + if err != nil { + return site, errors.New("站点数据格式错误," + err.Error()) + } + + if helper.ToStr(data.Code) != "1" { + return site, errors.New(data.Msg) + } + + if data.Data.Database != "" { + site["database"] = helper.ToStr(data.Data.Database) + } + if data.Data.SiteId != "" { + site["site_id"] = helper.ToStr(data.Data.SiteId) + } + + return site, nil +} diff --git a/master_test.go b/master_test.go new file mode 100644 index 0000000..fc9daf5 --- /dev/null +++ b/master_test.go @@ -0,0 +1,15 @@ +package site + +import ( + "testing" +) + +func Test_GetSiteFromMaster(t *testing.T) { + master := "https://devmaster.tetele.net" + host := "https://dev.tetele.net" + + ret, err := GetSiteFromMaster(master, host) + + t.Log(ret) + t.Log(err) +} diff --git a/order_sign.go b/order_sign.go new file mode 100644 index 0000000..8d507f6 --- /dev/null +++ b/order_sign.go @@ -0,0 +1,41 @@ +package orderattr + +import ( + "crypto/md5" + "encoding/hex" + "strings" +) + +/** + * 通知订单状态时签名 + * 签名方式:md5(order_sn-status--time---appid) + * 2020/08/21 + *gz + */ +func Sign(order_sn, status, time, appid string) string { + return Md5Password(StringJoin(order_sn, "--", status, "--", time, "--", appid)) +} + +//密码加密 +func Md5Password(password string) string { + h := md5.New() + h.Write([]byte(password)) // 需要加密的字符串 + cipher2Str := h.Sum(nil) + sMd5 := hex.EncodeToString(cipher2Str) // 输出加密结果 + return sMd5 +} + +/* + * 连接多个字符串 + * 2019/05/05 + */ +func StringJoin(s ...string) string { + var build strings.Builder + if len(s) > 0 { + for _, v := range s { + build.WriteString(v) + } + } + + return build.String() +} diff --git a/order_sn.go b/order_sn.go new file mode 100644 index 0000000..e0530e9 --- /dev/null +++ b/order_sn.go @@ -0,0 +1,69 @@ +package orderattr + +import ( + "math/rand" + "strconv" + "strings" + "time" +) + +/** + * 订单号规则 + * id后4位(随机4位)+当前时间(10位) + * 2020/08/10 + */ +func NewOrderSn(user_id string) string { + var prefix string + if user_id != "" { + if len(user_id) > 4 { + prefix = user_id[len(user_id)-4:] //截取后4位 + } else { + prefix = user_id + } + } else { + //随机4位 + str := "123456789" + bytes := []byte(str) + result := []byte{} + r := rand.New(rand.NewSource(time.Now().UnixNano())) + for i := 0; i < 4; i++ { + result = append(result, bytes[r.Intn(len(bytes))]) + } + prefix = string(result) + } + + //拼接 + var build strings.Builder + build.WriteString(prefix) + build.WriteString(strconv.FormatInt(time.Now().Unix(), 10)) //当前时间 + + return build.String() + +} + +/** + * 核销码规则 + * 随机2位+当前时间(6位) + * 2020/08/10 + */ +func VerifyNumber() string { + var prefix string + + //随机4位 + str := "123456789" + bytes := []byte(str) + result := []byte{} + r := rand.New(rand.NewSource(time.Now().UnixNano())) + for i := 0; i < 1; i++ { + result = append(result, bytes[r.Intn(len(bytes))]) + } + prefix = string(result) + + //拼接 + var build strings.Builder + build.WriteString(prefix) + build.WriteString(strconv.FormatInt(time.Now().UnixNano(), 10)[10:17]) //当前时间 + + return build.String() + +} diff --git a/order_sn_test.go b/order_sn_test.go new file mode 100644 index 0000000..f72eaa4 --- /dev/null +++ b/order_sn_test.go @@ -0,0 +1,30 @@ +package orderattr + +import ( + "testing" + "time" +) + +func Test_NewNumber(t *testing.T) { + + t.Log(time.Now().UnixNano()) + + var set map[string]int + set = make(map[string]int) + + var i, reply int = 0, 0 + var num string + + for i < 100000 { + i++ + num = VerifyNumber() + if _, ok := set[num]; ok { + reply++ + } else { + set[num] = i + } + } + + t.Log("reply:", reply) + // t.Log(set) +} diff --git a/order_status.go b/order_status.go new file mode 100644 index 0000000..916d1f1 --- /dev/null +++ b/order_status.go @@ -0,0 +1,176 @@ +package site + +/** +'0': 待支付 下单,尚未支付 created +'1':已支付 付款成功,此状态可退款 payed +'5':已验证 虚拟订单,验证核销码 using +'6':已完成 与供应商结算后,订单结束。配送订单配送完成 finished +'7':已取消 订单取消 canceled +'8':自动取消 到期未支付自动取消,此状态可由管理员手动延期取消 autocanceled +'9':申请退款 用户自助申请退订单,此状态可退款 +'13':已退款 订单完成退款 refunded +'15':自动完成 使用时间过期,自动完成。 finished + +cancel,received,payed,created,finished,using + +## 发货 +is_delivery 是否发货 '1': 已发货 +delivery 发货时间 + +## 收货 +is_received 是否收货 '1': 已收货 +received 收货时间 +*/ + +var statusList map[string]string = map[string]string{ + "0": "created", + "1": "payed", + "5": "using", + "6": "finished", + "7": "canceled", + "8": "autocanceled", + "9": "askForRefund", + "13": "refunded", + "15": "autofinished", +} +var statusExtendList map[string]string = map[string]string{ + "6": "received", //已收货归入已完成, +} + +/*串货订单,供应商系统订单状态*/ +var channelOrderStatusList map[string]string = map[string]string{ + "1": "nosend", + "2": "created", + "3": "payed", + "4": "askForRefund", + "5": "refunded", + "6": "canceled", + "7": "finished", + "8": "payFailed", //支付失败 + "9": "breakoff", //断开 + "10": "fullRefund", //全额退款 + "14": "autocanceled", + "15": "autofinished", + "16": "delivered", +} + +func GetOrderStatusKey(status string) (key string) { + for k, val := range statusList { + if val == status { + key = k + break + } + } + if key == "" { //再找扩展的状态 + for k, val := range statusExtendList { + if val == status { + key = k + break + } + } + } + return +} + +func GetOrderStatusText(key string) (text string) { + + for k, val := range statusList { + if k == key { + text = val + break + } + } + if text == "" { //再找扩展的状态 + for k, val := range statusExtendList { + if k == key { + text = val + break + } + } + } + return +} + +/** + * 返回订单状态描述 + * 2021/01/28 + */ +func GetOrderStatusDescByFlag(flag string) (text string) { + status := GetStatusText(flag) + return GetStatusDesc(status) +} + +/** + * 返回订单状态描述 + * 2020/10/22 + */ +func GetOrderStatusDesc(key string) (text string) { + var statusDesc map[string]string = map[string]string{ + "created": "已创建", + "payed": "已支付", + "askForRefund": "请求退款", + "using": "使用中", + "finished": "已完成", + "canceled": "已取消", + "autocanceled": "自动取消", + "refunded": "已退款", + "autofinished": "自动完成", + "received": "已收货", + } + for k, val := range statusDesc { + if k == key { + text = val + break + } + } + return +} + +func GetChannelOrderStatusKey(status string) (key string) { + for k, val := range channelOrderStatusList { + if val == status { + key = k + break + } + } + return +} + +func GetChannelOrderStatusText(key string) (text string) { + for k, val := range channelOrderStatusList { + if k == key { + text = val + break + } + } + return +} + +/** + * 返回订单状态描述 + * 2020/10/22 + */ +func GetChannelOrderStatusDesc(key string) (text string) { + var statusDesc map[string]string = map[string]string{ + "nosend": "未发送", + "created": "已创建", + "payed": "已支付", + "askForRefund": "请求退款", + "refunded": "已退款", + "canceled": "已取消", + "finished": "已完成", + "payFailed": "支付失败", + "breakoff": "断开", + "fullRefund": "全额退款", + "autocanceled": "自动取消", + "autofinished": "自动取消", + "delivered": "已发货", + } + for k, val := range statusDesc { + if k == key { + text = val + break + } + } + return +} diff --git a/redis.go b/redis.go new file mode 100644 index 0000000..7a76e7d --- /dev/null +++ b/redis.go @@ -0,0 +1,14 @@ +package site + +import ( + "strings" +) + +func GetConfigRedisKey(site_id string) string { + + var build strings.Builder + build.WriteString(site_id) + build.WriteString("_config") + + return build.String() +} diff --git a/sendmsg.go b/sendmsg.go new file mode 100644 index 0000000..c63b450 --- /dev/null +++ b/sendmsg.go @@ -0,0 +1,43 @@ +package site + +/* + * 串货接口,请求及通知订单状态 + */ +var MsgTypeList map[string]string = map[string]string{ + "1": "order_create", // 请求创建订单 + "2": "order_created_notify", //订单已创建通知 + "3": "order_refund", //请求订单退款 + "4": "order_refunded_notify", + "5": "order_pay", //请求支付订单 + "6": "order_payed_notify", + "7": "order_cancel", // 发送取消订单请求 + "8": "order_canceled_notify", + "9": "order_receive", //向被串货方发送订单已收货请求 + "10": "order_received_notify", + "11": "order_finished_notify", //订单已完成通知 + "12": "send_sms", //短信 + "13": "order_delivered_notify", //订单已发货通知 + "14": "order_autocanceled_notify", //订单自动取消通知 + "15": "order_askforrefund_notify", //订单已申请退款通知 + "16": "order_pay_by_master", //订单请求总站支付 +} + +func GetMsgTypeText(key string) (text string) { + for k, val := range MsgTypeList { + if k == key { + text = val + break + } + } + return +} + +func GetMsgTypeKey(text string) (key string) { + for k, val := range MsgTypeList { + if val == text { + key = k + break + } + } + return +} diff --git a/site.go b/site.go new file mode 100644 index 0000000..7b5bf47 --- /dev/null +++ b/site.go @@ -0,0 +1,101 @@ +package site + +import ( + "errors" + "log" + "net/http" + "strings" +) + +func GetSiteInfo(master, host string) (map[string]string, error) { + if master == "" || host == "" { + errors.New("params error") + } + + host = strings.Replace(host, "https://", "", 1) + host = strings.Replace(host, "http://", "", 1) + + //去掉端口 + if strings.Contains(host, ":") { + index := strings.Index(host, ":") + host = host[:index] + } + host = strings.Split(host, ":")[0] + + site, err := GetSiteFromRedis(host) //从redis获取 + if err != nil { + log.Println("从redis获取分站失败", err) + } + if err == nil && len(site) > 0 { + return site, nil + } + + log.Println("从总站", master, "获取分站信息", host) + site, err = GetSiteFromMaster(master, host) //从总站获取 + if err != nil { + log.Println("从总站获取分站信息失败", err) + return site, err + } + + if len(site) > 0 { + if _, ok := site["database"]; !ok { //检查是否有database + log.Println("站点数据没有database", site) + return site, errors.New("站点数据错误,缺少必要参数") + } + if _, ok := site["site_id"]; !ok { //检查是否有site_id + log.Println("站点数据没有site_id", site) + return site, errors.New("站点数据错误,缺少必要参数") + } + SetToRedis(host, site) //存入redis + + return site, nil + } else { + return site, errors.New("数据错误") + } +} + +/** + * 取完整数据库名 + * 2020/06/15 + * gz + */ +func GetDbName(master, host string) (string, error) { + + siteInfo, err := GetSiteInfo(master, host) + if err != nil { + return "", err + } + return siteInfo["database"], nil +} + +/** + * 取站点ID + * 2020/06/15 + * gz + */ +func GetSiteId(master, domain string) (string, error) { + + siteInfo, err := GetSiteInfo(master, domain) + if err != nil { + return "", err + } + if _, ok := siteInfo["site_id"]; !ok { + return "", errors.New("没有找到站点ID") + } + return siteInfo["site_id"], nil +} + +func GetSiteInfoFromReq(master string, req *http.Request) (map[string]string, error) { + + log.Println("header:", req.Header, ";", "body:", req.Body) + + host := GetHost(req) + siteInfo, err := GetSiteInfo(master, host) + if err != nil { + return siteInfo, err + } + if len(siteInfo) > 0 { + siteInfo["host"] = host + } + return siteInfo, nil +} diff --git a/site_test.go b/site_test.go new file mode 100644 index 0000000..c278d79 --- /dev/null +++ b/site_test.go @@ -0,0 +1,23 @@ +package site + +import ( + "strings" + "testing" +) + +func Test_GetSiteInfo(t *testing.T) { + + master := "https://devmaster.tetele.net" + host := "dev.tetele.net:80" + + ret, err := GetSiteInfo(master, host) + + t.Log(ret) + t.Log(err) + + str := " dev.tetele.net" + trim := strings.Trim(str, " ") + t.Log(str) + t.Log(trim) + +} diff --git a/user.go b/user.go new file mode 100644 index 0000000..0e104cb --- /dev/null +++ b/user.go @@ -0,0 +1,25 @@ +package site + +import ( + "crypto/hmac" + "fmt" + + "golang.org/x/crypto/ripemd160" +) + +func EncryptedUserToken(token string, key ...string) string { + + var token_key string = USER_TOKEN_KEY + if len(key) > 0 { + token_key = key[0] + } + + h2 := hmac.New(ripemd160.New, []byte(token_key)) + + h2.Write([]byte(token)) + + hashBytes := h2.Sum(nil) + hashString := fmt.Sprintf("%x", hashBytes) + + return hashString +} diff --git a/user_test.go b/user_test.go new file mode 100644 index 0000000..1ad326c --- /dev/null +++ b/user_test.go @@ -0,0 +1,14 @@ +package site + +import ( + // "fmt" + "testing" +) + +func Test_EncryptedUserToken(t *testing.T) { + //2a12d5373e04a96c2e1862f786e9d054464fc13d + // fmt.Println([]byte("2a12d5373e04a96c2e1862f786e9d054464fc13d")) + token := "5536827c-36a5-4ec7-946d-49e4380c5103" + ret := EncryptedUserToken(token) + t.Log(ret) +} diff --git a/var.go b/var.go new file mode 100644 index 0000000..b2dfcea --- /dev/null +++ b/var.go @@ -0,0 +1,6 @@ +package site + +const SITE_KEY = "allSitesInfo" // 所有站点的信息 + +//用户TOKEN加密转换key +var USER_TOKEN_KEY = "i3d6o32wo8fvs1fvdpwens"