├── .gitignore ├── README.md ├── app ├── constant │ ├── comparer.go │ ├── error.go │ ├── mysql.go │ ├── order.go │ └── redis.go ├── controller │ ├── tob │ │ └── search │ │ │ ├── dsearch.go │ │ │ ├── get.go │ │ │ └── search.go │ ├── toc │ │ └── order │ │ │ ├── addition │ │ │ ├── begin.go │ │ │ ├── commit.go │ │ │ ├── rollback.go │ │ │ └── sync.go │ │ │ ├── orderid │ │ │ ├── generate.go │ │ │ └── search.go │ │ │ ├── search │ │ │ ├── get.go │ │ │ ├── query.go │ │ │ └── search.go │ │ │ └── update │ │ │ └── update.go │ └── tool │ │ └── Tool.go ├── dao │ ├── es │ │ ├── es.go │ │ ├── extension.go │ │ ├── order_detail.go │ │ ├── order_info.go │ │ ├── query │ │ │ ├── i.go │ │ │ ├── nested_query.go │ │ │ └── query.go │ │ └── search.go │ ├── mysql │ │ ├── dao.go │ │ ├── extension.go │ │ ├── extension_idx.go │ │ ├── order_detail.go │ │ ├── order_detail_idx.go │ │ ├── order_info.go │ │ ├── order_info_idx.go │ │ └── order_info_with_order_id.go │ └── redis │ │ ├── dao.go │ │ ├── order.go │ │ ├── order_id.go │ │ ├── tx_order.go │ │ └── user_id.go ├── i │ ├── controller │ │ └── controller.go │ └── dao │ │ └── mysql │ │ └── dao.go ├── middlewares │ ├── cors.go │ └── sign.go ├── model │ ├── addition │ │ ├── insertion.go │ │ └── update.go │ ├── finishing │ │ ├── insertion.go │ │ └── update.go │ ├── order │ │ ├── extension.go │ │ ├── order_detail.go │ │ ├── order_id.go │ │ └── order_info.go │ ├── tob │ │ ├── get.go │ │ └── search.go │ ├── toc │ │ ├── get.go │ │ ├── orderid │ │ │ └── search.go │ │ ├── query.go │ │ └── search.go │ └── update │ │ └── update.go ├── output │ ├── order.go │ └── orderid.go ├── params │ ├── addition.go │ ├── tob.go │ ├── toc.go │ └── update.go ├── router │ └── router.go └── utils │ ├── app.go │ ├── casting.go │ ├── database.go │ ├── dbutil.go │ ├── filter.go │ ├── functions.go │ ├── gpool.go │ ├── httpResponse.go │ ├── idworker.go │ ├── jstime.go │ ├── message │ └── message.go │ ├── order.go │ ├── shadow.go │ └── sorter.go ├── cmd └── powerorder │ ├── main.go │ └── main_test.go ├── conf ├── conf.ini ├── conf_dev.ini └── order.ini ├── go.mod └── version └── version.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor/ 3 | go.sum 4 | .git/ 5 | .DS_Store 6 | .project 7 | .buildpath 8 | 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # power-order订单平台 2 | ## 介绍 3 | power-order提供一个通用、易扩展和高性能的订单平台解决方案。 4 | 5 | -------------------------------------------------------------------------------- /app/constant/comparer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-21 02:27:41 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-09 06:54:06 6 | * @Description: 7 | */ 8 | 9 | package constant 10 | 11 | const ( 12 | // 规则引擎中支持的比较类型 13 | IntGreater uint = 0 14 | IntGreaterOrEqual uint = 1 15 | IntLess uint = 2 16 | IntLessOrEqual uint = 3 17 | IntEqual uint = 4 18 | IntNotEqual uint = 5 19 | IntWithIn uint = 6 20 | IntNotWithIn uint = 7 21 | 22 | StrGreater uint = 100 23 | StrGreaterOrEqual uint = 101 24 | StrLess uint = 102 25 | StrLessOrEqual uint = 103 26 | StrEqual uint = 104 27 | StrNotEqual uint = 105 28 | StrWithIn uint = 106 29 | StrNotWithIn uint = 107 30 | ) 31 | -------------------------------------------------------------------------------- /app/constant/error.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | // 通用错误码 4 | const( 5 | SYSTEM_ERROR = 50000 6 | ORDER_SYSTEM_ERROR = 60000 7 | ORDER_SIGNATURE_ERROR = 60001 8 | ORDER_SIGNATURE_REQUIRED_ERROR = 60002 9 | ORDER_SIGNATURE_TIMEFORMAT_ERROR = 60003 10 | ORDER_SIGNATURE_EXPIRED_ERROR = 60004 11 | ORDER_SIGNATURE_VERIFIED_ERROR = 60005 12 | 13 | ORDER_TOC_ORDER_INFO_ERROR = 61000 14 | ) 15 | 16 | var ERROR_MSG_MAP map[int]string = map[int]string{ 17 | 50000 : "system error", 18 | 60000 : "order system error", 19 | 60001 : "order signature error", 20 | 60002 : "order signature required", 21 | 60003 : "order signature time format error", 22 | 60004 : "order system signature expired", 23 | 60005 : "order system signature verified error", 24 | 61000 : "save order info error", 25 | 26 | } -------------------------------------------------------------------------------- /app/constant/mysql.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-02-16 13:19:15 4 | * @LastEditors: Please set LastEditors 5 | * @LastEditTime: 2021-01-04 11:05:40 6 | * @Description: 7 | */ 8 | 9 | package constant 10 | 11 | const ( 12 | DBWriter = "writer" 13 | DBReader = "reader" 14 | ) 15 | 16 | // 分库分表配置 17 | const ( 18 | // 分库数量配置 19 | DatabaseNum uint = 4 20 | // 分表order_info数量配置 21 | TableOrderInfoNum uint = 4 22 | // 分表order_detail数量配置 23 | TableOrderDetailNum uint = 32 24 | ) 25 | 26 | // 分库和分表的前缀 27 | const ( 28 | DatabaseNamePrx = "pwr_platform_order_" 29 | TableOrderInfoNamePrx = "order_info_" 30 | TableOrderDetailNamePrx = "order_detail_" 31 | 32 | DatabaseIdxName = "pwr_platform_order_idx" 33 | TableOrderInfoIdxNamePrx = "order_info_idx_" 34 | TableOrderDetailIdxNamePrx = "order_detail_idx_" 35 | ) 36 | 37 | /* 业务线1扩展表*/ 38 | const ( 39 | BUS1_GrouponInfoTableName = "groupon_info" 40 | BUS1_GrouponDetailTableName = "groupon_detail" 41 | BUS1_PromotionInfoTableName = "promotion_info" 42 | ) 43 | 44 | /* 业务线2扩展表*/ 45 | const ( 46 | BUS2_ExtensionTableName = "order_info_ext" 47 | BUS2_ExtensionPromotionInfoTableName = "promotion_info" 48 | ) 49 | -------------------------------------------------------------------------------- /app/constant/order.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-06 22:56:33 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-01-11 22:40:06 6 | * @Description: 订单相关的常量定义 7 | */ 8 | package constant 9 | 10 | //业务线AppId 11 | const ( 12 | AppBus1 uint = 1 13 | AppBus2 uint = 8 14 | ) 15 | 16 | // 下单tx_status 状态 17 | const ( 18 | TxStatusUnCommitted uint = 0 19 | TxStatusCommitted uint = 1 20 | ) 21 | 22 | const ( 23 | OrderInfo string = "info" 24 | OrderDetail string = "detail" 25 | ) 26 | 27 | // 订单支付状态 28 | const ( 29 | StatusDefault int = 1 30 | StatusPaying int = 2 31 | StatusPaySuccess int = 3 32 | StatusCancel int = 3 33 | StatusCancelBySys int = 3 34 | ) 35 | 36 | const ( 37 | Addition = 1 // 添加订单 38 | Sync = 2 // 订单同步 39 | ZeroTime = -62135596800 40 | ) 41 | 42 | const ( 43 | MaxOrderCountAtInsertionTime uint = 20 44 | MaxOrderCountAtSearchTime uint = 20 45 | 46 | MaxBatchOrderCount = 20 47 | MaxBatchUserCount = 5 48 | MaxBatchUserOrderCount = 10 49 | ) 50 | 51 | const ( 52 | TopicPrex = "pwr_order_" 53 | ) 54 | 55 | const ( 56 | IndexPrex = "pwr_order_" 57 | 58 | OrderInfoType = OrderInfo 59 | OrderDetailType = OrderDetail 60 | ) 61 | 62 | const ( 63 | Desc = "desc" 64 | Asc = "asc" 65 | ) 66 | 67 | const ( 68 | Info = "info" 69 | Detail = "detail" 70 | ) 71 | 72 | // 自定义类型 73 | const ( 74 | TypeString = 1 75 | TypeInt = 2 76 | TypeTimestamp = 3 77 | TypeTimestamp2 = 101 //类型为string,但要转成int 78 | ) 79 | -------------------------------------------------------------------------------- /app/constant/redis.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-11 22:39:53 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-12 13:01:36 6 | * @Description: 7 | */ 8 | package constant 9 | 10 | const ( 11 | Order_HashKeyPrx = "power_order_" 12 | Order_HashSubKey_UserId = "user_id" 13 | Order_HashSubKey_OrderId = "order_id" 14 | Order_HashSubKey_Info = Info 15 | Order_HashSubKey_Detail = Detail 16 | 17 | OrderId_SetKeyPrx = "power_order_id_" 18 | 19 | UserId_KVKeyPrx = "power_order_user_id_" 20 | ) 21 | const ( 22 | OrderId_MemberDefault = "-1" 23 | Order_HashFieldDefault = "-2" 24 | Order_HashFieldDefaultDecode = "\"-2\"" 25 | ) 26 | const ( 27 | Order_SecondsPassedFromCreatedOneDay = 60 * 60 * 24 28 | Order_SecondsPassedFromCreatedOneMonth = Order_SecondsPassedFromCreatedOneDay * 30 29 | ) 30 | 31 | const ( 32 | Order_ExpiredTimeNoOperation = -2 33 | Order_ExpiredTimeNoCache = -1 34 | Order_ExpiredTimeTwoMinutes = 2 * 60 35 | Order_ExpiredTimeOneDay = 24 * 60 * 60 36 | 37 | OrderId_ExpiredTimeDefault = 24 * 60 * 60 38 | OrderId_ExpiredTime30Days = 24 * 60 * 60 * 30 39 | 40 | //UserId_ExpiredTimeDefault = 5 * 30 * 24 * 60 * 60 41 | //设置为不过期 42 | UserId_ExpiredTimeDefault = -1 //5 * 30 * 24 * 60 * 60 43 | ) 44 | 45 | // redis集群名称配置 46 | const ( 47 | Order_Redis_Cluster = "order" 48 | ) 49 | 50 | var Order_HashSubKey map[uint][]string 51 | 52 | func init() { 53 | Order_HashSubKey = make(map[uint][]string) 54 | Order_HashSubKey[AppBus1] = []string{Order_HashSubKey_UserId, Order_HashSubKey_Info, Order_HashSubKey_Detail, BUS1_GrouponInfoTableName, BUS1_GrouponDetailTableName, BUS2_ExtensionPromotionInfoTableName} 55 | Order_HashSubKey[AppBus2] = []string{Order_HashSubKey_UserId, Order_HashSubKey_Info, Order_HashSubKey_Detail, BUS2_ExtensionTableName, BUS2_ExtensionPromotionInfoTableName} 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/controller/tob/search/dsearch.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-02-01 02:30:41 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime : 2020-02-16 10:12:29 6 | * @Description: 7 | */ 8 | 9 | package search 10 | 11 | import ( 12 | "fmt" 13 | "github.com/gin-gonic/gin" 14 | logger "github.com/tal-tech/loggerX" 15 | "net/http" 16 | "powerorder/app/constant" 17 | "powerorder/app/model/tob" 18 | "powerorder/app/params" 19 | "powerorder/app/utils" 20 | ) 21 | 22 | type DSearch struct { 23 | //controller.Base 24 | } 25 | 26 | func (i DSearch) Index(ctx *gin.Context) { 27 | 28 | var param params.ReqDSearch 29 | 30 | if err := ctx.ShouldBindJSON(¶m); err != nil { 31 | resp := utils.Error(err) 32 | ctx.JSON(http.StatusOK, resp) 33 | return 34 | } 35 | reqParam, _ := i.initData(param) 36 | i.validate(ctx, reqParam) 37 | 38 | searchModel := tob.NewSearch(utils.TransferToContext(ctx), reqParam.AppId, true) 39 | result, total, err := searchModel.Get(reqParam) 40 | 41 | if err != nil { 42 | resp := utils.Error(err) 43 | ctx.JSON(http.StatusOK, resp) 44 | } else { 45 | ctx.JSON(http.StatusOK, utils.Success(map[string]interface{}{"total": total, "result": result})) 46 | } 47 | } 48 | 49 | func (i DSearch) validate(ctx *gin.Context, param params.TobReqSearch) { 50 | if param.AppId == 0 { 51 | resp := utils.Error(logger.NewError("error app_id")) 52 | ctx.JSON(http.StatusOK, resp) 53 | return 54 | } 55 | if len(param.Fields[0]) == 0 { 56 | resp := utils.Error(logger.NewError("empty field")) 57 | ctx.JSON(http.StatusOK, resp) 58 | return 59 | } 60 | if param.End-param.Start > constant.MaxOrderCountAtSearchTime { 61 | resp := utils.Error(logger.NewError(fmt.Sprintf("max order count at search time is %d", constant.MaxOrderCountAtSearchTime))) 62 | ctx.JSON(http.StatusOK, resp) 63 | return 64 | } 65 | } 66 | 67 | func (i DSearch) initData(param params.ReqDSearch) (req params.TobReqSearch, err error) { 68 | if param.End == 0 { 69 | param.End = constant.MaxOrderCountAtSearchTime 70 | } 71 | req.AppId = param.AppId 72 | req.End = param.End 73 | req.Start = param.Start 74 | req.Sorter = param.Sorter 75 | req.Fields = make([]string, 1) 76 | req.Fields[0] = param.Field 77 | 78 | if param.Field == constant.Detail { 79 | req.DetailFilter = param.Filter 80 | } else { 81 | req.ExtFilter = make(map[string][][][]interface{}, 0) 82 | req.ExtFilter[param.Field] = param.Filter 83 | } 84 | return 85 | } 86 | -------------------------------------------------------------------------------- /app/controller/tob/search/get.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2019-12-31 11:07:17 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime : 2020-02-04 14:10:49 6 | * @Description: 7 | */ 8 | package search 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | logger "github.com/tal-tech/loggerX" 13 | "net/http" 14 | "powerorder/app/constant" 15 | "powerorder/app/model/tob" 16 | "powerorder/app/params" 17 | "powerorder/app/utils" 18 | ) 19 | 20 | type Get struct { 21 | //controller.Base 22 | } 23 | 24 | /** 25 | * @description: 根据订单号查询,默认订单号可以解析分库分表的信息 26 | * @params {ctx} 27 | * @return: 28 | */ 29 | func (g Get) Index(ctx *gin.Context) { 30 | var param params.ReqGet 31 | if err := ctx.ShouldBindJSON(¶m); err != nil { 32 | resp := utils.Error(err) 33 | ctx.JSON(http.StatusOK, resp) 34 | return 35 | } 36 | 37 | g.validate(ctx, param) 38 | getModel := tob.NewGet(utils.TransferToContext(ctx), param.AppId) 39 | result, err := getModel.Get(param.OrderIds, param.Fields) 40 | 41 | if err != nil { 42 | resp := utils.Error(err) 43 | ctx.JSON(http.StatusOK, resp) 44 | } else { 45 | ctx.JSON(http.StatusOK, utils.Success(result)) 46 | } 47 | } 48 | 49 | func (g Get) validate(ctx *gin.Context, param params.ReqGet) { 50 | if param.AppId == 0 { 51 | resp := utils.Error(logger.NewError("error app_id")) 52 | ctx.JSON(http.StatusOK, resp) 53 | return 54 | } 55 | 56 | if len(param.OrderIds) == 0 { 57 | resp := utils.Error(logger.NewError("empty order_ids")) 58 | ctx.JSON(http.StatusOK, resp) 59 | return 60 | } 61 | 62 | if len(param.OrderIds) > constant.MaxBatchOrderCount { 63 | resp := utils.Error(logger.NewError("count of order_ids is limited")) 64 | ctx.JSON(http.StatusOK, resp) 65 | return 66 | } 67 | 68 | if len(param.Fields) == 0 { 69 | resp := utils.Error(logger.NewError("empty fields")) 70 | ctx.JSON(http.StatusOK, resp) 71 | return 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /app/controller/tob/search/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-02-01 02:30:41 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-16 09:36:59 6 | * @Description: 7 | */ 8 | 9 | package search 10 | 11 | import ( 12 | "fmt" 13 | "github.com/gin-gonic/gin" 14 | logger "github.com/tal-tech/loggerX" 15 | "net/http" 16 | "powerorder/app/constant" 17 | "powerorder/app/model/tob" 18 | "powerorder/app/params" 19 | "powerorder/app/utils" 20 | ) 21 | 22 | type Search struct { 23 | //controller.Base 24 | } 25 | 26 | /** 27 | * @description: 查找订单接口(2b)-高级 28 | * @params {ctx} 29 | * @return: 30 | */ 31 | func (s Search) Index(ctx *gin.Context) { 32 | var param params.TobReqSearch 33 | if err := ctx.ShouldBindJSON(¶m); err != nil { 34 | resp := utils.Error(err) 35 | ctx.JSON(http.StatusOK, resp) 36 | return 37 | } 38 | 39 | if param.End == 0 { 40 | param.End = constant.MaxOrderCountAtSearchTime 41 | } 42 | s.validate(ctx, param) 43 | var i int 44 | if len(param.DetailFilter) > 0 { 45 | for i = 0; i < len(param.Fields); i++ { 46 | if constant.OrderDetail == param.Fields[i] { 47 | break 48 | } 49 | } 50 | if i > len(param.Fields) { 51 | resp := utils.Error(logger.NewError("detail filter exist but detail field not")) 52 | ctx.JSON(http.StatusOK, resp) 53 | return 54 | } 55 | } 56 | 57 | if len(param.InfoFilter) > 0 { 58 | for i = 0; i < len(param.Fields); i++ { 59 | if constant.OrderInfo == param.Fields[i] { 60 | break 61 | } 62 | } 63 | 64 | if i > len(param.Fields) { 65 | resp := utils.Error(logger.NewError("info filter exist but info field not")) 66 | ctx.JSON(http.StatusOK, resp) 67 | return 68 | } 69 | } 70 | 71 | for ext, _ := range param.ExtFilter { 72 | for i = 0; i < len(param.Fields); i++ { 73 | if ext == param.Fields[i] { 74 | break 75 | } 76 | } 77 | 78 | if i > len(param.Fields) { 79 | resp := utils.Error(logger.NewError(fmt.Sprintf("%s filter exist but %s field not", ext, ext))) 80 | ctx.JSON(http.StatusOK, resp) 81 | return 82 | } 83 | } 84 | 85 | searchModel := tob.NewSearch(utils.TransferToContext(ctx), param.AppId, false) 86 | result, total, err := searchModel.Get(param) 87 | 88 | if err != nil { 89 | resp := utils.Error(err) 90 | ctx.JSON(http.StatusOK, resp) 91 | } else { 92 | ctx.JSON(http.StatusOK, utils.Success(map[string]interface{}{"total": total, "result": result})) 93 | } 94 | } 95 | 96 | func (s Search) validate(ctx *gin.Context, param params.TobReqSearch) { 97 | if param.AppId == 0 { 98 | resp := utils.Error(logger.NewError("error app_id")) 99 | ctx.JSON(http.StatusOK, resp) 100 | return 101 | } 102 | if len(param.Fields) == 0 { 103 | resp := utils.Error(logger.NewError("empty fields")) 104 | ctx.JSON(http.StatusOK, resp) 105 | return 106 | } 107 | if param.End-param.Start > utils.APP_SEA_MAXSIZE { 108 | resp := utils.Error(logger.NewError(fmt.Sprintf("max order count at search time is %d", utils.APP_SEA_MAXSIZE))) 109 | ctx.JSON(http.StatusOK, resp) 110 | return 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/controller/toc/order/addition/begin.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2019-12-31 11:07:17 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-09 14:34:03 6 | * @Description: 7 | */ 8 | package addition 9 | 10 | import ( 11 | "fmt" 12 | "github.com/gin-gonic/gin" 13 | "github.com/openzipkin/zipkin-go/idgenerator" 14 | logger "github.com/tal-tech/loggerX" 15 | "net/http" 16 | "powerorder/app/constant" 17 | "powerorder/app/model/addition" 18 | "powerorder/app/params" 19 | "powerorder/app/utils" 20 | ) 21 | 22 | type Begin struct { 23 | //controller.Base 24 | } 25 | 26 | /** 27 | * @description: 创建订单开始 28 | * @params {type} 29 | * @return: 30 | */ 31 | func (b Begin) Index(ctx *gin.Context) { 32 | var param params.ReqBegin 33 | 34 | if err := ctx.ShouldBindJSON(¶m); err != nil { 35 | resp := utils.Error(err) 36 | ctx.JSON(http.StatusOK, resp) 37 | return 38 | } 39 | txId := GetTxId(ctx) 40 | 41 | insertModel := addition.NewInsertion(constant.Addition, utils.TransferToContext(ctx)) 42 | orderIds, err := insertModel.Insert(param, txId) 43 | if err != nil { 44 | ctx.JSON(http.StatusOK, utils.Error(err)) 45 | return 46 | } 47 | ctx.JSON(http.StatusOK, utils.Success(map[string]interface{}{"tx_id": txId, "order_ids": orderIds})) 48 | } 49 | 50 | func GetTxId(ctx *gin.Context) (txId string) { 51 | return idgenerator.NewRandom64().TraceID().String() 52 | } 53 | 54 | func (b Begin) validate(ctx *gin.Context, param params.ReqBegin) { 55 | if param.AppId == 0 || param.UserId == 0 { 56 | resp := utils.Error(logger.NewError("error app_id or error user_id")) 57 | ctx.JSON(http.StatusOK, resp) 58 | return 59 | } 60 | 61 | if len(param.Additions) == 0 { 62 | resp := utils.Error(logger.NewError("error additions")) 63 | ctx.JSON(http.StatusOK, resp) 64 | return 65 | } else if len(param.Additions) > int(constant.MaxOrderCountAtInsertionTime) { 66 | resp := utils.Error(logger.NewError(fmt.Sprintf("max order count at insertion time is %d", constant.MaxOrderCountAtInsertionTime))) 67 | ctx.JSON(http.StatusOK, resp) 68 | return 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/controller/toc/order/addition/commit.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2019-12-31 11:07:17 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-11 00:54:55 6 | * @Description: 7 | */ 8 | package addition 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | logger "github.com/tal-tech/loggerX" 13 | "net/http" 14 | "powerorder/app/constant" 15 | "powerorder/app/model/addition" 16 | "powerorder/app/params" 17 | "powerorder/app/utils" 18 | ) 19 | 20 | type Commit struct { 21 | //Rollback 22 | } 23 | 24 | /** 25 | * @description: 创建订单-提交 26 | * @params {type} 27 | * @return: 28 | */ 29 | func (c Commit) Index(ctx *gin.Context) { 30 | var param params.ReqRollback 31 | 32 | if err := ctx.ShouldBindJSON(¶m); err != nil { 33 | resp := utils.Error(err) 34 | ctx.JSON(http.StatusOK, resp) 35 | return 36 | } 37 | param.TxStatus = constant.TxStatusCommitted 38 | 39 | c.validate(ctx, param) 40 | 41 | object := addition.NewUpdate(utils.TransferToContext(ctx), param.AppId) 42 | if err := object.UpdateStatus(param); err != nil { 43 | resp := utils.Error(err) 44 | ctx.JSON(http.StatusOK, resp) 45 | return 46 | } 47 | ctx.JSON(http.StatusOK, utils.Success(nil)) 48 | } 49 | 50 | /** 51 | * @description: 52 | * @params {type} 53 | * @return: 54 | */ 55 | func (c Commit) validate(ctx *gin.Context, param params.ReqRollback) { 56 | if param.AppId == 0 || param.UserId == 0 || len(param.TxId) == 0 { 57 | resp := utils.Error(logger.NewError("error app_id or error user_id")) 58 | ctx.JSON(http.StatusOK, resp) 59 | return 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/controller/toc/order/addition/rollback.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2019-12-31 11:07:17 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-27 23:17:38 6 | * @Description: 7 | */ 8 | package addition 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | logger "github.com/tal-tech/loggerX" 13 | "net/http" 14 | "powerorder/app/constant" 15 | "powerorder/app/model/addition" 16 | "powerorder/app/params" 17 | "powerorder/app/utils" 18 | ) 19 | 20 | type Rollback struct { 21 | //controller.Base 22 | } 23 | 24 | /** 25 | * @description: 回滚订单 26 | * @params {type} 27 | * @return: 28 | */ 29 | func (r Rollback) Index(ctx *gin.Context) { 30 | var param params.ReqRollback 31 | if err := ctx.ShouldBindJSON(¶m); err != nil { 32 | resp := utils.Error(err) 33 | ctx.JSON(http.StatusOK, resp) 34 | return 35 | } 36 | if param.TxStatus == constant.TxStatusUnCommitted { 37 | param.TxStatus = 2 // rollback 接口 默认 tx_status为2 38 | } 39 | 40 | r.validate(ctx, param) 41 | 42 | updateModel := addition.NewUpdate(ctx, param.AppId) 43 | if err := updateModel.UpdateStatus(param); err != nil { 44 | resp := utils.Error(err) 45 | ctx.JSON(http.StatusOK, resp) 46 | return 47 | } 48 | ctx.JSON(http.StatusOK, utils.Success(nil)) 49 | } 50 | 51 | func (r Rollback) validate(ctx *gin.Context, param params.ReqRollback) { 52 | if param.AppId == 0 || param.UserId == 0 || len(param.TxId) == 0 { 53 | resp := utils.Error(logger.NewError("error app_id or error user_id")) 54 | ctx.JSON(http.StatusOK, resp) 55 | return 56 | } 57 | 58 | if param.TxStatus == constant.TxStatusCommitted || param.TxStatus == constant.TxStatusUnCommitted { 59 | resp := utils.Error(logger.NewError("error tx_status")) 60 | ctx.JSON(http.StatusOK, resp) 61 | return 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/controller/toc/order/addition/sync.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2019-12-31 11:07:17 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-27 23:30:52 6 | * @Description: 7 | */ 8 | package addition 9 | 10 | import ( 11 | "fmt" 12 | "github.com/gin-gonic/gin" 13 | logger "github.com/tal-tech/loggerX" 14 | "net/http" 15 | "powerorder/app/constant" 16 | "powerorder/app/model/addition" 17 | "powerorder/app/params" 18 | "powerorder/app/utils" 19 | ) 20 | 21 | type Sync struct { 22 | //controller.Base 23 | } 24 | 25 | /** 26 | * @description: 历史数据同步(2c) 27 | * @params {ctx} 28 | * @return: 29 | */ 30 | func (s Sync) Index(ctx *gin.Context) { 31 | var param params.ReqBegin 32 | 33 | if err := ctx.ShouldBindJSON(¶m); err != nil { 34 | resp := utils.Error(err) 35 | ctx.JSON(http.StatusOK, resp) 36 | return 37 | } 38 | s.validate(ctx, param) 39 | 40 | txId := GetTxId(ctx) 41 | 42 | insertModel := addition.NewInsertion(constant.Sync, utils.TransferToContext(ctx)) 43 | if insertModel == nil { 44 | resp := utils.Error(logger.NewError("system error", logger.SYSTEM_DEFAULT)) 45 | ctx.JSON(http.StatusOK, resp) 46 | return 47 | } 48 | orderIds, err := insertModel.Insert(param, txId) 49 | 50 | if err != nil { 51 | ctx.JSON(http.StatusOK, utils.Error(err)) 52 | return 53 | } 54 | ctx.JSON(http.StatusOK, utils.Success(map[string]interface{}{"order_ids": orderIds})) 55 | } 56 | 57 | func (s Sync) validate(ctx *gin.Context, param params.ReqBegin) { 58 | if param.AppId == 0 || param.UserId == 0 { 59 | resp := utils.Error(logger.NewError("error app_id or error user_id", logger.PARAM_ERROR)) 60 | ctx.JSON(http.StatusOK, resp) 61 | return 62 | } 63 | 64 | if len(param.Additions) == 0 { 65 | resp := utils.Error(logger.NewError("error additions")) 66 | ctx.JSON(http.StatusOK, resp) 67 | return 68 | } else if len(param.Additions) > int(constant.MaxOrderCountAtInsertionTime) { 69 | resp := utils.Error(logger.NewError(fmt.Sprintf("max order count at insertion time is %d", constant.MaxOrderCountAtInsertionTime))) 70 | ctx.JSON(http.StatusOK, resp) 71 | return 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/controller/toc/order/orderid/generate.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2019-12-31 11:07:17 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-08 01:15:58 6 | * @Description: 7 | */ 8 | package orderid 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | logger "github.com/tal-tech/loggerX" 13 | "net/http" 14 | "powerorder/app/constant" 15 | "powerorder/app/model/toc" 16 | "powerorder/app/params" 17 | "powerorder/app/utils" 18 | "time" 19 | ) 20 | 21 | type Generate struct { 22 | //controller.Base 23 | } 24 | 25 | /** 26 | * @description: 生成订单号 27 | * @params {ctx} 28 | * @return: 29 | */ 30 | func (g Generate) Index(ctx *gin.Context) { 31 | var param params.ReqGenerate 32 | if err := ctx.ShouldBindJSON(¶m); err != nil { 33 | resp := utils.Error(err) 34 | ctx.JSON(http.StatusOK, resp) 35 | return 36 | } 37 | 38 | var OrderIds []string 39 | now := time.Now() 40 | for i := 0; i < int(param.Num); i++ { 41 | OrderIds = append(OrderIds, utils.GenOrderId(param.AppId, now, param.UserId)) 42 | } 43 | ctx.JSON(http.StatusOK, utils.Success(map[string]interface{}{"order_ids": OrderIds})) 44 | 45 | } 46 | 47 | func (g Generate) validate(ctx *gin.Context, param params.ReqGenerate) { 48 | if param.AppId == 0 || param.UserId == 0 || param.Num == 0 || param.Num > constant.MaxBatchOrderCount { 49 | resp := utils.Error(logger.NewError("error app_id or user_id or num", logger.PARAM_ERROR)) 50 | ctx.JSON(http.StatusOK, resp) 51 | return 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/controller/toc/order/orderid/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-04-08 01:14:43 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-13 00:54:59 6 | * @Description: 7 | */ 8 | 9 | package orderid 10 | 11 | import ( 12 | "github.com/gin-gonic/gin" 13 | logger "github.com/tal-tech/loggerX" 14 | "net/http" 15 | "powerorder/app/model/toc/orderid" 16 | "powerorder/app/params" 17 | "powerorder/app/utils" 18 | ) 19 | 20 | type Search struct { 21 | //controller.Base 22 | } 23 | 24 | /** 25 | * @description: 根据条件查询订单号 26 | * @params {ctx} 27 | * @return: 28 | */ 29 | func (s Search) Index(ctx *gin.Context) { 30 | var param params.ReqOrderIdSearch 31 | if err := ctx.ShouldBindJSON(¶m); err != nil { 32 | resp := utils.Error(err) 33 | ctx.JSON(http.StatusOK, resp) 34 | return 35 | } 36 | s.validate(ctx, param) 37 | 38 | searchModel := orderid.NewSearch(utils.TransferToContext(ctx), param.AppId, param.Table) 39 | ret, _, err := searchModel.Get(param) 40 | if err != nil { 41 | ctx.JSON(http.StatusOK, utils.Error(err)) 42 | return 43 | } 44 | ctx.JSON(http.StatusOK, utils.Success(ret)) 45 | } 46 | 47 | func (s Search) validate(ctx *gin.Context, param params.ReqOrderIdSearch) { 48 | if param.AppId == 0 || param.Num == 0 || param.Num > 256 { 49 | resp := utils.Error(logger.NewError("error app_id or num", logger.PARAM_ERROR)) 50 | ctx.JSON(http.StatusOK, resp) 51 | return 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/controller/toc/order/search/get.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-02-11 23:01:18 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime : 2020-02-12 21:31:19 6 | * @Description: 7 | */ 8 | 9 | package search 10 | 11 | import ( 12 | "github.com/gin-gonic/gin" 13 | logger "github.com/tal-tech/loggerX" 14 | "net/http" 15 | "powerorder/app/constant" 16 | "powerorder/app/model/toc" 17 | "powerorder/app/output" 18 | "powerorder/app/params" 19 | "powerorder/app/utils" 20 | ) 21 | 22 | type Get struct { 23 | //controller.Base 24 | } 25 | 26 | /** 27 | * @description:查找订单接口(2c)-获得订单详情 28 | * @params {ctx} 29 | * @return: 获得订单详情列表 30 | */ 31 | func (g Get) Index(ctx *gin.Context) { 32 | var param params.ReqBGet 33 | 34 | if err := ctx.ShouldBindJSON(¶m); err != nil { 35 | resp := utils.Error(err) 36 | ctx.JSON(http.StatusOK, resp) 37 | return 38 | } 39 | g.validate(ctx, param) 40 | 41 | out, err := g.getOrders(ctx, param) 42 | if err != nil { 43 | resp := utils.Error(err) 44 | ctx.JSON(http.StatusOK, resp) 45 | return 46 | } 47 | ctx.JSON(http.StatusOK, utils.Success(out)) 48 | } 49 | 50 | func (g Get) getOrders(ctx *gin.Context, param params.ReqBGet) (ret map[string]output.Order, err error) { 51 | ret = make(map[string]output.Order, 0) 52 | for i := 0; i < len(param.Get); i++ { 53 | order := toc.NewGet(utils.TransferToContext(ctx), param.AppId, param.Get[i].UserId) 54 | result, _, err := order.Get(param.Get[i].OrderIds, param.Fields) 55 | if err != nil { 56 | return 57 | } 58 | for OrderId, OrderInfo := range result { 59 | ret[OrderId] = OrderInfo 60 | } 61 | } 62 | return 63 | } 64 | 65 | func (g Get) validate(ctx *gin.Context, param params.ReqBGet) { 66 | if param.AppId == 0 { 67 | resp := utils.Error(logger.NewError("error app_id")) 68 | ctx.JSON(http.StatusOK, resp) 69 | return 70 | } 71 | if len(param.Fields) == 0 { 72 | resp := utils.Error(logger.NewError("empty fields")) 73 | ctx.JSON(http.StatusOK, resp) 74 | return 75 | } 76 | 77 | if len(param.Get) == 0 { 78 | resp := utils.Error(logger.NewError("empty get")) 79 | ctx.JSON(http.StatusOK, resp) 80 | return 81 | } 82 | 83 | if len(param.Get) > constant.MaxBatchOrderCount { 84 | resp := utils.Error(logger.NewError("count of get is limited")) 85 | ctx.JSON(http.StatusOK, resp) 86 | return 87 | } 88 | 89 | for i := 0; i < len(param.Get); i++ { 90 | if len(param.Get[i].OrderIds) > constant.MaxBatchOrderCount { 91 | resp := utils.Error(logger.NewError("count of order_id is limited")) 92 | ctx.JSON(http.StatusOK, resp) 93 | return 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /app/controller/toc/order/search/query.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-21 02:46:03 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-05 23:19:32 6 | * @Description: 7 | */ 8 | 9 | package search 10 | 11 | import ( 12 | "fmt" 13 | "github.com/gin-gonic/gin" 14 | logger "github.com/tal-tech/loggerX" 15 | "net/http" 16 | "powerorder/app/constant" 17 | "powerorder/app/model/toc" 18 | "powerorder/app/params" 19 | "powerorder/app/utils" 20 | ) 21 | 22 | type Query struct { 23 | //controller.Base 24 | } 25 | 26 | /** 27 | * @description:查找订单接口(2c)-筛选订单 28 | * @params {ctx} 29 | * @return: 获得订单详情列表 30 | */ 31 | func (q Query) Index(ctx *gin.Context) { 32 | var param params.ReqQuery 33 | if err := ctx.ShouldBindJSON(¶m); err != nil { 34 | resp := utils.Error(err) 35 | ctx.JSON(http.StatusOK, resp) 36 | return 37 | } 38 | if param.End == 0 { 39 | param.End = constant.MaxOrderCountAtSearchTime 40 | } 41 | q.validate(ctx, param) 42 | 43 | search := toc.NewSearchQuery(utils.TransferToContext(ctx), param.AppId, param.UserId) 44 | result, total, err := search.Get(param) 45 | //result, total, err = search.GetV2(params) 46 | if err != nil { 47 | resp := utils.Error(err) 48 | ctx.JSON(http.StatusOK, resp) 49 | return 50 | } 51 | ctx.JSON(http.StatusOK, utils.Success(map[string]interface{}{"total": total, "result": result})) 52 | return 53 | } 54 | 55 | func (q Query) validate(ctx *gin.Context, param params.ReqQuery) { 56 | if param.AppId == 0 || param.UserId == 0 { 57 | resp := utils.Error(logger.NewError("error app_id or user_id")) 58 | ctx.JSON(http.StatusOK, resp) 59 | return 60 | } 61 | if len(param.Fields) == 0 { 62 | resp := utils.Error(logger.NewError("empty fields")) 63 | ctx.JSON(http.StatusOK, resp) 64 | return 65 | } 66 | if param.StartDate == 0 || param.EndDate == 0 { 67 | resp := utils.Error(logger.NewError("empty start_date or end_date")) 68 | ctx.JSON(http.StatusOK, resp) 69 | return 70 | } 71 | difference := param.EndDate - param.StartDate 72 | if difference > utils.TOC_QUERY_MAX_DAY || difference < utils.TOC_QUERY_MIN_DAY { 73 | resp := utils.Error(logger.NewError("out of range start_date or end_date")) 74 | ctx.JSON(http.StatusOK, resp) 75 | return 76 | } 77 | if param.End-param.Start > utils.APP_SEA_MAXSIZE { 78 | resp := utils.Error(logger.NewError(fmt.Sprintf("max order count at search time is %d", utils.APP_SEA_MAXSIZE))) 79 | ctx.JSON(http.StatusOK, resp) 80 | return 81 | } 82 | 83 | i := 0 84 | if len(param.DetailFilter) > 0 { 85 | for i = 0; i < len(param.Fields); i++ { 86 | if constant.OrderDetail == param.Fields[i] { 87 | break 88 | } 89 | } 90 | if i > len(param.Fields) { 91 | resp := utils.Error(logger.NewError("detail filter exist but detail field not")) 92 | ctx.JSON(http.StatusOK, resp) 93 | return 94 | } 95 | } 96 | 97 | if len(param.InfoFilter) > 0 { 98 | for i = 0; i < len(param.Fields); i++ { 99 | if constant.OrderInfo == param.Fields[i] { 100 | break 101 | } 102 | } 103 | 104 | if i > len(param.Fields) { 105 | resp := utils.Error(logger.NewError("info filter exist but info field not")) 106 | ctx.JSON(http.StatusOK, resp) 107 | return 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/controller/toc/order/search/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-21 02:46:03 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-05 23:19:32 6 | * @Description: 7 | */ 8 | 9 | package search 10 | 11 | import ( 12 | "fmt" 13 | "github.com/gin-gonic/gin" 14 | logger "github.com/tal-tech/loggerX" 15 | "net/http" 16 | "powerorder/app/constant" 17 | "powerorder/app/model/toc" 18 | "powerorder/app/params" 19 | "powerorder/app/utils" 20 | ) 21 | 22 | type Search struct { 23 | //controller.Base 24 | } 25 | 26 | /** 27 | * @description:查找订单接口(2c)-筛选订单 28 | * @params {ctx} 29 | * @return: 获得订单详情列表 30 | */ 31 | func (s Search) Index(ctx *gin.Context) { 32 | var param params.ReqSearch 33 | 34 | if err := ctx.ShouldBindJSON(¶m); err != nil { 35 | resp := utils.Error(err) 36 | ctx.JSON(http.StatusOK, resp) 37 | return 38 | } 39 | if param.End == 0 { 40 | param.End = constant.MaxOrderCountAtSearchTime 41 | } 42 | var result interface{} 43 | var total uint 44 | search := toc.NewSearch(utils.TransferToContext(ctx), param.AppId, param.UserId) 45 | result, total, err := search.Get(param) 46 | //result, total, err = search.GetV2(params) 47 | 48 | if err != nil { 49 | resp := utils.Error(err) 50 | ctx.JSON(http.StatusOK, resp) 51 | return 52 | } 53 | ctx.JSON(http.StatusOK, utils.Success(map[string]interface{}{"total": total, "result": result})) 54 | } 55 | 56 | func (s Search) validate(ctx *gin.Context, param params.ReqSearch) { 57 | 58 | if len(param.Fields) == 0 { 59 | resp := utils.Error(logger.NewError("empty fields")) 60 | ctx.JSON(http.StatusOK, resp) 61 | return 62 | } 63 | if param.End-param.Start > utils.APP_SEA_MAXSIZE { 64 | resp := utils.Error(logger.NewError(fmt.Sprintf("max order count at search time is %d", utils.APP_SEA_MAXSIZE))) 65 | ctx.JSON(http.StatusOK, resp) 66 | return 67 | } 68 | var i int 69 | if len(param.DetailFilter) > 0 { 70 | for i = 0; i < len(param.Fields); i++ { 71 | if constant.OrderDetail == param.Fields[i] { 72 | break 73 | } 74 | } 75 | if i > len(param.Fields) { 76 | resp := utils.Error(logger.NewError("detail filter exist but detail field not")) 77 | ctx.JSON(http.StatusOK, resp) 78 | return 79 | } 80 | } 81 | if len(param.InfoFilter) > 0 { 82 | for i = 0; i < len(param.Fields); i++ { 83 | if constant.OrderInfo == param.Fields[i] { 84 | break 85 | } 86 | } 87 | if i > len(param.Fields) { 88 | resp := utils.Error(logger.NewError("info filter exist but info field not")) 89 | ctx.JSON(http.StatusOK, resp) 90 | return 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/controller/toc/order/update/update.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-19 23:07:48 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-01-28 01:35:48 6 | * @Description: 7 | */ 8 | 9 | package update 10 | 11 | import ( 12 | "github.com/gin-gonic/gin" 13 | logger "github.com/tal-tech/loggerX" 14 | "net/http" 15 | "powerorder/app/model/update" 16 | "powerorder/app/params" 17 | "powerorder/app/utils" 18 | ) 19 | 20 | type Update struct { 21 | //controller.Base 22 | } 23 | 24 | /** 25 | * @description: 更新订单接口(2c) 26 | * @params {ctx} 27 | * @return: 28 | */ 29 | func (u Update) Index(ctx *gin.Context) { 30 | var param params.ReqUpdate 31 | if err := ctx.ShouldBindJSON(¶m); err != nil { 32 | resp := utils.Error(err) 33 | ctx.JSON(http.StatusOK, resp) 34 | return 35 | } 36 | u.validate(ctx, param) 37 | updateModel := update.NewUpdate(utils.TransferToContext(ctx), param.AppId, param.UserId) 38 | if err := updateModel.Update(param); err != nil { 39 | resp := utils.Error(err) 40 | ctx.JSON(http.StatusOK, resp) 41 | return 42 | } 43 | ctx.JSON(http.StatusOK, utils.Success([]interface{}{})) 44 | } 45 | 46 | func (u Update) validate(ctx *gin.Context, param params.ReqUpdate) { 47 | if param.AppId == 0 || param.UserId == 0 { 48 | resp := logger.NewError("error app_id or error user_id") 49 | ctx.JSON(http.StatusOK, utils.Error(resp)) 50 | return 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/controller/tool/Tool.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2019-12-31 14:20:01 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2019-12-31 14:26:59 6 | * @Description: 7 | */ 8 | package tool 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | "net/http" 13 | "os" 14 | "powerorder/app/utils" 15 | ) 16 | 17 | //HealthCheck 健康检测 18 | func HealthCheck(ctx *gin.Context) { 19 | resp := utils.Success(os.Getpid()) 20 | ctx.JSON(http.StatusOK, resp) 21 | return 22 | } 23 | -------------------------------------------------------------------------------- /app/dao/es/es.go: -------------------------------------------------------------------------------- 1 | package es 2 | 3 | import ( 4 | "github.com/olivere/elastic" 5 | "github.com/pkg/errors" 6 | "github.com/spf13/cast" 7 | "github.com/tal-tech/xtools/confutil" 8 | "net/http" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | const ( 14 | defaultHTTPTimeout = 10 * time.Second 15 | ) 16 | 17 | var ( 18 | client *elastic.Client 19 | once sync.Once 20 | ) 21 | 22 | func EsClient() *elastic.Client { 23 | if client == nil { 24 | if err := InitEngine(); err != nil { 25 | } 26 | } 27 | return client 28 | } 29 | 30 | //InitEngine 初始化 31 | func InitEngine() (err error) { 32 | once.Do(func() { 33 | //load config 34 | cfg := confutil.GetConfStringMap("ElasticSearch") 35 | 36 | URLs := confutil.GetConfs("ElasticSearch", "url") 37 | if len(URLs) == 0 { 38 | err = errors.New("config ElasticSearch.url does not exists") 39 | return 40 | } 41 | options := []elastic.ClientOptionFunc{ 42 | elastic.SetURL(URLs...), 43 | elastic.SetSniff(cast.ToBool(cfg["sniff"])), 44 | elastic.SetHealthcheck(true), 45 | elastic.SetHealthcheckInterval(10 * time.Second), 46 | } 47 | //http超时 48 | timeout := cast.ToDuration(cfg["timeout"]) 49 | if timeout == 0 { 50 | timeout = defaultHTTPTimeout 51 | } 52 | httpclient := &http.Client{ 53 | Timeout: timeout, 54 | } 55 | options = append(options, elastic.SetHttpClient(httpclient)) 56 | //创建 57 | client, err = elastic.NewClient(options...) 58 | if err != nil { 59 | return 60 | } 61 | }) 62 | return 63 | } -------------------------------------------------------------------------------- /app/dao/es/extension.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-31 09:55:04 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-03 11:25:34 6 | * @Description: 7 | */ 8 | 9 | package es 10 | 11 | import ( 12 | "context" 13 | "encoding/json" 14 | "github.com/olivere/elastic" 15 | "net/http" 16 | "powerorder/app/constant" 17 | query2 "powerorder/app/dao/es/query" 18 | "powerorder/app/output" 19 | "powerorder/app/params" 20 | "strings" 21 | ) 22 | 23 | type ExtensionSearch struct { 24 | Search 25 | } 26 | 27 | // 查询ES时,使用keyword类型。 28 | var extensionKeywordFieldMap = map[interface{}]int{ 29 | "order_id": 1, 30 | } 31 | 32 | /** 33 | * @description: 34 | * @params {uAppId} 35 | * @params {header} 36 | * @return: 37 | */ 38 | func NewExtensionSearch(uAppId uint, header http.Header) *ExtensionSearch { 39 | object := new(ExtensionSearch) 40 | 41 | object.Search = *NewSearch(uAppId, header) 42 | return object 43 | } 44 | 45 | /** 46 | * @description: 47 | * @params {type} 48 | * @return: 49 | */ 50 | func (this *ExtensionSearch) Get(param params.TobReqSearch) (o []output.Order, total uint, err error) { 51 | var ExtName string 52 | for i := 0; i < len(param.Fields); i++ { 53 | if param.Fields[i] == constant.Order_HashSubKey_Detail || param.Fields[i] == constant.Order_HashSubKey_Info { 54 | continue 55 | } 56 | ExtName = param.Fields[i] 57 | break 58 | } 59 | 60 | this.InitIndexName(ExtName) 61 | 62 | var Filter [][][]interface{} 63 | 64 | Filter, _ = param.ExtFilter[ExtName] 65 | 66 | rq := elastic.NewBoolQuery() 67 | r := elastic.NewBoolQuery() 68 | 69 | for i := 0; i < len(Filter); i++ { 70 | nq := query2.NewQuery(nil) 71 | for j := 0; j < len(Filter[i]); j++ { 72 | err = nq.Add(Filter[i][j]) 73 | if err != nil { 74 | return 75 | } 76 | } 77 | r.Should(nq.Get()) 78 | 79 | } 80 | sortArr := Sort(param.Sorter, extensionKeywordFieldMap) 81 | r.MinimumNumberShouldMatch(1) 82 | rq.Filter(r) 83 | service := EsClient().Search(this.strIndexName) 84 | 85 | for i := 0; i < len(sortArr); i++ { 86 | for k, v := range sortArr[i] { 87 | service = service.Sort(k, v) 88 | } 89 | } 90 | 91 | ret, err := service.Query(rq). 92 | From(int(param.Start)). 93 | Size(int(param.End - param.Start)). 94 | Headers(this.header). 95 | Type(ExtName). 96 | Do(context.Background()) 97 | 98 | if err != nil { 99 | return 100 | } 101 | datas := ret.Hits.Hits 102 | total = uint(ret.TotalHits()) 103 | num := len(ret.Hits.Hits) 104 | o = make([]output.Order, num) 105 | for i := 0; i < num; i++ { 106 | source := datas[i].Source 107 | 108 | data := string(*source) 109 | 110 | data = strings.Replace(data, "_time\": \"0000-00-00 00:00:00\"", "_time\": \"0001-01-01 00:00:00\"", -1) 111 | 112 | o[i].Extensions = make(map[string][]map[string]interface{}, 0) 113 | 114 | o[i].Extensions[ExtName] = make([]map[string]interface{}, 1) 115 | err = json.Unmarshal([]byte(data), &(o[i].Extensions[ExtName][0])) 116 | 117 | if err != nil { 118 | return 119 | } 120 | } 121 | 122 | return 123 | } 124 | -------------------------------------------------------------------------------- /app/dao/es/order_detail.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-31 09:54:59 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-03 11:25:20 6 | * @Description: 7 | */ 8 | package es 9 | 10 | import ( 11 | "context" 12 | "encoding/json" 13 | "github.com/olivere/elastic" 14 | "net/http" 15 | "powerorder/app/constant" 16 | "powerorder/app/dao/mysql" 17 | "powerorder/app/output" 18 | "powerorder/app/params" 19 | "strings" 20 | ) 21 | 22 | type OrderDetailSearch struct { 23 | Search 24 | } 25 | 26 | // 查询ES时,使用keyword类型。 27 | var detailKeywordFieldMap = map[interface{}]int{ 28 | "order_id": 1, 29 | } 30 | 31 | /** 32 | * @description: 33 | * @params {uAppId} 34 | * @params {header} 35 | * @return: 36 | */ 37 | func NewOrderDetailSearch(uAppId uint, header http.Header) *OrderDetailSearch { 38 | object := new(OrderDetailSearch) 39 | object.Search = *NewSearch(uAppId, header) 40 | object.InitIndexName(constant.Detail) 41 | return object 42 | } 43 | 44 | /** 45 | * @description: 46 | * @params {type} 47 | * @return: 48 | */ 49 | func (this *OrderDetailSearch) Get(param params.TobReqSearch) (o []output.Order, total uint, err error) { 50 | bq := elastic.NewBoolQuery() 51 | 52 | if err := GenOrderDetailQuery(param, bq); err != nil { 53 | return 54 | } 55 | 56 | sortArr := Sort(param.Sorter, extensionKeywordFieldMap) 57 | 58 | service := EsClient().Search(this.strIndexName) 59 | 60 | for i := 0; i < len(sortArr); i++ { 61 | for k, v := range sortArr[i] { 62 | service = service.Sort(k, v) 63 | } 64 | } 65 | 66 | ret, err := service.Query(bq). 67 | From(int(param.Start)). 68 | Size(int(param.End - param.Start)). 69 | Headers(this.header). 70 | Type(constant.OrderDetailType). 71 | Do(context.Background()) 72 | 73 | if err != nil { 74 | return 75 | } 76 | datas := ret.Hits.Hits 77 | total = uint(ret.TotalHits()) 78 | num := len(ret.Hits.Hits) 79 | o = make([]output.Order, num) 80 | for i := 0; i < num; i++ { 81 | source := datas[i].Source 82 | data := string(*source) 83 | 84 | data = strings.Replace(data, "_time\": \"0000-00-00 00:00:00\"", "_time\": \"0001-01-01 00:00:00\"", -1) 85 | 86 | o[i].Detail = make([]mysql.OrderDetail, 1) 87 | err = json.Unmarshal([]byte(data), &(o[i].Detail[0])) 88 | 89 | //解析details 90 | if err != nil { 91 | return 92 | } 93 | 94 | } 95 | 96 | return 97 | } 98 | -------------------------------------------------------------------------------- /app/dao/es/order_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-31 09:54:50 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-03 11:24:51 6 | * @Description: 7 | */ 8 | 9 | package es 10 | 11 | import ( 12 | "context" 13 | "encoding/json" 14 | "fmt" 15 | "github.com/bitly/go-simplejson" 16 | "github.com/olivere/elastic" 17 | "net/http" 18 | "powerorder/app/constant" 19 | "powerorder/app/dao/es/query" 20 | "powerorder/app/output" 21 | "powerorder/app/params" 22 | "strings" 23 | ) 24 | 25 | type OrderInfoSearch struct { 26 | Search 27 | } 28 | 29 | // 查询ES时,使用keyword类型。 30 | var keywordFieldMap = map[interface{}]int{ 31 | "parent_order_id": 1, 32 | "order_id": 1, 33 | } 34 | 35 | /** 36 | * @description: 37 | * @params {uAppId} 38 | * @params {header} 39 | * @return: 40 | */ 41 | func NewOrderInfoSearch(uAppId uint, header http.Header) *OrderInfoSearch { 42 | object := new(OrderInfoSearch) 43 | object.Search = *NewSearch(uAppId, header) 44 | object.InitIndexName(constant.Info) 45 | return object 46 | } 47 | 48 | /** 49 | * @description: 50 | * @params {params} 51 | * @return: 52 | */ 53 | func (this *OrderInfoSearch) Get(param params.TobReqSearch) (o []output.Order, total uint, err error) { 54 | bq := elastic.NewBoolQuery() 55 | 56 | if err := GenOrderInfoQuery(param, bq); err != nil { 57 | return 58 | } 59 | if err := GenOrderDetailQuery(param, bq); err != nil { 60 | return 61 | } 62 | if err := GenExtensionQuery(param, bq); err != nil { 63 | return 64 | } 65 | 66 | sortArr := Sort(param.Sorter, extensionKeywordFieldMap) 67 | 68 | service := EsClient().Search(this.strIndexName) 69 | 70 | for i := 0; i < len(sortArr); i++ { 71 | for k, v := range sortArr[i] { 72 | service = service.Sort(k, v) 73 | } 74 | } 75 | 76 | ret, err := service.Query(bq). 77 | RequestCache(true). 78 | From(int(param.Start)). 79 | Size(int(param.End - param.Start)). 80 | Headers(this.header). 81 | Type(constant.OrderInfoType). 82 | Do(context.Background()) 83 | 84 | if err != nil { 85 | return 86 | } 87 | 88 | ExtCount := 0 89 | for i := 0; i < len(param.Fields); i++ { 90 | if param.Fields[i] == constant.OrderInfo || param.Fields[i] == constant.OrderDetail { 91 | } else { 92 | ExtCount++ 93 | } 94 | } 95 | 96 | total = uint(ret.TotalHits()) 97 | datas := ret.Hits.Hits 98 | num := len(ret.Hits.Hits) 99 | 100 | o = make([]output.Order, num) 101 | for i := 0; i < num; i++ { 102 | source := datas[i].Source 103 | data := string(*source) 104 | 105 | data = strings.Replace(data, "_time\": \"0000-00-00 00:00:00\"", "_time\": \"0001-01-01 00:00:00\"", -1) 106 | 107 | for _, field := range param.Fields { 108 | if field == constant.Detail { 109 | err = json.Unmarshal([]byte(data), &(o[i])) 110 | //解析details 111 | if err != nil { 112 | return 113 | } 114 | break 115 | } 116 | } 117 | 118 | for _, field := range param.Fields { 119 | if field == constant.Info { 120 | err = json.Unmarshal([]byte(data), &(o[i].Info)) 121 | //解析info 122 | if err != nil { 123 | return 124 | } 125 | break 126 | } 127 | } 128 | 129 | if ExtCount > 0 { 130 | var res *simplejson.Json 131 | res, err = simplejson.NewJson([]byte(*source)) 132 | 133 | if err != nil { 134 | return 135 | } 136 | o[i].Extensions = make(map[string][]map[string]interface{}, 0) 137 | 138 | for _, field := range param.Fields { 139 | 140 | if field == constant.OrderDetail || field == constant.OrderInfo { 141 | continue 142 | } 143 | o[i].Extensions[field] = make([]map[string]interface{}, 0) 144 | rows, _ := res.Get(field).Array() 145 | 146 | for _, row := range rows { 147 | //对每个row获取其类型,每个row相当于 C++/Golang 中的map、Python中的dict 148 | //每个row对应一个map,该map类型为map[string]interface{},也即key为string类型,value是interface{}类型 149 | if slice, ok := row.(map[string]interface{}); ok { 150 | o[i].Extensions[field] = append(o[i].Extensions[field], slice) 151 | } 152 | } 153 | 154 | } 155 | 156 | } 157 | } 158 | 159 | return 160 | } 161 | 162 | func GenOrderInfoQuery(param params.TobReqSearch, bq *elastic.BoolQuery) (err error) { 163 | if len(param.InfoFilter) < 1 { 164 | return 165 | } 166 | _bq := elastic.NewBoolQuery() 167 | for i := 0; i < len(param.InfoFilter); i++ { 168 | q := query.NewQuery(nil) 169 | for j := 0; j < len(param.InfoFilter[i]); j++ { 170 | field := param.InfoFilter[i][j][0] 171 | if _, ok := keywordFieldMap[field]; ok { 172 | param.InfoFilter[i][j][0] = fmt.Sprintf("%s.keyword", param.InfoFilter[i][j][0]) 173 | err = q.Add(param.InfoFilter[i][j]) 174 | } else { 175 | err = q.Add(param.InfoFilter[i][j]) 176 | } 177 | if err != nil { 178 | return 179 | } 180 | } 181 | _bq.Should(q.Get()) 182 | } 183 | _bq.MinimumNumberShouldMatch(1) 184 | bq.Filter(_bq) 185 | return 186 | } 187 | 188 | func GenOrderDetailQuery(param params.TobReqSearch, bq *elastic.BoolQuery) (err error) { 189 | if len(param.DetailFilter) < 1 { 190 | return 191 | } 192 | _bq := elastic.NewBoolQuery() 193 | for i := 0; i < len(param.DetailFilter); i++ { 194 | nq := query.NewNestedQuery(constant.Order_HashSubKey_Detail, nil) 195 | for j := 0; j < len(param.DetailFilter[i]); j++ { 196 | err = nq.Add(param.DetailFilter[i][j]) 197 | if err != nil { 198 | return 199 | } 200 | } 201 | _bq.Should(nq.Get()) 202 | } 203 | _bq.MinimumNumberShouldMatch(1) 204 | bq.Filter(_bq) 205 | return 206 | } 207 | 208 | func GenExtensionQuery(param params.TobReqSearch, bq *elastic.BoolQuery) (err error) { 209 | if len(param.ExtFilter) < 1 { 210 | return 211 | } 212 | for extension, Filter := range param.ExtFilter { 213 | _bq := elastic.NewBoolQuery() 214 | for i := 0; i < len(Filter); i++ { 215 | nq := query.NewNestedQuery(extension, nil) 216 | for j := 0; j < len(Filter[i]); j++ { 217 | err = nq.Add(Filter[i][j]) 218 | if err != nil { 219 | return 220 | } 221 | } 222 | _bq.Should(nq.Get()) 223 | } 224 | _bq.MinimumNumberShouldMatch(1) 225 | bq.Filter(_bq) 226 | } 227 | return 228 | } 229 | -------------------------------------------------------------------------------- /app/dao/es/query/i.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-02-02 18:27:11 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-02 20:37:24 6 | * @Description: 7 | */ 8 | 9 | package query 10 | 11 | type IQuery interface { 12 | key(key string) string 13 | } 14 | type GenKey struct { 15 | query IQuery 16 | } 17 | 18 | /** 19 | * @description: 20 | * @params {type} 21 | * @return: 22 | */ 23 | func NewGenKey(query IQuery) *GenKey { 24 | object := new(GenKey) 25 | object.query = query 26 | return object 27 | } 28 | 29 | /** 30 | * @description: 31 | * @params {type} 32 | * @return: 33 | */ 34 | func (this *GenKey) key(key string) string { 35 | return this.query.key(key) 36 | } 37 | -------------------------------------------------------------------------------- /app/dao/es/query/nested_query.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-02-01 01:32:06 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-04 01:41:30 6 | * @Description: 7 | */ 8 | 9 | package query 10 | 11 | import ( 12 | "fmt" 13 | "github.com/olivere/elastic" 14 | ) 15 | 16 | type NestedQuery struct { 17 | Query 18 | strKeyPrex string 19 | } 20 | 21 | /** 22 | * @description: 23 | * @params {type} 24 | * @return: 25 | */ 26 | func NewNestedQuery(strKeyPrex string, query *elastic.BoolQuery) *NestedQuery { 27 | object := new(NestedQuery) 28 | object.Query = *NewQuery(query) 29 | object.genKey = *NewGenKey(object) 30 | 31 | object.strKeyPrex = strKeyPrex 32 | return object 33 | } 34 | 35 | /** 36 | * @description: 37 | * @params {type} 38 | * @return: 39 | */ 40 | func (this *NestedQuery) key(key string) string { 41 | 42 | return fmt.Sprintf("%s.%s", this.strKeyPrex, key) 43 | } 44 | 45 | /** 46 | * @description: 47 | * @params {type} 48 | * @return: 49 | */ 50 | func (this *NestedQuery) Get() elastic.Query { 51 | return elastic.NewNestedQuery(this.strKeyPrex, this.query) 52 | } 53 | -------------------------------------------------------------------------------- /app/dao/es/query/query.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-02-01 01:32:06 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-03 23:03:43 6 | * @Description: 7 | */ 8 | 9 | package query 10 | 11 | import ( 12 | "github.com/olivere/elastic" 13 | "github.com/pkg/errors" 14 | "powerorder/app/constant" 15 | "powerorder/app/utils" 16 | ) 17 | 18 | type Query struct { 19 | genKey GenKey 20 | query *elastic.BoolQuery 21 | } 22 | 23 | /** 24 | * @description: 25 | * @params {type} 26 | * @return: 27 | */ 28 | func NewQuery(query *elastic.BoolQuery) *Query { 29 | object := new(Query) 30 | if query == nil { 31 | query = elastic.NewBoolQuery() 32 | } 33 | object.query = query 34 | object.genKey = *NewGenKey(object) 35 | return object 36 | } 37 | 38 | /** 39 | * @description: 40 | * @params {type} 41 | * @return: 42 | */ 43 | func (this *Query) key(key string) string { 44 | return key 45 | } 46 | 47 | /** 48 | * @description: 49 | * @params {type} 50 | * @return: 51 | */ 52 | func (this *Query) Get() elastic.Query { 53 | return this.query 54 | } 55 | 56 | /** 57 | * @description: 58 | * @params {type} 59 | * @return: 60 | */ 61 | func (this *Query) Add(rule []interface{}) (err error) { 62 | 63 | err = nil 64 | if len(rule) != 3 { 65 | return 66 | } 67 | 68 | var key string 69 | var comparer uint 70 | var field interface{} 71 | var fields []interface{} 72 | var ok bool 73 | var ret bool 74 | 75 | key, ret = utils.JsonInterface2String(rule[0]) 76 | if ret == false { 77 | return 78 | } 79 | comparer, ret = utils.JsonInterface2UInt(rule[1]) 80 | 81 | if err != nil { 82 | err = errors.New("error JsonInterface2String") 83 | return 84 | } 85 | 86 | field = rule[2] 87 | key = this.genKey.key(key) 88 | 89 | switch comparer { 90 | case constant.IntGreater: 91 | this.query = this.query.Must(elastic.NewRangeQuery(key).Gt(field)) 92 | break 93 | case constant.IntGreaterOrEqual: 94 | this.query = this.query.Must(elastic.NewRangeQuery(key).Gte(field)) 95 | break 96 | case constant.IntLess: 97 | this.query = this.query.Must(elastic.NewRangeQuery(key).Lt(field)) 98 | break 99 | case constant.IntLessOrEqual: 100 | this.query = this.query.Must(elastic.NewRangeQuery(key).Lte(field)) 101 | break 102 | case constant.IntEqual: 103 | this.query = this.query.Must(elastic.NewTermQuery(key, field)) 104 | break 105 | case constant.IntNotEqual: 106 | this.query = this.query.MustNot(elastic.NewTermQuery(key, field)) 107 | break 108 | case constant.IntNotWithIn: 109 | 110 | if fields, ok = field.([]interface{}); !ok { 111 | err = errors.New("error fields") 112 | return 113 | } 114 | 115 | this.query = this.query.MustNot(elastic.NewTermsQuery(key, fields...)) 116 | break 117 | case constant.IntWithIn: 118 | if fields, ok = field.([]interface{}); !ok { 119 | err = errors.New("error fields") 120 | return 121 | } 122 | 123 | this.query = this.query.Must(elastic.NewTermsQuery(key, fields...)) 124 | break 125 | default: 126 | err = errors.New("error comparer") 127 | } 128 | 129 | return nil 130 | } 131 | -------------------------------------------------------------------------------- /app/dao/es/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-30 20:07:48 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-03-31 13:54:01 6 | * @Description: 7 | */ 8 | package es 9 | 10 | import ( 11 | "fmt" 12 | "net/http" 13 | "powerorder/app/constant" 14 | "powerorder/app/output" 15 | "powerorder/app/params" 16 | ) 17 | 18 | type ISearch interface { 19 | Get(param params.TobReqSearch) (o []output.Order, total uint, err error) 20 | } 21 | type Search struct { 22 | uAppId uint 23 | strIndexName string 24 | header http.Header 25 | } 26 | 27 | /** 28 | * @description: 29 | * @params {} 30 | * @return: 31 | */ 32 | func init() { 33 | _ = InitEngine() 34 | } 35 | 36 | /** 37 | * @description: 38 | * @params {from} 39 | * @return: 40 | */ 41 | func Sort(from []string, keywordFieldMap map[interface{}]int) []map[string]bool { 42 | var to []map[string]bool 43 | var field string 44 | 45 | to = make([]map[string]bool, len(from)/2) 46 | for i := 0; i < len(from); i += 2 { 47 | if _, ok := keywordFieldMap[from[i]]; !ok { 48 | field = from[i] 49 | } else { 50 | field = fmt.Sprintf("%s.keyword", from[i]) 51 | } 52 | 53 | if from[i+1] == constant.Desc { 54 | to[i/2] = map[string]bool{field: false} 55 | } else { 56 | to[i/2] = map[string]bool{field: true} 57 | } 58 | } 59 | return to 60 | } 61 | 62 | /** 63 | * @description: 64 | * @params {type} 65 | * @return: 66 | */ 67 | func NewSearch(uAppId uint, header http.Header) *Search { 68 | object := new(Search) 69 | object.header = header 70 | object.uAppId = uAppId 71 | return object 72 | 73 | } 74 | 75 | /** 76 | * @description: 77 | * @params {type} 78 | * @return: 79 | */ 80 | func (this *Search) InitIndexName(strPostfix string) { 81 | this.strIndexName = fmt.Sprintf("%s%03d_%s", constant.IndexPrex, this.uAppId, strPostfix) 82 | } 83 | -------------------------------------------------------------------------------- /app/dao/mysql/dao.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-03 18:06:07 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-11 09:43:02 6 | * @Description: 7 | */ 8 | package mysql 9 | 10 | import ( 11 | "context" 12 | "github.com/spf13/cast" 13 | logger "github.com/tal-tech/loggerX" 14 | "github.com/tal-tech/torm" 15 | "github.com/tal-tech/xtools/traceutil" 16 | "powerorder/app/utils" 17 | "strconv" 18 | "time" 19 | ) 20 | 21 | var TiDefaultTimestamp utils.Time 22 | 23 | func init() { 24 | formatTime, _ := time.Parse("2006-01-02 15:04:05", "2001-01-01 00:00:00") 25 | 26 | TiDefaultTimestamp = utils.Time(formatTime) 27 | } 28 | 29 | type Dao struct { 30 | torm.DbBaseDao 31 | strDatabaseName string 32 | strTableName string 33 | uUserId uint64 34 | uAppId uint 35 | ctx context.Context 36 | } 37 | 38 | /** 39 | * @description: 40 | * @params {type} 41 | * @return: 42 | */ 43 | func NewSession(ctx context.Context, uAppId uint, uUserId uint64, writer string) *torm.Session { 44 | // 如果是压测 走子库连接 45 | writer = utils.DbShadowHandler(ctx, writer) 46 | object := new(Dao) 47 | object.InitDatabaseName(uAppId, uUserId) 48 | if ins := torm.GetDbInstance(object.strDatabaseName, writer); ins != nil { 49 | return ins.GetSession() 50 | } else { 51 | return nil 52 | } 53 | } 54 | 55 | /** 56 | * @description: 57 | * @params {type} 58 | * @return: 59 | */ 60 | func NewSessionIdx(ctx context.Context, uAppId uint, uUserId uint64, writer string) *torm.Session { 61 | // 如果是压测 走影子库连接 62 | writer = utils.DbShadowHandler(ctx, writer) 63 | object := new(Dao) 64 | object.uAppId = uAppId 65 | object.uUserId = uUserId 66 | object.strDatabaseName = utils.GenDatabaseIdxName() 67 | if ins := torm.GetDbInstance(object.strDatabaseName, writer); ins != nil { 68 | return ins.GetSession() 69 | } else { 70 | return nil 71 | } 72 | } 73 | 74 | /** 75 | * @description: 76 | * @params {type} 77 | * @return: 78 | */ 79 | func (this *Dao) InitDatabaseName(uAppId uint, uUserId uint64) { 80 | this.uAppId = uAppId 81 | this.uUserId = uUserId 82 | 83 | this.strDatabaseName = utils.GenDatabaseName(uUserId) 84 | //fmt.Printf("InitDatabaseName user_id = %d, databasename = %s", uUserId, this.strDatabaseName) 85 | logger.Dx(this.ctx, "dao.mysql.dao.InitDatabaseName", "InitDatabaseName", "user_id = %d, databasename = %s", uUserId, this.strDatabaseName) 86 | //fmt.Printf() 87 | } 88 | 89 | /** 90 | * @description: 91 | * @params {type} 92 | * @return: 93 | */ 94 | func (this *Dao) InitCtx(ctx context.Context) { 95 | this.ctx = ctx 96 | } 97 | 98 | /** 99 | * @description: 100 | * @params {type} 101 | * @return: 102 | */ 103 | func (this *Dao) InitTableName() { 104 | 105 | } 106 | 107 | /** 108 | * @description: 109 | * @params {type} 110 | * @return: 111 | */ 112 | 113 | func (this *Dao) GetTable() string { 114 | return this.strTableName 115 | } 116 | 117 | /** 118 | * @description: 119 | * @params {type} 120 | * @return: 121 | */ 122 | func (this *Dao) UpdateColsWhere(bean interface{}, cols []string, query interface{}, args []interface{}) (int64, error) { 123 | if this.Session == nil { 124 | return this.Engine.Cols(cols...).Where(query, args...).Update(bean) 125 | } else { 126 | return this.Session.Cols(cols...).Where(query, args...).Update(bean) 127 | } 128 | } 129 | 130 | /** 131 | * @description: 132 | * @params {type} 133 | * @return: 134 | */ 135 | func (this *Dao) Where(query interface{}, args ...interface{}) *torm.Session { 136 | if this.Session == nil { 137 | return this.Engine.Where(query, args...) 138 | } else { 139 | return this.Session.Where(query, args...) 140 | } 141 | } 142 | 143 | /** 144 | * @description: 145 | * @params {type} 146 | * @return: 147 | */ 148 | func (this *Dao) SetTableName() { 149 | this.SetTable(this.GetTable()) 150 | } 151 | 152 | /** 153 | * @description: 154 | * @params {type} 155 | * @return: 156 | */ 157 | 158 | func (this *Dao) Create(bean interface{}) (int64, error) { 159 | n := time.Now().UnixNano() 160 | ns := strconv.FormatInt(n, 10) 161 | span, _ := traceutil.Trace(this.ctx, "create_"+ns) 162 | if span != nil { 163 | //节点参数注入 可以从链路追踪界面查看节点数据 164 | span.Tag("db", this.strDatabaseName) 165 | span.Tag("table", this.strTableName) 166 | span.Tag("timeStamp1", cast.ToString(time.Now().UnixNano())) 167 | //切记要回收span 168 | defer span.Finish() 169 | } 170 | r1, r2 := this.DbBaseDao.Create(bean) 171 | 172 | //span2, _ := traceutil.Trace(this.ctx, "create2_" + ns) 173 | if span != nil { 174 | //节点参数注入 可以从链路追踪界面查看节点数据 175 | span.Tag("timeStamp2", cast.ToString(time.Now().UnixNano())) 176 | } 177 | 178 | return r1, r2 179 | } 180 | 181 | /** 182 | * @description: 183 | * @params {type} 184 | * @return: 185 | */ 186 | func (this *Dao) GetColsWhere(bean interface{}, cols []string, query interface{}, args []interface{}, start, limit int) error { 187 | if this.Session == nil { 188 | return this.Engine.Cols(cols...).Where(query, args...).Limit(limit, start).Find(bean) 189 | } else { 190 | return this.Session.Cols(cols...).Where(query, args...).Limit(limit, start).Find(bean) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /app/dao/mysql/extension.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-01 16:45:57 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-23 01:44:12 6 | * @Description: 7 | */ 8 | package mysql 9 | 10 | import ( 11 | "context" 12 | "github.com/tal-tech/torm" 13 | "powerorder/app/utils" 14 | ) 15 | 16 | type ExtensionDao struct { 17 | Dao 18 | strExtName string 19 | } 20 | 21 | /** 22 | * @description: 23 | * @params {type} 24 | * @return: 25 | */ 26 | func NewExtensionDao2(uAppId uint, uUserId uint64, strExtName string, session *torm.Session, ctx context.Context) *ExtensionDao { 27 | object := new(ExtensionDao) 28 | object.InitExtName(strExtName) 29 | object.InitDatabaseName(uAppId, uUserId) 30 | object.UpdateEngine(session) 31 | object.InitTableName() 32 | object.SetTable(object.GetTable()) 33 | object.Dao.InitCtx(ctx) 34 | 35 | return object 36 | } 37 | 38 | /** 39 | * @description: 40 | * @params {type} 41 | * @return: 42 | */ 43 | func NewExtensionDao(ctx context.Context, uAppId uint, uUserId uint64, strExtName string, writer string) *ExtensionDao { 44 | object := new(ExtensionDao) 45 | object.InitExtName(strExtName) 46 | 47 | object.InitDatabaseName(uAppId, uUserId) 48 | writer = utils.DbShadowHandler(ctx, writer) 49 | if ins := torm.GetDbInstance(object.strDatabaseName, writer); ins != nil { 50 | object.UpdateEngine(ins.Engine) 51 | } else { 52 | return nil 53 | } 54 | object.UpdateEngine(uUserId, writer) 55 | object.Engine.ShowSQL(true) 56 | object.InitTableName() 57 | object.SetTable(object.GetTable()) 58 | object.Dao.InitCtx(ctx) 59 | 60 | return object 61 | } 62 | 63 | /** 64 | * @description: 65 | * @params {type} 66 | * @return: 67 | */ 68 | func (this *ExtensionDao) InitDatabaseName(uAppId uint, uUserId uint64) { 69 | this.uAppId = uAppId 70 | this.uUserId = uUserId 71 | this.strDatabaseName = utils.GenDatabaseName(uUserId) 72 | } 73 | 74 | /** 75 | * @description: 76 | * @params {type} 77 | * @return: 78 | */ 79 | func (this *ExtensionDao) InitExtName(strExtName string) { 80 | this.strExtName = strExtName 81 | } 82 | 83 | /** 84 | * @description: 85 | * @params {type} 86 | * @return: 87 | */ 88 | func (this *ExtensionDao) InitTableName() { 89 | this.strTableName, _ = utils.GenOrderExtensionTableName(this.uUserId, this.uAppId, this.strExtName) 90 | } 91 | 92 | /** 93 | * @description: 94 | * @params {type} 95 | * @return: 96 | */ 97 | 98 | func (this *ExtensionDao) Get(arrOrderIds []string) (ret []map[string]interface{}, err error) { 99 | ret = make([]map[string]interface{}, 0) 100 | this.InitSession() 101 | this.InitTableName() 102 | this.SetTable(this.strTableName) 103 | 104 | if len(arrOrderIds) > 0 { 105 | this.BuildQuery(torm.CastToParamIn(arrOrderIds), "order_id") 106 | } 107 | 108 | err = this.Session.Find(&ret) 109 | 110 | for i := 0; i < len(ret); i++ { 111 | for k, v := range ret[i] { 112 | switch v.(type) { 113 | case []byte: 114 | ret[i][k] = string(v.([]byte)) 115 | default: 116 | } 117 | } 118 | } 119 | return 120 | } 121 | -------------------------------------------------------------------------------- /app/dao/mysql/extension_idx.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-01 16:45:57 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-23 01:44:12 6 | * @Description: 7 | */ 8 | package mysql 9 | 10 | import ( 11 | "context" 12 | "github.com/tal-tech/torm" 13 | "powerorder/app/utils" 14 | ) 15 | 16 | type ExtensionIdxDao struct { 17 | Dao 18 | strExtName string 19 | } 20 | 21 | /** 22 | * @description: 23 | * @params {type} 24 | * @return: 25 | */ 26 | func NewExtensionIdxDao2(uAppId uint, strExtName string, session *torm.Session, ctx context.Context) *ExtensionIdxDao { 27 | object := new(ExtensionIdxDao) 28 | object.InitExtName(strExtName) 29 | object.InitDatabaseName(uAppId, 0) 30 | object.UpdateEngine(session) 31 | object.InitTableName() 32 | object.SetTable(object.GetTable()) 33 | object.Dao.InitCtx(ctx) 34 | 35 | return object 36 | } 37 | 38 | /** 39 | * @description: 40 | * @params {type} 41 | * @return: 42 | */ 43 | func NewExtensionIdxDao(ctx context.Context, uAppId uint, strExtName string, writer string) *ExtensionIdxDao { 44 | object := new(ExtensionIdxDao) 45 | object.InitExtName(strExtName) 46 | 47 | object.InitDatabaseName(uAppId, 0) 48 | writer = utils.DbShadowHandler(ctx, writer) 49 | if ins := torm.GetDbInstance(object.strDatabaseName, writer); ins != nil { 50 | object.UpdateEngine(ins.Engine) 51 | } else { 52 | return nil 53 | } 54 | object.Engine.ShowSQL(true) 55 | object.InitTableName() 56 | object.SetTable(object.GetTable()) 57 | object.Dao.InitCtx(ctx) 58 | 59 | return object 60 | } 61 | 62 | /** 63 | * @description: 64 | * @params {type} 65 | * @return: 66 | */ 67 | func (this *ExtensionIdxDao) InitDatabaseName(uAppId uint, id uint64) { 68 | this.uAppId = uAppId 69 | this.strDatabaseName = utils.GenDatabaseIdxName() 70 | } 71 | 72 | /** 73 | * @description: 74 | * @params {type} 75 | * @return: 76 | */ 77 | func (this *ExtensionIdxDao) InitExtName(strExtName string) { 78 | this.strExtName = strExtName 79 | } 80 | 81 | /** 82 | * @description: 83 | * @params {type} 84 | * @return: 85 | */ 86 | func (this *ExtensionIdxDao) InitTableName() { 87 | this.strTableName, _ = utils.GenOrderExtensionIdxTableName(this.uAppId, this.strExtName) 88 | } 89 | 90 | /** 91 | * @description: 92 | * @params {type} 93 | * @return: 94 | */ 95 | 96 | func (this *ExtensionIdxDao) Get(arrOrderIds []string) (ret []map[string]interface{}, err error) { 97 | ret = make([]map[string]interface{}, 0) 98 | this.InitSession() 99 | this.InitTableName() 100 | this.SetTable(this.strTableName) 101 | 102 | if len(arrOrderIds) > 0 { 103 | this.BuildQuery(torm.CastToParamIn(arrOrderIds), "order_id") 104 | } 105 | 106 | err = this.Session.Find(&ret) 107 | 108 | for i := 0; i < len(ret); i++ { 109 | for k, v := range ret[i] { 110 | switch v.(type) { 111 | case []byte: 112 | ret[i][k] = string(v.([]byte)) 113 | default: 114 | } 115 | } 116 | } 117 | return 118 | } 119 | 120 | /** 121 | * @description: 122 | * @params {type} 123 | * @return: 124 | */ 125 | 126 | func (this *ExtensionIdxDao) IsExists(userId uint64, Id int64) (ret bool, err error) { 127 | this.InitSession() 128 | this.InitTableName() 129 | this.SetTable(this.strTableName) 130 | this.Where("user_id = ? and id = ?", userId, Id) 131 | ret, err = this.Session.Exist() 132 | return 133 | } 134 | 135 | /** 136 | * @description: 137 | * @params {type} 138 | * @return: 139 | */ 140 | 141 | func (this *ExtensionIdxDao) GetList(cols []string, query interface{}, args []interface{}, start, limit int) (ret []map[string]interface{}, err error) { 142 | ret = make([]map[string]interface{}, 0) 143 | this.InitSession() 144 | this.InitTableName() 145 | this.SetTable(this.strTableName) 146 | this.Where(query, args) 147 | err = this.Session.Limit(0, limit).Find(&ret) 148 | for i := 0; i < len(ret); i++ { 149 | for k, v := range ret[i] { 150 | switch v.(type) { 151 | case []byte: 152 | ret[i][k] = string(v.([]byte)) 153 | default: 154 | } 155 | } 156 | } 157 | return 158 | } 159 | -------------------------------------------------------------------------------- /app/dao/mysql/order_detail.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-01 16:44:42 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-23 01:44:05 6 | * @Description: 7 | */ 8 | package mysql 9 | 10 | import ( 11 | "context" 12 | "encoding/json" 13 | "github.com/spf13/cast" 14 | "github.com/tal-tech/torm" 15 | "github.com/tal-tech/xtools/traceutil" 16 | "powerorder/app/utils" 17 | "time" 18 | ) 19 | 20 | type OrderDetail struct { 21 | Id int64 `json:"id" xorm:"not null pk autoincr INT(11)"` 22 | AppId uint `json:"app_id" xorm:"not null default 0 comment('接入的商户ID app_id') index(order_app) INT(11)"` 23 | UserId uint64 `json:"user_id" xorm:"not null default 0 comment('user_id') BIGINT(20)"` 24 | OrderId string `json:"order_id" xorm:"not null default '' comment('订单号') index(order_app) CHAR(32)"` 25 | ProductId uint `json:"product_id" xorm:"not null default 0 comment('商品ID') INT(11)"` 26 | ProductType uint `json:"product_type" xorm:"not null default 1 comment('商品类别') TINYINT(4)"` 27 | ProductName string `json:"product_name" xorm:"not null default '' comment('商品名称') VARCHAR(100)"` 28 | ProductNum uint `json:"product_num" xorm:"not null default 1 comment('商品数量') INT(11)"` 29 | ProductPrice uint `json:"product_price" xorm:"not null default 0 comment('商品销售金额') INT(11)"` 30 | CouponPrice uint `json:"coupon_price" xorm:"not null default 0 comment('优惠券分摊金额') INT(11)"` 31 | PromotionPrice uint `json:"promotion_price" xorm:"not null default 0 comment('促销分摊金额') INT(10)"` 32 | PromotionId string `json:"promotion_id" xorm:"not null default 0 comment('促销id') char(24)"` 33 | PromotionType string `json:"promotion_type" xorm:"not null default '' comment('促销类型') VARCHAR(100)"` 34 | ParentProductId uint `json:"parent_product_id" xorm:"not null default 0 comment('父商品ID') INT(11)"` 35 | ParentProductType uint `json:"parent_product_type" xorm:"not null default 1 comment('父商品类别,业务线可自己定义') TINYINT(4)"` 36 | Extras string `json:"extras" xorm:"not null comment('订单商品中附属信息存储 比如促销的关键不变更信息存储之类的,不会来查询,不会用来检索') TEXT"` 37 | CreatedTime utils.Time `json:"created_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') TIMESTAMP"` 38 | UpdatedTime utils.Time `json:"updated_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('修改时间') TIMESTAMP"` 39 | Version uint `json:"version" xorm:"not null default 0 comment('版本号') TINYINT(2)"` 40 | SourceId string `json:"source_id" xorm:"not null default '' comment('热点数据') varchar(255)"` 41 | } 42 | 43 | type OrderDetailDao struct { 44 | Dao 45 | } 46 | 47 | /** 48 | * @description: 49 | * @params {type} 50 | * @return: 51 | */ 52 | func NewOrderDetailDao2(uAppId uint, uUserId uint64, session *torm.Session, ctx context.Context) *OrderDetailDao { 53 | object := new(OrderDetailDao) 54 | object.InitDatabaseName(uAppId, uUserId) 55 | object.UpdateEngine(session) 56 | object.InitTableName() 57 | object.SetTable(object.GetTable()) 58 | object.Dao.InitCtx(ctx) 59 | 60 | return object 61 | } 62 | 63 | /** 64 | * @description: 65 | * @params {type} 66 | * @return: 67 | */ 68 | func NewOrderDetailDao(ctx context.Context, uAppId uint, uUserId uint64, writer string) *OrderDetailDao { 69 | object := new(OrderDetailDao) 70 | object.InitDatabaseName(uAppId, uUserId) 71 | writer = utils.DbShadowHandler(ctx, writer) 72 | if ins := torm.GetDbInstance(object.strDatabaseName, writer); ins != nil { 73 | object.UpdateEngine(ins.Engine) 74 | } else { 75 | return nil 76 | } 77 | object.UpdateEngine(uUserId, writer) 78 | object.Engine.ShowSQL(true) 79 | object.InitTableName() 80 | object.SetTable(object.GetTable()) 81 | object.Dao.InitCtx(ctx) 82 | 83 | return object 84 | } 85 | 86 | /** 87 | * @description: 格式转换,OrderInfo => Map 88 | * @params {info} OrderInfo 89 | * @return: Map 90 | */ 91 | func OrderDetail2Map(detail OrderDetail) (res map[string]interface{}, err error) { 92 | j, err := json.Marshal(detail) 93 | if err != nil { 94 | return nil, err 95 | } 96 | res = make(map[string]interface{}) 97 | json.Unmarshal(j, &res) 98 | return 99 | } 100 | 101 | /** 102 | * @description: 103 | * @params {type} 104 | * @return: 105 | */ 106 | func (this *OrderDetailDao) InitTableName() { 107 | this.strTableName = utils.GenOrderDetailTableName(this.uUserId) 108 | 109 | } 110 | 111 | /** 112 | * @description:根据orderId查询mysql 113 | * @params {arrOrderIds} 订单ID集合 114 | * @return: 订单detail集合 115 | */ 116 | 117 | func (this *OrderDetailDao) Get(arrOrderIds []string) (ret []OrderDetail, err error) { 118 | span, _ := traceutil.Trace(this.ctx, "dao_mysql_order_detail") 119 | if span != nil { 120 | defer span.Finish() 121 | span.Tag("t1", cast.ToString(time.Now().UnixNano())) 122 | } 123 | ret = make([]OrderDetail, 0) 124 | this.InitSession() 125 | this.InitTableName() 126 | this.SetTable(this.strTableName) 127 | this.BuildQuery(this.uUserId, "user_id") 128 | 129 | if len(arrOrderIds) > 0 { 130 | this.BuildQuery(torm.CastToParamIn(arrOrderIds), "order_id") 131 | } 132 | 133 | this.BuildQuery(this.uAppId, "app_id") 134 | if span != nil { 135 | span.Tag("t2", cast.ToString(time.Now().UnixNano())) 136 | } 137 | 138 | err = this.Session.Find(&ret) 139 | 140 | if span != nil { 141 | span.Tag("t3", cast.ToString(time.Now().UnixNano())) 142 | } 143 | 144 | return 145 | } 146 | -------------------------------------------------------------------------------- /app/dao/mysql/order_detail_idx.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-01 16:44:42 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-23 01:44:05 6 | * @Description: 7 | */ 8 | package mysql 9 | 10 | import ( 11 | "context" 12 | "github.com/spf13/cast" 13 | "github.com/tal-tech/torm" 14 | "github.com/tal-tech/xtools/traceutil" 15 | "powerorder/app/utils" 16 | "time" 17 | ) 18 | 19 | type OrderDetailIdx struct { 20 | Id int64 `json:"-" xorm:"-"` 21 | Bid int64 `json:"bid" xorm:"not null default '0' comment('对应原库表的id') BIGINT(20)"` 22 | UserId uint64 `json:"user_id" xorm:"not null default 0 comment('user_id') BIGINT(20)"` 23 | OrderId string `json:"order_id" xorm:"not null default '' comment('订单号') index(order_app) CHAR(32)"` 24 | ProductId uint `json:"product_id" xorm:"not null default 0 comment('商品ID') INT(11)"` 25 | ProductName string `json:"product_name" xorm:"not null default '' comment('商品名称') VARCHAR(100)"` 26 | PromotionType string `json:"promotion_type" xorm:"not null default '' comment('促销类型') VARCHAR(100)"` 27 | ParentProductId uint `json:"parent_product_id" xorm:"not null default 0 comment('父商品ID') INT(11)"` 28 | CreatedTime utils.Time `json:"created_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') TIMESTAMP"` 29 | UpdatedTime utils.Time `json:"updated_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('修改时间') TIMESTAMP"` 30 | } 31 | 32 | type OrderDetailIdxDao struct { 33 | Dao 34 | } 35 | 36 | /** 37 | * @description: 38 | * @params {type} 39 | * @return: 40 | */ 41 | func NewOrderDetailIdxDao2(uAppId uint, session *torm.Session, ctx context.Context) *OrderDetailIdxDao { 42 | object := new(OrderDetailIdxDao) 43 | object.InitDatabaseName(uAppId, 0) 44 | object.UpdateEngine(session) 45 | 46 | object.InitTableName() 47 | object.SetTable(object.GetTable()) 48 | object.Dao.InitCtx(ctx) 49 | 50 | return object 51 | } 52 | 53 | /** 54 | * @description: 55 | * @params {type} 56 | * @return: 57 | */ 58 | func NewOrderDetailIdxDao(ctx context.Context, uAppId uint, writer string) *OrderDetailIdxDao { 59 | object := new(OrderDetailIdxDao) 60 | object.InitDatabaseName(uAppId, 0) 61 | writer = utils.DbShadowHandler(ctx, writer) 62 | if ins := torm.GetDbInstance(object.strDatabaseName, writer); ins != nil { 63 | object.UpdateEngine(ins.Engine) 64 | } else { 65 | return nil 66 | } 67 | object.Engine.ShowSQL(true) 68 | object.InitTableName() 69 | object.SetTable(object.GetTable()) 70 | object.Dao.InitCtx(ctx) 71 | 72 | return object 73 | } 74 | 75 | /** 76 | * @description: 77 | * @params {type} 78 | * @return: 79 | */ 80 | func (this *OrderDetailIdxDao) InitDatabaseName(uAppId uint, id uint64) { 81 | this.uAppId = uAppId 82 | this.strDatabaseName = utils.GenDatabaseIdxName() 83 | } 84 | 85 | /** 86 | * @description: 87 | * @params {type} 88 | * @return: 89 | */ 90 | func (this *OrderDetailIdxDao) InitTableName() { 91 | this.strTableName = utils.GenOrderDetailIdxTableName(this.uAppId) 92 | } 93 | 94 | /** 95 | * @description:根据orderId查询mysql 96 | * @params {arrOrderIds} 订单ID集合 97 | * @return: 订单detail集合 98 | */ 99 | 100 | func (this *OrderDetailIdxDao) Get(arrOrderIds []string) (ret []OrderDetailIdx, err error) { 101 | span, _ := traceutil.Trace(this.ctx, "dao_mysql_order_detail") 102 | if span != nil { 103 | defer span.Finish() 104 | span.Tag("t1", cast.ToString(time.Now().UnixNano())) 105 | } 106 | ret = make([]OrderDetailIdx, 0) 107 | this.InitSession() 108 | this.InitTableName() 109 | this.SetTable(this.strTableName) 110 | this.BuildQuery(this.uUserId, "user_id") 111 | 112 | if len(arrOrderIds) > 0 { 113 | this.BuildQuery(torm.CastToParamIn(arrOrderIds), "order_id") 114 | } 115 | 116 | this.BuildQuery(this.uAppId, "app_id") 117 | if span != nil { 118 | span.Tag("t2", cast.ToString(time.Now().UnixNano())) 119 | } 120 | 121 | err = this.Session.Find(&ret) 122 | 123 | if span != nil { 124 | span.Tag("t3", cast.ToString(time.Now().UnixNano())) 125 | } 126 | 127 | return 128 | } 129 | -------------------------------------------------------------------------------- /app/dao/mysql/order_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-01 16:45:33 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-11 01:25:50 6 | * @Description: 7 | */ 8 | package mysql 9 | 10 | import ( 11 | "context" 12 | "encoding/json" 13 | "github.com/spf13/cast" 14 | logger "github.com/tal-tech/loggerX" 15 | "github.com/tal-tech/torm" 16 | "github.com/tal-tech/xtools/traceutil" 17 | "powerorder/app/utils" 18 | "time" 19 | ) 20 | 21 | type OrderInfo struct { 22 | Id int64 `json:"id" xorm:"not null pk autoincr INT(11)"` 23 | BId int64 `json:"bid" xorm:"not null default 0 bigint(20)"` 24 | UserId uint64 `json:"user_id" xorm:"not null default 0 comment('user_id,如果业务线user_id为字符串,则可以使用36进制或59进制法转成10进制') index(user_app) BIGINT(20)"` 25 | AppId uint `json:"app_id" xorm:"not null default 0 comment('接入的商户ID appid') unique(order_app) index(user_app) INT(11)"` 26 | OrderId string `json:"order_id" xorm:"not null default '' comment('订单号') unique(order_app) CHAR(32)"` 27 | OrderType uint `json:"order_type" xorm:"not null default 0 comment('订单类型') TINYINT(4)"` 28 | Status uint `json:"status" xorm:"not null default 1 comment('状态(如:1:未付款,2:支付中,3:支付成功,4:用户手动取消,5:已过期脚本取消)') TINYINT(2)"` 29 | Source uint `json:"source" xorm:"not null default 1 comment('订单来源(如: 1: 商城, 2: 购物车, 3:续报列表)') TINYINT(2)"` 30 | OrderDevice uint `json:"order_device" xorm:"not null default 1 comment('订单设备(如1:pc,2:iPad,3:Touch,4:APP,7:IOS,8:Android)') TINYINT(2)"` 31 | CreatedTime utils.Time `json:"created_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') TIMESTAMP"` 32 | CancelledTime utils.Time `json:"cancelled_time" xorm:"not null default '1970-00-00 00:00:00' comment('取消时间') TIMESTAMP"` 33 | UpdatedTime utils.Time `json:"updated_time" xorm:"not null default '1970-00-00 00:00:00' comment('修改时间') TIMESTAMP"` 34 | Extras string `json:"extras" xorm:"not null comment('订单产生中附属信息存储 比如交易快照之类的,不会来查询,不会用来检索,格式任意') TEXT"` 35 | Price uint `json:"price" xorm:"not null default 0 comment('订单金额,单位:分') INT(11)"` 36 | PromotionPrice uint `json:"promotion_price" xorm:"not null default 0 comment('促销总金额,单位:分') INT(11)"` 37 | RealPrice uint `json:"real_price" xorm:"not null default 0 comment('实际缴费金额,单位:分') INT(11)"` 38 | PayDevice uint `json:"pay_device" xorm:"not null default 1 comment('支付订单时的设备(如1:pc,2:iPad,3:Touch,4:APP,7:IOS,8:Android)') TINYINT(2)"` 39 | PayCreatedTime utils.Time `json:"pay_created_time" xorm:"not null default '1970-00-00 00:00:00' comment('支付开始时间') datetime"` 40 | PaidTime utils.Time `json:"paid_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('支付时间') TIMESTAMP"` 41 | ExpiredTime utils.Time `json:"expired_time" xorm:"default null comment('过期时间') datetime"` 42 | TxId string `json:"-" xorm:"not null default '' comment('事务id,格式为traceId的后32位') VARCHAR(32)"` 43 | TxStatus uint `json:"-" xorm:"not null default 0 comment('事务状态:0,刚创建 1、已提交 2~50、被回滚(可自定义回滚原因)') TINYINT(2)"` 44 | Version uint `json:"version" xorm:"not null default 0 comment('版本号') TINYINT(2)"` 45 | } 46 | 47 | type OrderInfoDao struct { 48 | Dao 49 | } 50 | 51 | /** 52 | * @description: 类型转换,OrderDetail => map 53 | * @params {detail} OrderDetail 54 | * @return: 55 | */ 56 | func OrderInfo2Map(info OrderInfo) (res map[string]interface{}, err error) { 57 | j, err := json.Marshal(info) 58 | if err != nil { 59 | return nil, err 60 | } 61 | res = make(map[string]interface{}) 62 | json.Unmarshal(j, &res) 63 | return 64 | } 65 | 66 | /** 67 | * @description: 68 | * @params {type} 69 | * @return: 70 | */ 71 | func NewOrderInfoDao2(uAppId uint, uUserId uint64, session *torm.Session, ctx context.Context) *OrderInfoDao { 72 | object := new(OrderInfoDao) 73 | object.InitDatabaseName(uAppId, uUserId) 74 | object.UpdateEngine(session) 75 | object.InitTableName() 76 | object.SetTable(object.GetTable()) 77 | object.Dao.InitCtx(ctx) 78 | 79 | return object 80 | } 81 | 82 | /** 83 | * @description: 84 | * @params {type} 85 | * @return: 86 | */ 87 | func NewOrderInfoDao(ctx context.Context, uAppId uint, uUserId uint64, writer string) *OrderInfoDao { 88 | object := new(OrderInfoDao) 89 | object.InitDatabaseName(uAppId, uUserId) 90 | writer = utils.DbShadowHandler(ctx, writer) 91 | if ins := torm.GetDbInstance(object.strDatabaseName, writer); ins != nil { 92 | object.UpdateEngine(ins.Engine) 93 | } else { 94 | return nil 95 | } 96 | object.UpdateEngine(uUserId, writer) 97 | object.Engine.ShowSQL(true) 98 | object.InitTableName() 99 | object.SetTable(object.GetTable()) 100 | object.Dao.InitCtx(ctx) 101 | 102 | return object 103 | } 104 | 105 | /** 106 | * @description: 107 | * @params {type} 108 | * @return: 109 | */ 110 | func (this *OrderInfoDao) InitTableName() { 111 | this.strTableName = utils.GenOrderInfoTableName(this.uUserId) 112 | 113 | } 114 | 115 | /** 116 | * @description: mysql查询订单信息 [已提交状态] 117 | * @params {arrOrderIds} 订单ID集合 118 | * @params {strTxId} 事务ID 119 | * @params {txStatus} 事务状态 120 | * @return: 订单信息集合 121 | */ 122 | 123 | func (this *OrderInfoDao) Get(arrOrderIds []string, strTxId string, txStatus []uint, startDate, endDate string) (ret []OrderInfo, err error) { 124 | 125 | if this == nil { 126 | err = logger.NewError("system error dao instance is nil") 127 | return 128 | } 129 | span, _ := traceutil.Trace(this.ctx, "dao_mysql_order_info") 130 | if span != nil { 131 | defer span.Finish() 132 | span.Tag("t1", cast.ToString(time.Now().UnixNano())) 133 | } 134 | ret = make([]OrderInfo, 0) 135 | this.InitSession() 136 | this.InitTableName() 137 | this.SetTable(this.strTableName) 138 | this.BuildQuery(this.uUserId, "user_id") 139 | this.BuildQuery(this.uAppId, "app_id") 140 | 141 | if len(arrOrderIds) > 0 { 142 | this.BuildQuery(torm.CastToParamIn(arrOrderIds), "order_id") 143 | } 144 | 145 | if len(strTxId) > 0 { 146 | this.BuildQuery(strTxId, "tx_id") 147 | } 148 | 149 | if len(txStatus) > 0 { 150 | this.BuildQuery(torm.CastToParamIn(txStatus), "tx_status") 151 | } 152 | if len(startDate) > 0 && len(endDate) > 0 { 153 | this.BuildQuery(torm.ParamRange{startDate, endDate}, "created_time") 154 | } 155 | 156 | if span != nil { 157 | span.Tag("t2", cast.ToString(time.Now().UnixNano())) 158 | } 159 | err = this.Session.Find(&ret) 160 | 161 | if err != nil { 162 | logger.Ex(this.ctx, "DaoMysqlOrderInfoDao", "get from db error = %v", err) 163 | } 164 | if span != nil { 165 | span.Tag("t3", cast.ToString(time.Now().UnixNano())) 166 | } 167 | return 168 | } 169 | -------------------------------------------------------------------------------- /app/dao/mysql/order_info_idx.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-01 16:45:33 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-11 01:25:50 6 | * @Description: 7 | */ 8 | package mysql 9 | 10 | import ( 11 | "context" 12 | "encoding/json" 13 | logger "github.com/tal-tech/loggerX" 14 | "github.com/tal-tech/torm" 15 | "powerorder/app/utils" 16 | ) 17 | 18 | type OrderInfoIdx struct { 19 | Id int64 `json:"-" xorm:"-"` 20 | Bid int64 `json:"bid" xorm:"not null default '0' comment('对应原库表的id') BIGINT(20)"` 21 | UserId uint64 `json:"user_id" xorm:"not null default 0 comment('user_id,如果业务线user_id为字符串,则可以使用36进制或59进制法转成10进制') index(user_app) BIGINT(20)"` 22 | OrderId string `json:"order_id" xorm:"not null default '' comment('订单号') unique(order_app) CHAR(32)"` 23 | CreatedTime utils.Time `json:"created_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') TIMESTAMP"` 24 | CancelledTime utils.Time `json:"cancelled_time" xorm:"not null default '1970-00-00 00:00:00' comment('取消时间') TIMESTAMP"` 25 | UpdatedTime utils.Time `json:"updated_time" xorm:"not null default '1970-00-00 00:00:00' comment('修改时间') TIMESTAMP"` 26 | PayCreatedTime utils.Time `json:"pay_created_time" xorm:"not null default '1970-00-00 00:00:00' comment('支付开始时间') datetime"` 27 | PaidTime utils.Time `json:"paid_time" xorm:"not null default 'CURRENT_TIMESTAMP' comment('支付时间') TIMESTAMP"` 28 | ExpiredTime utils.Time `json:"expired_time" xorm:"default null comment('过期时间') datetime"` 29 | } 30 | 31 | type OrderInfoIdxDao struct { 32 | Dao 33 | } 34 | 35 | /** 36 | * @description: 类型转换,OrderDetail => map 37 | * @params {detail} OrderDetail 38 | * @return: 39 | */ 40 | func OrderInfoIdx2Map(info OrderInfo) (res map[string]interface{}, err error) { 41 | j, err := json.Marshal(info) 42 | if err != nil { 43 | return nil, err 44 | } 45 | res = make(map[string]interface{}) 46 | json.Unmarshal(j, &res) 47 | return 48 | } 49 | 50 | /** 51 | * @description: 52 | * @params {type} 53 | * @return: 54 | */ 55 | func NewOrderInfoIdxDao2(uAppId uint, session *torm.Session, ctx context.Context) *OrderInfoIdxDao { 56 | object := new(OrderInfoIdxDao) 57 | object.InitDatabaseName(uAppId, 0) 58 | logger.Ix(ctx, "session", "session = %v", session) 59 | 60 | object.UpdateEngine(session) 61 | logger.Ix(ctx, "engine", "engine = %v", object.Engine) 62 | 63 | object.InitTableName() 64 | object.SetTable(object.GetTable()) 65 | object.Dao.InitCtx(ctx) 66 | 67 | //object.Engine.ShowSQL(true) 68 | 69 | return object 70 | } 71 | 72 | /** 73 | * @description: 74 | * @params {type} 75 | * @return: 76 | */ 77 | func NewOrderInfoIdxDao(ctx context.Context, uAppId uint, writer string) *OrderInfoIdxDao { 78 | object := new(OrderInfoIdxDao) 79 | object.InitDatabaseName(uAppId, 0) 80 | writer = utils.DbShadowHandler(ctx, writer) 81 | if ins := torm.GetDbInstance(object.strDatabaseName, writer); ins != nil { 82 | object.UpdateEngine(ins.Engine) 83 | } else { 84 | return nil 85 | } 86 | object.Engine.ShowSQL(true) 87 | object.InitTableName() 88 | object.SetTable(object.GetTable()) 89 | object.Dao.InitCtx(ctx) 90 | 91 | return object 92 | } 93 | 94 | /** 95 | * @description: 96 | * @params {type} 97 | * @return: 98 | */ 99 | func (this *OrderInfoIdxDao) InitDatabaseName(uAppId uint, id uint64) { 100 | this.uAppId = uAppId 101 | this.strDatabaseName = utils.GenDatabaseIdxName() 102 | } 103 | 104 | /** 105 | * @description: 106 | * @params {type} 107 | * @return: 108 | */ 109 | func (this *OrderInfoIdxDao) InitTableName() { 110 | this.strTableName = utils.GenOrderInfoIdxTableName(this.uAppId) 111 | 112 | } 113 | 114 | /** 115 | * @description: mysql查询订单信息 [已提交状态] 116 | * @params {arrOrderIds} 订单ID集合 117 | * @params {strTxId} 事务ID 118 | * @params {txStatus} 事务状态 119 | * @return: 订单信息集合 120 | */ 121 | 122 | func (this *OrderInfoIdxDao) Get(arrOrderIds []string) (ret []OrderInfoIdx, err error) { 123 | 124 | if this == nil { 125 | err = logger.NewError("system error dao instance is nil") 126 | return 127 | } 128 | ret = make([]OrderInfoIdx, 0) 129 | this.InitSession() 130 | this.InitTableName() 131 | this.SetTable(this.strTableName) 132 | 133 | if len(arrOrderIds) > 0 { 134 | this.BuildQuery(torm.CastToParamIn(arrOrderIds), "order_id") 135 | } 136 | 137 | err = this.Session.Find(&ret) 138 | 139 | if err != nil { 140 | logger.Ex(this.ctx, "DaoMysqlOrderInfoDao", "get from db error = %v", err) 141 | } 142 | return 143 | } 144 | -------------------------------------------------------------------------------- /app/dao/mysql/order_info_with_order_id.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-02-11 23:56:28 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-23 01:50:36 6 | * @Description: 7 | */ 8 | 9 | package mysql 10 | 11 | import ( 12 | "context" 13 | logger "github.com/tal-tech/loggerX" 14 | "github.com/tal-tech/torm" 15 | "powerorder/app/constant" 16 | "powerorder/app/utils" 17 | ) 18 | 19 | type OrderInfoWithOrderIdDao struct { 20 | Dao 21 | strOrderId string 22 | } 23 | 24 | /** 25 | * @description: 26 | * @params {type} 27 | * @return: 28 | */ 29 | func NewOrderInfoWithOrderIdDao(ctx context.Context, uAppId uint, strOrderId string, writer string) *OrderInfoWithOrderIdDao { 30 | object := new(OrderInfoWithOrderIdDao) 31 | object.InitDatabaseName(uAppId, strOrderId) 32 | writer = utils.DbShadowHandler(ctx, writer) 33 | if ins := torm.GetDbInstance(object.strDatabaseName, writer); ins != nil { 34 | object.UpdateEngine(ins.Engine) 35 | } else { 36 | return nil 37 | } 38 | object.UpdateEngine(writer) 39 | object.Engine.ShowSQL(true) 40 | object.InitTableName() 41 | object.SetTable(object.GetTable()) 42 | object.Dao.InitCtx(ctx) 43 | 44 | return object 45 | } 46 | 47 | /** 48 | * @description:批量获得OrderInfo,同库同表的一起查询 49 | * @params {uAppId 业务线ID} 50 | * @params {arrOrderIds 订单ID数组} 51 | * @return: 52 | */ 53 | func BGet(ctx context.Context, uAppId uint, arrOrderIds []string) (info []OrderInfo, err error) { 54 | 55 | info = make([]OrderInfo, 0) 56 | err = nil 57 | OrderIdsMapped := make(map[string]map[string][]string) 58 | for i := 0; i < len(arrOrderIds); i++ { 59 | strDbName := utils.GenDatabaseNameByOrderId(arrOrderIds[i]) 60 | strTableName := utils.GenOrderInfoTableNameByOrderId(arrOrderIds[i]) 61 | 62 | if _, ok := OrderIdsMapped[strDbName]; !ok { 63 | OrderIdsMapped[strDbName] = make(map[string][]string) 64 | } 65 | 66 | if _, ok := OrderIdsMapped[strDbName][strTableName]; !ok { 67 | OrderIdsMapped[strDbName][strTableName] = make([]string, 0) 68 | } 69 | OrderIdsMapped[strDbName][strTableName] = append(OrderIdsMapped[strDbName][strTableName], arrOrderIds[i]) 70 | } 71 | 72 | for _, OrderIdsInSameDb := range OrderIdsMapped { 73 | for _, OrderIdsInSameTable := range OrderIdsInSameDb { 74 | object := NewOrderInfoWithOrderIdDao(ctx, uAppId, OrderIdsInSameTable[0], constant.DBReader) 75 | ret, err := object.Get(OrderIdsInSameTable) 76 | if err != nil { 77 | return []OrderInfo{}, err 78 | } 79 | for _, Info := range ret { 80 | info = append(info, Info) 81 | } 82 | } 83 | } 84 | 85 | return 86 | } 87 | 88 | /** 89 | * @description: 90 | * @params {type} 91 | * @return: 92 | */ 93 | func (this *OrderInfoWithOrderIdDao) InitDatabaseName(uAppId uint, strOrderId string) { 94 | this.uAppId = uAppId 95 | this.strOrderId = strOrderId 96 | this.strDatabaseName = utils.GenDatabaseNameByOrderId(strOrderId) 97 | } 98 | 99 | /** 100 | * @description: 101 | * @params {type} 102 | * @return: 103 | */ 104 | func (this *OrderInfoWithOrderIdDao) InitTableName() { 105 | this.strTableName = utils.GenOrderInfoTableNameByOrderId(this.strOrderId) 106 | } 107 | 108 | /** 109 | * @description: 110 | * @params {type} 111 | * @return: 112 | */ 113 | 114 | func (this *OrderInfoWithOrderIdDao) Get(arrOrderIds []string) (ret []OrderInfo, err error) { 115 | ret = make([]OrderInfo, 0) 116 | this.InitSession() 117 | this.InitTableName() 118 | this.SetTable(this.strTableName) 119 | 120 | if len(arrOrderIds) > 0 { 121 | this.BuildQuery(torm.CastToParamIn(arrOrderIds), "order_id") 122 | } else { 123 | logger.Ex(this.ctx, "dao.mysql.orderinfowithorderid.get error", "OrderInfoWithOrderIdDao empty order_ids", "") 124 | return 125 | } 126 | this.BuildQuery(this.uAppId, "app_id") 127 | 128 | this.BuildQuery(constant.TxStatusCommitted, "tx_status") 129 | 130 | err = this.Session.Find(&ret) 131 | 132 | return 133 | } 134 | -------------------------------------------------------------------------------- /app/dao/redis/dao.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-11 22:32:41 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-01-22 11:32:24 6 | * @Description: 7 | */ 8 | package redis 9 | 10 | type Dao struct { 11 | uAppId uint 12 | instance string 13 | } 14 | 15 | /** 16 | * @description: 17 | * @params {type} 18 | * @return: 19 | */ 20 | func NewDao(uAppId uint, instance string) *Dao { 21 | 22 | object := new(Dao) 23 | object.uAppId = uAppId 24 | object.instance = instance 25 | 26 | return object 27 | } 28 | -------------------------------------------------------------------------------- /app/dao/redis/order_id.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-12 15:16:47 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-06 19:45:51 6 | * @Description: 7 | */ 8 | package redis 9 | 10 | import ( 11 | "context" 12 | "fmt" 13 | logger "github.com/tal-tech/loggerX" 14 | "github.com/tal-tech/xredis" 15 | "powerorder/app/constant" 16 | "powerorder/app/utils" 17 | "time" 18 | ) 19 | 20 | type OrderId struct { 21 | Dao 22 | uUserId uint64 23 | ctx context.Context 24 | } 25 | 26 | func NewOrderIdDao(ctx context.Context, uAppId uint, uUserId uint64, instance string) *OrderId { 27 | object := new(OrderId) 28 | // object.uAppId = uAppId 29 | object.uUserId = uUserId 30 | object.ctx = ctx 31 | object.Dao.uAppId = uAppId 32 | object.Dao.instance = instance 33 | return object 34 | } 35 | 36 | /** 37 | * @description: 获取order_id列表的redis无序集合的key 38 | * @params {} 39 | * @return: xes_platform_order_id_001_1 40 | */ 41 | func (this *OrderId) GenSetKey() string { 42 | pts := utils.GetPts(this.ctx) 43 | if pts { 44 | return fmt.Sprintf("pts_%s%03d_%v", constant.OrderId_SetKeyPrx, this.uAppId, this.uUserId) 45 | } 46 | return fmt.Sprintf("%s%03d_%v", constant.OrderId_SetKeyPrx, this.uAppId, this.uUserId) 47 | } 48 | 49 | /** 50 | * @description: 获取order_id列表的redis无序集合的key 51 | * @params {} 52 | * @return: xes_platform_order_id_001_1_20200922 53 | */ 54 | func (this *OrderId) GenSetKeyV2() string { 55 | pts := utils.GetPts(this.ctx) 56 | today := time.Now().Format("20060102") // 拼接当天的日期 57 | if pts { 58 | return fmt.Sprintf("pts_%s%03d_%v_%s", constant.OrderId_SetKeyPrx, this.uAppId, this.uUserId, today) 59 | } 60 | 61 | return fmt.Sprintf("%s%03d_%v_%s", constant.OrderId_SetKeyPrx, this.uAppId, this.uUserId, today) 62 | } 63 | 64 | /** 65 | * @description: 66 | * @params {OrderIds} 67 | * @return: 68 | */ 69 | func (this *OrderId) SDel(OrderIds []string) (back int64, err error) { 70 | setKey := this.GenSetKey() 71 | 72 | if len(OrderIds) == 0 { 73 | //删除所有的数据 74 | _, err1 := xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).Del(setKey, nil) 75 | if err1 != nil { 76 | err = err1 77 | logger.Ex(this.ctx, "dao.redis.orderid.sdel error", "sdel failed!", err) 78 | } 79 | return 80 | } 81 | params := make([]interface{}, 0) 82 | for i := 0; i < len(OrderIds); i++ { 83 | params = append(params, OrderIds[i]) 84 | } 85 | back, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).SRem(setKey, []interface{}{}, params) 86 | return 87 | } 88 | 89 | /** 90 | * @description: 获取order_id列表,Redis 无序集合key列表 91 | * @params {} 92 | * @return: 订单ID集合 93 | */ 94 | func (this *OrderId) SGet() (back []string, err error) { 95 | setKey := this.GenSetKey() 96 | back, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).SMembers(setKey, []interface{}{}) 97 | return 98 | } 99 | 100 | /** 101 | * @description: 新增order_id列表 102 | * @params {OrderIds} 103 | * @return: 104 | */ 105 | func (this *OrderId) SAdd(OrderIds []string) (back int64, err error) { 106 | 107 | var bDefaultMemberExisted = false 108 | if len(OrderIds) == 0 { 109 | //logger.Ix(this.ctx,"dao.redis.orderid.SAdd","sadd empty OrderIds","") 110 | return 111 | } 112 | 113 | members := make([]interface{}, 0) 114 | for i := 0; i < len(OrderIds); i++ { 115 | members = append(members, OrderIds[i]) 116 | if OrderIds[i] == constant.OrderId_MemberDefault { 117 | bDefaultMemberExisted = true 118 | } 119 | } 120 | setKey := this.GenSetKey() 121 | back, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).SAdd(setKey, []interface{}{}, members) 122 | if bDefaultMemberExisted { 123 | //如果是设置一个空的数据,则设置过期时间 124 | _, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).Expire(setKey, []interface{}{}, constant.OrderId_ExpiredTimeDefault) 125 | } else { 126 | _, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).Expire(setKey, []interface{}{}, constant.OrderId_ExpiredTime30Days) 127 | } 128 | return 129 | 130 | } 131 | 132 | /** 133 | * @description: 获取order_id列表,Redis 无序集合key列表 134 | * @params {} 135 | * @return: 订单ID集合 136 | */ 137 | func (this *OrderId) SGetV2() (back []string, err error) { 138 | setKey := this.GenSetKeyV2() 139 | back, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).SMembers(setKey, []interface{}{}) 140 | return 141 | } 142 | 143 | /** 144 | * @description: 新增order_id列表 145 | * @params {OrderIds} 146 | * @return: 147 | */ 148 | func (this *OrderId) SAddV2(OrderIds []string) (back int64, err error) { 149 | 150 | if len(OrderIds) == 0 { 151 | logger.Ix(this.ctx, "dao.redis.orderid.SAddV2", "sadd empty OrderIds", "") 152 | return 153 | } 154 | var members []interface{} 155 | for i := 0; i < len(OrderIds); i++ { 156 | members = append(members, OrderIds[i]) 157 | } 158 | setKey := this.GenSetKeyV2() 159 | 160 | back, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).SAdd(setKey, []interface{}{}, members) 161 | _, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).Expire(setKey, []interface{}{}, constant.OrderId_ExpiredTimeDefault) 162 | return 163 | } 164 | 165 | /** 166 | * @description: 167 | * @params {OrderIds} 168 | * @return: 169 | */ 170 | func (this *OrderId) SDelV2(OrderIds []string) (back int64, err error) { 171 | setKey := this.GenSetKeyV2() 172 | if len(OrderIds) == 0 { 173 | //删除所有的数据 174 | return xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).Del(setKey, nil) 175 | } 176 | var params []interface{} 177 | for i := 0; i < len(OrderIds); i++ { 178 | params = append(params, OrderIds[i]) 179 | } 180 | return xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).SRem(setKey, []interface{}{}, params) 181 | } 182 | -------------------------------------------------------------------------------- /app/dao/redis/tx_order.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-12 15:16:47 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-10 00:51:34 6 | * @Description: 7 | */ 8 | package redis 9 | 10 | import ( 11 | "context" 12 | "encoding/json" 13 | "fmt" 14 | "github.com/tal-tech/xredis" 15 | "powerorder/app/constant" 16 | "powerorder/app/output" 17 | "powerorder/app/utils" 18 | ) 19 | 20 | type TxOrder struct { 21 | Dao 22 | txId string 23 | ctx context.Context 24 | } 25 | 26 | type TxInfo struct { 27 | OrderIds []string 28 | Extensions map[string]bool 29 | OrderInfos map[string]output.Order 30 | } 31 | 32 | /** 33 | * @description: 34 | * @params {type} 35 | * @return: 36 | */ 37 | 38 | func NewTxOrderDao(ctx context.Context, uAppId uint, txId string, instance string) *TxOrder { 39 | object := new(TxOrder) 40 | object.txId = txId 41 | object.ctx = ctx 42 | object.Dao.uAppId = uAppId 43 | object.Dao.instance = instance 44 | return object 45 | } 46 | 47 | /** 48 | * @description: 获取order_id列表的redis无序集合的key 49 | * @params {} 50 | * @return: xes_platform_order_id_001_1 51 | */ 52 | func (this *TxOrder) GenSetKey() string { 53 | pts := utils.GetPts(this.ctx) 54 | if pts { 55 | return fmt.Sprintf("pts_%s%d_%v", "tx_order_", this.uAppId, this.txId) 56 | } 57 | 58 | return fmt.Sprintf("%s%d_%v", "tx_order_", this.uAppId, this.txId) 59 | } 60 | 61 | func (this *TxOrder) Set(TxInfo TxInfo) error { 62 | key := this.GenSetKey() 63 | b, err := json.Marshal(TxInfo) 64 | if err != nil { 65 | return err 66 | } 67 | _, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).Set(key, []interface{}{}, string(b), constant.OrderId_ExpiredTimeDefault) 68 | return err 69 | 70 | } 71 | 72 | func (this *TxOrder) Get() (TxInfo, error) { 73 | 74 | key := this.GenSetKey() 75 | back, err := xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).Get(key, []interface{}{}) 76 | var txInfo TxInfo 77 | 78 | err = json.Unmarshal([]byte(back), &txInfo) 79 | 80 | return txInfo, err 81 | 82 | } 83 | -------------------------------------------------------------------------------- /app/dao/redis/user_id.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-23 20:14:46 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-22 19:51:40 6 | * @Description: 7 | */ 8 | 9 | package redis 10 | 11 | import ( 12 | "context" 13 | "fmt" 14 | "github.com/tal-tech/xredis" 15 | "powerorder/app/constant" 16 | "powerorder/app/utils" 17 | "strconv" 18 | ) 19 | 20 | type UserId struct { 21 | Dao 22 | ctx context.Context 23 | } 24 | 25 | /** 26 | * @description: 27 | * @params {type} 28 | * @return: 29 | */ 30 | func NewUserIdDao(ctx context.Context, uAppId uint, instance string) *UserId { 31 | 32 | object := new(UserId) 33 | object.uAppId = uAppId 34 | object.ctx = ctx 35 | 36 | return object 37 | } 38 | 39 | /** 40 | * @description: 41 | * @params {type} 42 | * @return: 43 | */ 44 | func (this *UserId) GenKVKey(OrderId string) string { 45 | if utils.GetPts(this.ctx) { 46 | return fmt.Sprintf("pts_%s%03d_%v", constant.UserId_KVKeyPrx, this.uAppId, OrderId) 47 | } 48 | return fmt.Sprintf("%s%03d_%v", constant.UserId_KVKeyPrx, this.uAppId, OrderId) 49 | } 50 | 51 | /** 52 | * @description: 53 | * @params {type} 54 | * @return: 55 | */ 56 | func (this *UserId) MGet(OrderIds []string) (ret map[string]uint64, err error) { 57 | OtherKeys := make([]interface{}, 0) 58 | for i := 1; i < len(OrderIds); i++ { 59 | OtherKeys = append(OtherKeys, this.GenKVKey(OrderIds[i])) 60 | OtherKeys = append(OtherKeys, nil) 61 | } 62 | Key1 := this.GenKVKey(OrderIds[0]) 63 | 64 | var back []string 65 | back, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).MGet(Key1, []interface{}{}, OtherKeys...) 66 | 67 | var UserId uint64 68 | var OrderId string 69 | 70 | ret = make(map[string]uint64, 0) 71 | for i := 0; i < len(back); i++ { 72 | OrderId = OrderIds[i] 73 | UserId, err = strconv.ParseUint(back[i], 10, 64) 74 | 75 | if err != nil { 76 | return 77 | } 78 | 79 | ret[OrderId] = UserId 80 | 81 | } 82 | 83 | return 84 | } 85 | 86 | /** 87 | * @description: 88 | * @params {type} 89 | * @return: 90 | */ 91 | func (this *UserId) Set(OrderId string, UserId uint64) (back string, err error) { 92 | 93 | Key := this.GenKVKey(OrderId) 94 | 95 | back, err = xredis.NewSimpleXesRedis(this.ctx, constant.Order_Redis_Cluster).Set(Key, []interface{}{}, UserId, int64(constant.UserId_ExpiredTimeDefault)) 96 | 97 | return 98 | 99 | } 100 | -------------------------------------------------------------------------------- /app/i/controller/controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-10 14:59:46 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-01-11 20:52:15 6 | * @Description: 7 | */ 8 | package controller 9 | 10 | type Controller interface { 11 | InitParam() error 12 | VldParam() error 13 | Run() error 14 | Output() 15 | } 16 | 17 | //func Index(instance Controller) { 18 | // 19 | // err := instance.InitParam() 20 | // 21 | // if err != nil { 22 | // return 23 | // } 24 | // 25 | // err = instance.VldParam() 26 | // 27 | // 28 | // if err != nil { 29 | // return 30 | // } 31 | // 32 | // instance.Run() 33 | // instance.Output() 34 | // 35 | //} 36 | -------------------------------------------------------------------------------- /app/i/dao/mysql/dao.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-08 16:44:48 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-01-10 15:01:05 6 | * @Description: 7 | */ 8 | package mysql 9 | 10 | type Dao interface { 11 | Create(bean interface{}) (int64, error) 12 | InitDatabaseName(uAppId uint, uUserId uint64) 13 | InitTableName() 14 | SetTable(strTableName string) 15 | GetTable() string 16 | } 17 | 18 | /** 19 | * @description: 20 | * @params {type} 21 | * @return: 22 | */ 23 | func Create(instance Dao, bean interface{}) (int64, error) { 24 | instance.InitTableName() 25 | instance.SetTable(instance.GetTable()) 26 | 27 | return instance.Create(bean) 28 | } 29 | -------------------------------------------------------------------------------- /app/middlewares/cors.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | func CorsMiddleware() gin.HandlerFunc { 8 | return func(c *gin.Context) { 9 | currentOrigin := c.Request.Header.Get("Origin") 10 | c.Writer.Header().Set("Access-Control-Allow-Origin", currentOrigin) 11 | c.Writer.Header().Set("Access-Control-Max-Age", "86400") 12 | c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") 13 | c.Writer.Header().Set("Access-Control-Allow-Headers", "content-type, x-csrf-token, X-XSRF-TOKEN, authorization, X-Requested-With, timestamp, nonce, device-id, sign, app-id") 14 | c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length") 15 | c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") 16 | 17 | // 设置压测标识 18 | traceid := c.GetHeader("trace_id") 19 | c.Set("shadow",false) 20 | if len(traceid) > 7 && traceid[0:7] == "shadow_" { 21 | c.Set("pts",true) 22 | } 23 | 24 | if c.Request.Method == "OPTIONS" { 25 | c.AbortWithStatus(204) 26 | } else { 27 | c.Next() 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/middlewares/sign.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "github.com/gin-gonic/gin" 7 | logger "github.com/tal-tech/loggerX" 8 | "net/http" 9 | "powerorder/app/constant" 10 | "powerorder/app/utils" 11 | ) 12 | 13 | // 接口验签 14 | func VerifySignMiddleware() gin.HandlerFunc { 15 | return func(c *gin.Context) { 16 | // 签名验证 17 | ok := verifySign("1","app_001") 18 | if !ok { 19 | outError(c, constant.ORDER_SIGNATURE_VERIFIED_ERROR) 20 | return 21 | } 22 | c.Next() 23 | } 24 | } 25 | 26 | func outError(c *gin.Context, code int) { 27 | msg := "order system error" 28 | if value, ok:=constant.ERROR_MSG_MAP[code]; ok{ 29 | msg = value 30 | } 31 | err := logger.NewError(msg) 32 | err.Code = code 33 | resp := utils.Error(err) 34 | c.AbortWithStatusJSON(http.StatusOK, resp) 35 | } 36 | 37 | func verifySign(appId, sign string) bool { 38 | bodyMd5 := _md5(appId) 39 | return bodyMd5 == sign 40 | } 41 | 42 | func _md5(str string) string { 43 | h := md5.New() 44 | h.Write([]byte(str)) 45 | return hex.EncodeToString(h.Sum(nil)) 46 | } 47 | -------------------------------------------------------------------------------- /app/model/addition/insertion.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-07 18:04:10 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-10 16:33:26 6 | * @Description: 7 | */ 8 | package addition 9 | 10 | import ( 11 | "context" 12 | "fmt" 13 | logger "github.com/tal-tech/loggerX" 14 | "powerorder/app/constant" 15 | "powerorder/app/dao/mysql" 16 | "powerorder/app/dao/redis" 17 | imysql "powerorder/app/i/dao/mysql" 18 | "powerorder/app/model/finishing" 19 | "powerorder/app/output" 20 | "powerorder/app/params" 21 | "powerorder/app/utils" 22 | "time" 23 | ) 24 | 25 | type Insertion struct { 26 | action uint8 27 | ctx context.Context 28 | } 29 | 30 | func NewInsertion(action uint8, ctx context.Context) *Insertion { 31 | object := new(Insertion) 32 | object.action = action 33 | object.ctx = ctx 34 | return object 35 | } 36 | 37 | /** 38 | * @description: 39 | * @params {params} 40 | * @return: 41 | */ 42 | func (this *Insertion) Insert(param params.ReqBegin, TxId string) (OrderIds []string, err error) { 43 | var txInfo redis.TxInfo 44 | txInfo.OrderIds = make([]string, 0) 45 | txInfo.Extensions = make(map[string]bool) 46 | txInfo.OrderInfos = make(map[string]output.Order, 0) 47 | 48 | CreatedTime := utils.Time(time.Now()) 49 | //data := make(map[string]map[string]interface{}) 50 | 51 | infos := make([]mysql.OrderInfo, 0) 52 | details := make([]mysql.OrderDetail, 0) 53 | extensions := make(map[string][]map[string]interface{}) 54 | 55 | for j := 0; j < len(param.Additions); j++ { 56 | var orderData output.Order 57 | 58 | addition := param.Additions[j] 59 | 60 | var OrderId string 61 | if len(addition.Info.OrderId) == 0 { 62 | OrderId = utils.GenOrderId(param.AppId, time.Time(CreatedTime), param.UserId) 63 | } else { 64 | OrderId = addition.Info.OrderId 65 | } 66 | 67 | OrderIds = append(OrderIds, OrderId) 68 | //if data[OrderId] == nil { 69 | // data[OrderId] = make(map[string]interface{}) 70 | //} 71 | 72 | orderData.Detail = this.assembleOrderDetail(addition.Detail, OrderId, param.UserId, param.AppId) 73 | details = append(details, orderData.Detail...) 74 | 75 | orderData.Extensions = this.assembleExtension(addition.Extensions, &txInfo, OrderId, param.UserId) 76 | for k, val := range orderData.Extensions { 77 | if _, ok := extensions[k]; ok == false { 78 | extensions[k] = []map[string]interface{}{} 79 | } 80 | extensions[k] = append(extensions[k], val...) 81 | } 82 | 83 | info := this.assembleOrderInfo(addition.Info) 84 | info.UserId = param.UserId 85 | info.AppId = param.AppId 86 | info.TxId = TxId 87 | 88 | txInfo.OrderIds = append(txInfo.OrderIds, OrderId) 89 | infos = append(infos, info) 90 | orderData.Info = info 91 | 92 | txInfo.OrderInfos[OrderId] = orderData 93 | } 94 | 95 | session := mysql.NewSession(this.ctx, param.AppId, param.UserId, constant.DBWriter) 96 | if session == nil { 97 | err = logger.NewError("init session error") 98 | return 99 | } 100 | defer session.Close() 101 | 102 | if err1 := session.Begin(); err1 != nil { 103 | err = err1 104 | return 105 | } 106 | 107 | var dao imysql.Dao 108 | 109 | for key, value := range extensions { 110 | dao = mysql.NewExtensionDao2(param.AppId, param.UserId, key, session, this.ctx) 111 | if _, err1 := imysql.Create(dao, value); err1 != nil { 112 | if err1 := session.Rollback(); err1 != nil { 113 | err = err1 114 | } 115 | return 116 | } 117 | } 118 | dao = mysql.NewOrderDetailDao2(param.AppId, param.UserId, session, this.ctx) 119 | if _, err1 := imysql.Create(dao, details); err1 != nil { 120 | if err1 = session.Rollback(); err1 != nil { 121 | err = err1 122 | } 123 | return 124 | } 125 | 126 | dao = mysql.NewOrderInfoDao2(param.AppId, param.UserId, session, this.ctx) 127 | if _, err1 := imysql.Create(dao, infos); err1 != nil { 128 | if err1 = session.Rollback(); err1 != nil { 129 | err = err1 130 | } 131 | return 132 | } 133 | if err1 := session.Commit(); err1 != nil { 134 | err = err1 135 | return 136 | } 137 | 138 | // 将数据临时写入到redis,减少对mysql的访问 139 | if this.action == constant.Addition { 140 | TxInfoDao := redis.NewTxOrderDao(this.ctx, param.AppId, TxId, constant.Order_Redis_Cluster) 141 | err = TxInfoDao.Set(txInfo) 142 | return 143 | } 144 | // 异步处理相关数据 145 | go this.finishing(param, TxId, txInfo) 146 | return 147 | } 148 | 149 | /** 150 | * @description: 151 | * @params {type} 152 | * @return: 153 | */ 154 | func (this *Insertion) finishing(param params.ReqBegin, txId string, txInfo redis.TxInfo) { 155 | // 数据同步到索引库中 156 | finishingModel := finishing.NewInsertion(param.AppId, param.UserId, this.ctx, txId) 157 | OrderIds, _ := finishingModel.InsertOrder(txInfo) 158 | 159 | // 清理redis数据 160 | orderIdDao := redis.NewOrderIdDao(this.ctx, param.AppId, param.UserId, constant.Order_Redis_Cluster) 161 | if _, err := orderIdDao.SDel([]string{}); err != nil { 162 | 163 | } 164 | logger.Ix(this.ctx, "model.addition.insertion.finishing", "insert finishing", "txId %s , params %+v , txInfo %+v", txId, param, OrderIds) 165 | 166 | } 167 | 168 | func (this *Insertion) assembleOrderInfo(reqInfo mysql.OrderInfo) (info mysql.OrderInfo) { 169 | var CreatedTime = utils.Time(time.Now()) 170 | info = reqInfo 171 | info.BId = utils.GenId() 172 | if info.Source < 1 { 173 | info.Source = 1 // 默认值 174 | } 175 | if constant.ZeroTime == reqInfo.PaidTime.Unix() { 176 | info.PaidTime = mysql.TiDefaultTimestamp 177 | } 178 | if constant.ZeroTime == reqInfo.PayCreatedTime.Unix() { 179 | info.PayCreatedTime = mysql.TiDefaultTimestamp 180 | } 181 | if constant.ZeroTime == reqInfo.CancelledTime.Unix() { 182 | info.CancelledTime = mysql.TiDefaultTimestamp 183 | } 184 | if constant.ZeroTime == reqInfo.ExpiredTime.Unix() { 185 | info.ExpiredTime = mysql.TiDefaultTimestamp 186 | } 187 | if this.action == constant.Addition { 188 | info.CreatedTime = CreatedTime 189 | info.UpdatedTime = CreatedTime 190 | info.TxStatus = constant.TxStatusUnCommitted 191 | } else { 192 | if constant.ZeroTime == reqInfo.CreatedTime.Unix() { 193 | info.CreatedTime = mysql.TiDefaultTimestamp 194 | } 195 | if constant.ZeroTime == reqInfo.UpdatedTime.Unix() { 196 | info.UpdatedTime = mysql.TiDefaultTimestamp 197 | } 198 | info.TxStatus = constant.TxStatusCommitted 199 | } 200 | info.Version = 1 201 | 202 | return 203 | } 204 | 205 | func (this *Insertion) assembleOrderDetail(reqDetails []mysql.OrderDetail, orderId string, userId uint64, appId uint) []mysql.OrderDetail { 206 | CreatedTime := utils.Time(time.Now()) 207 | tmpDetails := make([]mysql.OrderDetail, len(reqDetails)) 208 | for i := 0; i < len(reqDetails); i++ { 209 | reqDetails[i].AppId = appId 210 | reqDetails[i].UserId = userId 211 | reqDetails[i].OrderId = orderId 212 | 213 | if reqDetails[i].ProductType < 1 { 214 | reqDetails[i].ProductType = 1 // 默认值 215 | } 216 | 217 | if this.action == constant.Addition { 218 | reqDetails[i].CreatedTime = CreatedTime //.Format("2006-01-02 15:04:05") 219 | reqDetails[i].UpdatedTime = CreatedTime 220 | } else { 221 | if constant.ZeroTime == reqDetails[i].CreatedTime.Unix() { 222 | reqDetails[i].CreatedTime = mysql.TiDefaultTimestamp 223 | } 224 | if constant.ZeroTime == reqDetails[i].UpdatedTime.Unix() { 225 | reqDetails[i].UpdatedTime = mysql.TiDefaultTimestamp 226 | } 227 | } 228 | reqDetails[i].Version = 1 229 | } 230 | return tmpDetails 231 | } 232 | 233 | // todo 注释 234 | // 235 | // 236 | // 237 | func (this *Insertion) assembleExtension(extensions map[string][]map[string]interface{}, txInfo *redis.TxInfo, orderId string, userId uint64) map[string][]map[string]interface{} { 238 | tmpExtensions := make(map[string][]map[string]interface{}, 0) 239 | for key, value := range extensions { 240 | (*txInfo).Extensions[key] = true 241 | for i := 0; i < len(value); i++ { 242 | value[i]["order_id"] = orderId 243 | value[i]["user_id"] = userId 244 | value[i]["version"] = 1 245 | value[i]["id"] = fmt.Sprintf("%d", utils.GenId()) 246 | if _, ok := extensions[key]; ok == false { 247 | extensions[key] = []map[string]interface{}{} 248 | tmpExtensions[key] = []map[string]interface{}{} 249 | } 250 | extensions[key] = append(extensions[key], value[i]) 251 | tmpExtensions[key] = append(tmpExtensions[key], value[i]) 252 | } 253 | } 254 | return tmpExtensions 255 | } 256 | -------------------------------------------------------------------------------- /app/model/addition/update.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-07 18:04:10 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-11 00:41:47 6 | * @Description: 7 | * @Todo : 更新时清除缓存、更新缓存应该改成事务 8 | */ 9 | package addition 10 | 11 | import ( 12 | "context" 13 | logger "github.com/tal-tech/loggerX" 14 | "powerorder/app/constant" 15 | "powerorder/app/dao/mysql" 16 | "powerorder/app/dao/redis" 17 | "powerorder/app/model/finishing" 18 | "powerorder/app/params" 19 | ) 20 | 21 | type Update struct { 22 | uAppId uint 23 | ctx context.Context 24 | } 25 | 26 | /** 27 | * @description: 28 | * @params {type} 29 | * @return: 30 | */ 31 | func NewUpdate(ctx context.Context, uAppId uint) *Update { 32 | object := new(Update) 33 | object.ctx = ctx 34 | object.uAppId = uAppId 35 | return object 36 | } 37 | 38 | /** 39 | * @description: 40 | * @params {type} 41 | * @return: 42 | */ 43 | func (this *Update) UpdateStatus(param params.ReqRollback) (err error) { 44 | instance := mysql.NewOrderInfoDao(this.ctx, param.AppId, param.UserId, constant.DBWriter) 45 | if instance == nil { 46 | err = logger.NewError("init dao error!") 47 | return 48 | } 49 | var info mysql.OrderInfo 50 | info.TxStatus = param.TxStatus 51 | instance.InitTableName() 52 | instance.SetTable(instance.GetTable()) 53 | 54 | ret, err := instance.UpdateColsWhere(info, []string{"tx_status"}, " user_id = ? and app_id = ? and tx_id = ? and tx_status = ? ", []interface{}{param.UserId, param.AppId, param.TxId, constant.TxStatusUnCommitted}) 55 | if err != nil || ret == 0 { 56 | logger.Ix(this.ctx, "model.addition.update.update failed", "model addition/update error = %+v ,ret = %d", err, ret) 57 | return 58 | } 59 | 60 | if param.TxStatus != constant.TxStatusCommitted { 61 | return 62 | } 63 | 64 | this.setCache(param) 65 | return 66 | } 67 | 68 | func (this *Update) setCache(param params.ReqRollback) { 69 | finishingModel := finishing.NewInsertion(param.AppId, param.UserId, this.ctx, param.TxId) 70 | TxOrderDao := redis.NewTxOrderDao(this.ctx, param.AppId, param.TxId, constant.Order_Redis_Cluster) 71 | TxInfo, _ := TxOrderDao.Get() 72 | ok := finishingModel.SetCache(make([]string, 0), TxInfo) 73 | if !ok { 74 | logger.Ex(this.ctx, "model.addition.update.update failed", "update.update.SetCache", "ok:%+v", ok) 75 | } 76 | go this.insertOrderIdx(param, TxInfo) 77 | } 78 | 79 | /** 80 | * @description: 81 | * @params {type} 82 | * @return: 83 | */ 84 | func (this *Update) insertOrderIdx(param params.ReqRollback, txInfo redis.TxInfo) { 85 | 86 | finishingModel := finishing.NewInsertion(param.AppId, param.UserId, this.ctx, param.TxId) 87 | OrderIds, _ := finishingModel.InsertOrder(txInfo) 88 | 89 | orderIdDao := redis.NewOrderIdDao(this.ctx, param.AppId, param.UserId, constant.Order_Redis_Cluster) 90 | if _, err := orderIdDao.SDel([]string{}); err != nil { 91 | logger.Ex(this.ctx, "model.addition.update.insertOrderIdx", "sdel order_ids ", "%+v", OrderIds) 92 | } 93 | // todo 去掉 | 加注释 94 | if _, err := orderIdDao.SDelV2([]string{}); err != nil { 95 | logger.Ex(this.ctx, "model.addition.update.insertOrderIdx", "sdelv2 order_ids ", "%+v", OrderIds) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/model/order/extension.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-17 16:58:44 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-23 01:51:54 6 | * @Description: 7 | */ 8 | package order 9 | 10 | import ( 11 | "context" 12 | "powerorder/app/constant" 13 | "powerorder/app/dao/mysql" 14 | ) 15 | 16 | type Extension struct { 17 | uAppId uint 18 | uUserId uint64 19 | strExtName string 20 | ctx context.Context 21 | } 22 | 23 | /** 24 | * @description: 25 | * @params {type} 26 | * @return: 27 | */ 28 | func NewExtensionModel(ctx context.Context, uAppId uint, uUserId uint64, strExtName string) *Extension { 29 | object := new(Extension) 30 | object.uAppId = uAppId 31 | object.uUserId = uUserId 32 | object.strExtName = strExtName 33 | object.ctx = ctx 34 | return object 35 | } 36 | 37 | /** 38 | * @description: 组装返回数据,根据redis缓存的订单信息【extensions】 39 | * @params {OrderIds} 想要获得Extension的OrderId 40 | * @params {OrderCached} 可能已从cache中读取到的cache 41 | * @return: 返回Extensions集合 42 | * @return: 返回需要种缓存的Extensions集合 43 | */ 44 | func (this *Extension) Get(OrderIds []string, OrderCached map[string]map[string]interface{}) (ret map[string][]map[string]interface{}, PipeData map[string]map[string]interface{}, err error) { 45 | ret = make(map[string][]map[string]interface{}) 46 | PipeData = make(map[string]map[string]interface{}) 47 | var OrderIdsWithoutExtension []string 48 | 49 | for i := 0; i < len(OrderIds); i++ { 50 | PipeData[OrderIds[i]] = make(map[string]interface{}) 51 | var ExtValue map[string]interface{} 52 | var Value interface{} 53 | var ok bool 54 | //如果某个OrderId在Cache中存在 55 | if ExtValue, ok = OrderCached[OrderIds[i]]; ok { 56 | if Value, ok = ExtValue[this.strExtName]; ok { 57 | var ExtData []map[string]interface{} 58 | if ExtData, ok = Value.([]map[string]interface{}); ok { 59 | ret[OrderIds[i]] = ExtData 60 | } 61 | continue 62 | } 63 | 64 | } 65 | 66 | OrderIdsWithoutExtension = append(OrderIdsWithoutExtension, OrderIds[i]) 67 | PipeData[OrderIds[i]][this.strExtName] = constant.Order_HashFieldDefault 68 | } 69 | 70 | if len(OrderIdsWithoutExtension) == 0 { 71 | return 72 | } 73 | ExtensionDao := mysql.NewExtensionDao(this.ctx, this.uAppId, this.uUserId, this.strExtName, constant.DBReader) 74 | ExtDataFromMysql, err := ExtensionDao.Get(OrderIdsWithoutExtension) 75 | 76 | if err != nil { 77 | return 78 | } 79 | 80 | for i := 0; i < len(ExtDataFromMysql); i++ { 81 | OrderId := ExtDataFromMysql[i]["order_id"].(string) 82 | ret[OrderId] = append(ret[OrderId], ExtDataFromMysql[i]) 83 | //种缓存 84 | if Pipe, ok := PipeData[OrderId][this.strExtName]; ok { 85 | 86 | // todo 考虑改成if 87 | switch Pipe.(type) { 88 | case string: 89 | PipeData[OrderId][this.strExtName] = make([]map[string]interface{}, 0) 90 | } 91 | 92 | } else { 93 | PipeData[OrderId][this.strExtName] = make([]map[string]interface{}, 0) 94 | 95 | } 96 | PipeData[OrderId][this.strExtName] = append(PipeData[OrderId][this.strExtName].([]map[string]interface{}), ExtDataFromMysql[i]) 97 | 98 | } 99 | 100 | return 101 | 102 | } 103 | -------------------------------------------------------------------------------- /app/model/order/order_detail.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-17 16:58:44 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-23 01:52:49 6 | * @Description: 7 | */ 8 | package order 9 | 10 | import ( 11 | "context" 12 | "github.com/spf13/cast" 13 | logger "github.com/tal-tech/loggerX" 14 | "powerorder/app/dao/mysql" 15 | "time" 16 | ) 17 | 18 | type OrderDetail struct { 19 | uAppId uint 20 | uUserId uint64 21 | ctx context.Context 22 | } 23 | 24 | /** 25 | * @description: 26 | * @params {type} 27 | * @return: 28 | */ 29 | func NewOrderDetail(ctx context.Context, uAppId uint, uUserId uint64) *OrderDetail { 30 | object := new(OrderDetail) 31 | object.uAppId = uAppId 32 | object.uUserId = uUserId 33 | object.ctx = ctx 34 | return object 35 | } 36 | 37 | /** 38 | * @description: 组装返回数据,根据redis缓存的订单信息【detail】 39 | * @params {OrderIds} 想要获得OrderInfo的OrderId 40 | * @params {OrderCached} 可能已从cache中读取到的cache 41 | * @return: 返回OrderDetail集合 42 | * @return: 返回需要种缓存OrderDetail集合 43 | */ 44 | func (this *OrderDetail) Get(OrderIds []string, OrderCached map[string]map[string]interface{}) (ret map[string][]mysql.OrderDetail, PipeData map[string]map[string]interface{}, err error) { 45 | 46 | ret = make(map[string][]mysql.OrderDetail) 47 | PipeData = make(map[string]map[string]interface{}) 48 | var OrderIdsWithoutOrderDetail []string 49 | //遍历要获得的OrderId 50 | 51 | logger.Dx(this.ctx, "model.toc.orderdetail.Get", "model order_detail order_ids = %v ", OrderIds) 52 | 53 | for i := 0; i < len(OrderIds); i++ { 54 | PipeData[OrderIds[i]] = make(map[string]interface{}) 55 | 56 | //如果某个OrderId在Cache中存在 57 | if Order, ok := OrderCached[OrderIds[i]]; ok { 58 | var Value interface{} 59 | 60 | if Value, ok = Order[sconstant.Detail]; ok { 61 | var Detail []mysql.OrderDetail 62 | //且在Cache中的类型为[]mysql.OrderDetail 63 | 64 | if Detail, ok = Value.([]mysql.OrderDetail); ok { 65 | ret[OrderIds[i]] = Detail 66 | } 67 | continue 68 | } 69 | 70 | } 71 | 72 | OrderIdsWithoutOrderDetail = append(OrderIdsWithoutOrderDetail, OrderIds[i]) 73 | PipeData[OrderIds[i]][sconstant.Detail] = sconstant.Order_HashFieldDefault 74 | } 75 | if span != nil { 76 | span.Tag("t2", cast.ToString(time.Now().UnixNano())) 77 | } 78 | if len(OrderIdsWithoutOrderDetail) == 0 { 79 | return 80 | } 81 | // 超过200个订单 则分批次获取 82 | var OrderDetailFromMysql []mysql.OrderDetail 83 | if len(OrderIdsWithoutOrderDetail) > util.SQL_WHERE_IN_MAX { 84 | OrderDetailFromMysql = this.getBatchByOrderIds(OrderIdsWithoutOrderDetail) 85 | } else { 86 | OrderDetailDao := mysql.NewOrderDetailDao(this.ctx, this.uAppId, this.uUserId, sconstant.DBReader) 87 | 88 | OrderDetailFromMysql, err = OrderDetailDao.Get(OrderIdsWithoutOrderDetail) 89 | } 90 | 91 | if err != nil { 92 | return 93 | } 94 | if span != nil { 95 | span.Tag("t3", cast.ToString(time.Now().UnixNano())) 96 | } 97 | for i := 0; i < len(OrderDetailFromMysql); i++ { 98 | 99 | ret[OrderDetailFromMysql[i].OrderId] = append(ret[OrderDetailFromMysql[i].OrderId], OrderDetailFromMysql[i]) 100 | //种缓存 101 | if Pipe, ok := PipeData[OrderDetailFromMysql[i].OrderId][sconstant.Detail]; ok { 102 | switch Pipe.(type) { 103 | case string: 104 | PipeData[OrderDetailFromMysql[i].OrderId][sconstant.Detail] = make([]mysql.OrderDetail, 0) 105 | } 106 | } else { 107 | PipeData[OrderDetailFromMysql[i].OrderId][sconstant.Detail] = make([]mysql.OrderDetail, 0) 108 | } 109 | 110 | PipeData[OrderDetailFromMysql[i].OrderId][sconstant.Detail] = append(PipeData[OrderDetailFromMysql[i].OrderId][sconstant.Order_HashSubKey_Detail].([]mysql.OrderDetail), OrderDetailFromMysql[i]) 111 | 112 | } 113 | if span != nil { 114 | span.Tag("t4", cast.ToString(time.Now().UnixNano())) 115 | } 116 | return 117 | 118 | } 119 | 120 | func (this *OrderDetail) getBatchByOrderIds(OrderIds []string) (datas []mysql.OrderDetail) { 121 | defer util2.Catch() 122 | datas = make([]mysql.OrderDetail, 0) 123 | tmpOrderIdArr := make([]string, 0) 124 | for i := 0; i < len(OrderIds); i++ { 125 | tmpOrderIdArr = append(tmpOrderIdArr, OrderIds[i]) 126 | if len(tmpOrderIdArr) == util.SQL_WHERE_IN_MAX { 127 | OrderDetailDao := mysql.NewOrderDetailDao(this.ctx, this.uAppId, this.uUserId, sconstant.DBReader) 128 | OrderDetailFromMysql, err := OrderDetailDao.Get(tmpOrderIdArr) 129 | if err != nil { 130 | logger.Ex(this.ctx, "model.toc.orderdetail.getBatchByOrderIds", "getBatchByOrderIds error:%v", err) 131 | continue 132 | } 133 | if len(OrderDetailFromMysql) > 0 { 134 | datas = append(datas, OrderDetailFromMysql...) 135 | } 136 | tmpOrderIdArr = make([]string, 0) 137 | } 138 | } 139 | if len(tmpOrderIdArr) > 0 { 140 | OrderDetailDao := mysql.NewOrderDetailDao(this.ctx, this.uAppId, this.uUserId, sconstant.DBReader) 141 | OrderDetailFromMysql, err := OrderDetailDao.Get(tmpOrderIdArr) 142 | if err != nil { 143 | logger.Ex(this.ctx, "model.toc.orderdetail.getBatchByOrderIds", "getBatchByOrderIds error:%v", err) 144 | } 145 | if len(OrderDetailFromMysql) > 0 { 146 | datas = append(datas, OrderDetailFromMysql...) 147 | } 148 | } 149 | return 150 | } 151 | -------------------------------------------------------------------------------- /app/model/order/order_id.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-12 22:32:33 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-23 01:52:02 6 | * @Description: 7 | */ 8 | 9 | package order 10 | 11 | import ( 12 | "context" 13 | "powerorder/app/constant" 14 | "powerorder/app/dao/mysql" 15 | "powerorder/app/dao/redis" 16 | ) 17 | 18 | type OrderId struct { 19 | uAppId uint 20 | uUserId uint64 21 | ctx context.Context 22 | } 23 | 24 | /** 25 | * @description:初始化 26 | * @params {uAppId} 27 | * @params {uUserId} 28 | * @params {ctx} 29 | * @return: 30 | */ 31 | func NewOrderIdModel(uAppId uint, uUserId uint64, ctx context.Context) *OrderId { 32 | object := new(OrderId) 33 | object.uAppId = uAppId 34 | object.uUserId = uUserId 35 | object.ctx = ctx 36 | return object 37 | } 38 | 39 | /** 40 | * @description: 根据userId和appId,查询所有订单 41 | * @params {} 42 | * @return: {OrderIds} 订单ID集合 43 | * @return {OrderInfo} 订单OrderInfo集合 44 | */ 45 | func (this *OrderId) Get() (OrderIds []string, OrderInfo []mysql.OrderInfo, err error) { 46 | OrderIdRedisDao := redis.NewOrderIdDao(this.ctx, this.uAppId, this.uUserId, constant.Order_Redis_Cluster) 47 | value, err := OrderIdRedisDao.SGet() 48 | if err != nil || len(value) == 0 { 49 | // 缓存无数据,可能发生了错误,需要读取mysql 50 | OrderInfo, err = this.GetOrderInfo("", "") 51 | if err != nil { 52 | return 53 | } 54 | for i := 0; i < len(OrderInfo); i++ { 55 | OrderIds = append(OrderIds, OrderInfo[i].OrderId) 56 | } 57 | if len(OrderIds) == 0 { 58 | OrderIds = []string{constant.OrderId_MemberDefault} 59 | } 60 | _, err := OrderIdRedisDao.SAdd(OrderIds) 61 | if err != nil { 62 | return 63 | } 64 | return 65 | } 66 | for i := 0; i < len(value); i++ { 67 | if value[i] != constant.OrderId_MemberDefault { 68 | OrderIds = append(OrderIds, value[i]) 69 | } 70 | } 71 | return 72 | 73 | } 74 | 75 | /** 76 | * @description: 根据userId和appId,查询所有订单 77 | * @params {} 78 | * @return: {OrderIds} 订单ID集合 79 | * @return {OrderInfo} 订单OrderInfo集合 80 | */ 81 | func (this *OrderId) GetV2(startDate, endDate string) (OrderIds []string, OrderInfo []mysql.OrderInfo, err error) { 82 | var value []string 83 | OrderIdRedisDao := redis.NewOrderIdDao(this.ctx, this.uAppId, this.uUserId, constant.Order_Redis_Cluster) 84 | 85 | value, err = OrderIdRedisDao.SGetV2() 86 | 87 | if err != nil || len(value) == 0 { 88 | // 缓存无数据,可能发生了错误,需要读取mysql 89 | OrderInfo, err = this.GetOrderInfo(startDate, endDate) 90 | if err != nil { 91 | return 92 | } 93 | for i := 0; i < len(OrderInfo); i++ { 94 | OrderIds = append(OrderIds, OrderInfo[i].OrderId) 95 | } 96 | if len(OrderIds) == 0 { 97 | OrderIds = []string{constant.OrderId_MemberDefault} 98 | } 99 | _, err := OrderIdRedisDao.SAddV2(OrderIds) 100 | if err != nil { 101 | return 102 | } 103 | return 104 | } 105 | 106 | for i := 0; i < len(value); i++ { 107 | if value[i] != constant.OrderId_MemberDefault { 108 | OrderIds = append(OrderIds, value[i]) 109 | } 110 | } 111 | return 112 | 113 | } 114 | 115 | /** 116 | * @description:根据userId和appID,读取mysql, 117 | * @params {} 118 | * @return: OrderInfo集合 119 | */ 120 | func (this *OrderId) GetOrderInfo(startDate, endDate string) (OrderInfo []mysql.OrderInfo, err error) { 121 | OrderInfoDao := mysql.NewOrderInfoDao(this.ctx, this.uAppId, this.uUserId, constant.DBReader) 122 | OrderInfo, err = OrderInfoDao.Get([]string{}, "", []uint{constant.TxStatusCommitted}, startDate, endDate) 123 | return 124 | } 125 | -------------------------------------------------------------------------------- /app/model/order/order_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-17 16:58:44 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-23 01:54:01 6 | * @Description: 7 | */ 8 | package order 9 | 10 | import ( 11 | "context" 12 | logger "github.com/tal-tech/loggerX" 13 | "powerorder/app/constant" 14 | "powerorder/app/dao/mysql" 15 | "powerorder/app/utils" 16 | ) 17 | 18 | type OrderInfo struct { 19 | uAppId uint 20 | uUserId uint64 21 | ctx context.Context 22 | } 23 | 24 | /** 25 | * @description:初始化赋值 26 | * @params {ctx} 27 | * @params {uAppId} 28 | * @params {uUserId} 29 | * @return: 30 | */ 31 | func NewOrderInfo(ctx context.Context, uAppId uint, uUserId uint64) *OrderInfo { 32 | object := new(OrderInfo) 33 | object.uAppId = uAppId 34 | object.uUserId = uUserId 35 | object.ctx = ctx 36 | return object 37 | } 38 | 39 | /** 40 | * @description: 组装返回数据,根据redis缓存的订单信息【info】 41 | * @params {OrderInfo} 可能已拿到的OrderInfo,概率有点小 42 | * @params {OrderIds} 想要获得OrderInfo的OrderId 43 | * @params {OrderCached} 可能已从cache中读取到的cache 44 | * @return: 返回OrderInfo集合 45 | * @return: 返回需要缓存Info数据 46 | */ 47 | func (this *OrderInfo) Get(OrderInfo []mysql.OrderInfo, OrderIds []string, OrderCached map[string]map[string]interface{}) (ret map[string]mysql.OrderInfo, PipeData map[string]map[string]interface{}, err error) { 48 | 49 | if this == nil { 50 | err = errors.New("system recover error") 51 | return 52 | } 53 | 54 | ret = make(map[string]mysql.OrderInfo) 55 | PipeData = make(map[string]map[string]interface{}) 56 | var OrderIdsWithoutOrderInfo []string 57 | //遍历要获得的OrderId 58 | 59 | for i := 0; i < len(OrderIds); i++ { 60 | PipeData[OrderIds[i]] = make(map[string]interface{}) 61 | //如果某个OrderId在Cache中存在 62 | 63 | if Order, ok := OrderCached[OrderIds[i]]; ok { 64 | 65 | var Value interface{} 66 | if Value, ok = Order[constant.Order_HashSubKey_Info]; ok { 67 | var Info mysql.OrderInfo 68 | if Info, ok = Value.(mysql.OrderInfo); ok { 69 | ret[OrderIds[i]] = Info 70 | } 71 | continue 72 | } 73 | //且在Cache中的类型为mysql.OrderInfo 74 | 75 | } 76 | //缓存不存在,则需要种缓存 77 | exist := false 78 | for j := 0; j < len(OrderInfo); j++ { 79 | 80 | if OrderInfo[j].OrderId == OrderIds[i] { 81 | //设置在返回值中 82 | ret[OrderIds[i]] = OrderInfo[j] 83 | //种缓存 84 | 85 | PipeData[OrderIds[i]][constant.Info] = OrderInfo[j] 86 | exist = true 87 | break 88 | } 89 | } 90 | 91 | if exist == false { 92 | OrderIdsWithoutOrderInfo = append(OrderIdsWithoutOrderInfo, OrderIds[i]) 93 | PipeData[OrderIds[i]][constant.Order_HashSubKey_Info] = constant.Order_HashFieldDefault 94 | } 95 | } 96 | 97 | if len(OrderIdsWithoutOrderInfo) == 0 { 98 | return 99 | } 100 | // 缓存没命中,查询只读库mysql 101 | var OrderInfoFromMysql []mysql.OrderInfo 102 | if len(OrderIdsWithoutOrderInfo) > utils.SQL_WHERE_IN_MAX { 103 | OrderInfoFromMysql = this.getBatchByOrderIds(OrderIdsWithoutOrderInfo) 104 | } else { 105 | OrderInfoDao := mysql.NewOrderInfoDao(this.ctx, this.uAppId, this.uUserId, constant.DBReader) 106 | OrderInfoFromMysql, err = OrderInfoDao.Get(OrderIdsWithoutOrderInfo, "", []uint{constant.TxStatusCommitted}, "", "") 107 | } 108 | 109 | if err != nil { 110 | logger.Ex(this.ctx, "ModelOrderInfo", "get from db error =%v", err) 111 | return 112 | } 113 | 114 | for i := 0; i < len(OrderInfoFromMysql); i++ { 115 | ret[OrderInfoFromMysql[i].OrderId] = OrderInfoFromMysql[i] 116 | //种缓存 117 | PipeData[OrderInfoFromMysql[i].OrderId][constant.Order_HashSubKey_Info] = OrderInfoFromMysql[i] 118 | 119 | } 120 | 121 | return 122 | 123 | } 124 | 125 | func (this *OrderInfo) getBatchByOrderIds(OrderIds []string) (datas []mysql.OrderInfo) { 126 | defer utils.Catch() 127 | datas = make([]mysql.OrderInfo, 0) 128 | tmpOrderIdArr := make([]string, 0) 129 | for i := 0; i < len(OrderIds); i++ { 130 | tmpOrderIdArr = append(tmpOrderIdArr, OrderIds[i]) 131 | if len(tmpOrderIdArr) == utils.SQL_WHERE_IN_MAX { 132 | OrderInfoDao := mysql.NewOrderInfoDao(this.ctx, this.uAppId, this.uUserId, constant.DBReader) 133 | OrderInfoFromMysql, err := OrderInfoDao.Get(tmpOrderIdArr, "", []uint{constant.TxStatusCommitted}, "", "") 134 | if err != nil { 135 | logger.Ex(this.ctx, "model.toc.orderinfo.getBatchByOrderIds", "getBatchByOrderIds error:%v", err) 136 | continue 137 | } 138 | if len(OrderInfoFromMysql) > 0 { 139 | datas = append(datas, OrderInfoFromMysql...) 140 | } 141 | tmpOrderIdArr = make([]string, 0) 142 | } 143 | } 144 | if len(tmpOrderIdArr) > 0 { 145 | OrderInfoDao := mysql.NewOrderInfoDao(this.ctx, this.uAppId, this.uUserId, constant.DBReader) 146 | OrderInfoFromMysql, err := OrderInfoDao.Get(tmpOrderIdArr, "", []uint{constant.TxStatusCommitted}, "", "") 147 | if err != nil { 148 | logger.Ex(this.ctx, "model.toc.orderinfo.getBatchByOrderIds", "getBatchByOrderIds error:%v", err) 149 | } 150 | if len(OrderInfoFromMysql) > 0 { 151 | datas = append(datas, OrderInfoFromMysql...) 152 | } 153 | } 154 | return 155 | } 156 | -------------------------------------------------------------------------------- /app/model/tob/get.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-23 13:13:33 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-12 10:29:53 6 | * @Description: 7 | */ 8 | 9 | package tob 10 | 11 | import ( 12 | "context" 13 | logger "github.com/tal-tech/loggerX" 14 | "powerorder/app/constant" 15 | "powerorder/app/dao/mysql" 16 | "powerorder/app/dao/redis" 17 | "powerorder/app/model/order" 18 | "powerorder/app/output" 19 | "strconv" 20 | ) 21 | 22 | type Get struct { 23 | uAppId uint 24 | ctx context.Context 25 | } 26 | 27 | /** 28 | * @description: 29 | * @params {type} 30 | * @return: 31 | */ 32 | func NewGet(ctx context.Context, uAppId uint) *Get { 33 | object := new(Get) 34 | object.uAppId = uAppId 35 | object.ctx = ctx 36 | return object 37 | } 38 | 39 | /** 40 | * @description: 41 | * @params {type} 42 | * @return: 43 | */ 44 | func (this *Get) Get(OrderIds []string, Fields []string) (ret map[string]output.Order, err error) { 45 | err = nil 46 | 47 | var OrderInfo []mysql.OrderInfo 48 | var InfoToBeCached = make(map[string]map[string]interface{}, 0) 49 | var InfoCached map[string]map[string]interface{} 50 | 51 | if len(OrderIds) == 0 { 52 | err = logger.NewError("empty order_ids") 53 | logger.Ex(this.ctx, "model.tob.get error", "empty order_ids") 54 | return 55 | } 56 | OrderRedisDao := redis.NewOrderDao(this.ctx, this.uAppId, 0) 57 | InfoCached, err = OrderRedisDao.BatchGet(OrderIds) 58 | 59 | OrderIdsCached := make(map[uint64][]string, 0) 60 | OrderIdsWithoutUserId := make([]string, 0) 61 | 62 | //logger.Ix(this.ctx,"model.tob.get.get","orderIds: %v, infoCached:= %d", OrderIds, len(InfoCached)) 63 | for _, orderId := range OrderIds { 64 | if value, ok := InfoCached[orderId]; ok && len(value) > 0 { 65 | if UserIdStr, ok := value[constant.Order_HashSubKey_UserId].(string); ok { 66 | UserIdTmp, _ := strconv.ParseInt(UserIdStr, 10, 64) 67 | UserId := uint64(UserIdTmp) 68 | if _, ok := OrderIdsCached[UserId]; !ok { 69 | OrderIdsCached[UserId] = make([]string, 0) 70 | } 71 | OrderIdsCached[UserId] = append(OrderIdsCached[UserId], orderId) 72 | } 73 | } else { 74 | OrderIdsWithoutUserId = append(OrderIdsWithoutUserId, orderId) 75 | } 76 | } 77 | 78 | //logger.Dx(this.ctx,"model tob/get order_ids = %v, OrderIdsCached= %v, OrderIdsWithoutUserId= %v", OrderIds, OrderIdsCached, OrderIdsWithoutUserId) 79 | 80 | if len(OrderIdsWithoutUserId) != 0 { 81 | OrderInfo, err = mysql.BGet(this.ctx, this.uAppId, OrderIdsWithoutUserId) 82 | if err != nil { 83 | return 84 | } 85 | for _, info := range OrderInfo { 86 | UserId := info.UserId 87 | OrderId := info.OrderId 88 | if _, ok := OrderIdsCached[UserId]; !ok { 89 | OrderIdsCached[UserId] = make([]string, 0) 90 | } 91 | OrderIdsCached[UserId] = append(OrderIdsCached[UserId], OrderId) 92 | } 93 | } 94 | 95 | ret = make(map[string]output.Order, 0) 96 | 97 | var OrderInfoMapped map[string]mysql.OrderInfo 98 | var OrderDetailMapped map[string][]mysql.OrderDetail 99 | 100 | var PipeData map[string]map[string]interface{} 101 | for i := 0; i < len(Fields); i++ { 102 | if Fields[i] == constant.OrderInfo { 103 | 104 | for UserId, OrderIds := range OrderIdsCached { 105 | ModelOrderInfo := order.NewOrderInfo(this.ctx, this.uAppId, UserId) 106 | OrderInfoMapped, PipeData, _ = ModelOrderInfo.Get(OrderInfo, OrderIds, InfoCached) 107 | 108 | for OrderId, Pipe := range PipeData { 109 | 110 | for field, value := range Pipe { 111 | if _, ok := InfoToBeCached[OrderId]; !ok { 112 | InfoToBeCached[OrderId] = make(map[string]interface{}, 0) 113 | } 114 | InfoToBeCached[OrderId][field] = value 115 | } 116 | } 117 | 118 | for OrderId, OrderInfo := range OrderInfoMapped { 119 | ret[OrderId] = output.Order{OrderInfo, make([]mysql.OrderDetail, 0), make(map[string][]map[string]interface{})} 120 | } 121 | 122 | } 123 | break 124 | 125 | } 126 | } 127 | 128 | for i := 0; i < len(Fields); i++ { 129 | 130 | if Fields[i] == constant.OrderDetail { 131 | for UserId, OrderIds := range OrderIdsCached { 132 | 133 | ModelOrderDetail := order.NewOrderDetail(this.ctx, this.uAppId, UserId) 134 | OrderDetailMapped, PipeData, _ = ModelOrderDetail.Get(OrderIds, InfoCached) 135 | 136 | for OrderId, Pipe := range PipeData { 137 | for field, value := range Pipe { 138 | if _, ok := InfoToBeCached[OrderId]; !ok { 139 | InfoToBeCached[OrderId] = make(map[string]interface{}, 0) 140 | } 141 | InfoToBeCached[OrderId][field] = value 142 | } 143 | } 144 | 145 | for OrderId, OrderDetails := range OrderDetailMapped { 146 | if _, ok := ret[OrderId]; !ok { 147 | ret[OrderId] = output.Order{mysql.OrderInfo{}, OrderDetails, make(map[string][]map[string]interface{})} 148 | } else { 149 | ret[OrderId] = output.Order{ret[OrderId].Info, OrderDetails, make(map[string][]map[string]interface{})} 150 | } 151 | } 152 | } 153 | break 154 | 155 | } 156 | } 157 | 158 | for i := 0; i < len(Fields); i++ { 159 | if Fields[i] == constant.OrderDetail || Fields[i] == constant.OrderInfo { 160 | 161 | } else { 162 | for UserId, OrderIds := range OrderIdsCached { 163 | 164 | ExtensionModel := order.NewExtensionModel(this.ctx, this.uAppId, UserId, Fields[i]) 165 | ExtDataMapped, PipeData, _ := ExtensionModel.Get(OrderIds, InfoCached) 166 | 167 | for OrderId, Pipe := range PipeData { 168 | for field, value := range Pipe { 169 | if _, ok := InfoToBeCached[OrderId]; !ok { 170 | InfoToBeCached[OrderId] = make(map[string]interface{}, 0) 171 | } 172 | InfoToBeCached[OrderId][field] = value 173 | } 174 | //OrderRedisDao.HMSet(OrderId, Pipe) 175 | } 176 | for OrderId, ExtData := range ExtDataMapped { 177 | if _, ok := ret[OrderId]; !ok { 178 | ret[OrderId] = output.Order{mysql.OrderInfo{}, make([]mysql.OrderDetail, 0), make(map[string][]map[string]interface{})} 179 | } 180 | ret[OrderId].Extensions[Fields[i]] = ExtData 181 | } 182 | } 183 | } 184 | 185 | } 186 | 187 | for OrderId, Value := range InfoToBeCached { 188 | err := OrderRedisDao.HMSet(OrderId, Value) 189 | if err != nil { 190 | logger.Ex(this.ctx, "model.tob.get error", "hmset error", err) 191 | continue 192 | } 193 | } 194 | return 195 | } 196 | -------------------------------------------------------------------------------- /app/model/tob/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-21 02:51:14 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime : 2020-02-16 09:55:33 6 | * @Description: 7 | */ 8 | 9 | package tob 10 | 11 | import ( 12 | "context" 13 | "github.com/pkg/errors" 14 | "net/http" 15 | "powerorder/app/constant" 16 | "powerorder/app/dao/es" 17 | "powerorder/app/output" 18 | "powerorder/app/params" 19 | "powerorder/app/utils" 20 | ) 21 | 22 | type Search struct { 23 | uAppId uint 24 | ctx context.Context 25 | dissociaton bool 26 | } 27 | 28 | /** 29 | * @description: 30 | * @params {type} 31 | * @return: 32 | */ 33 | func NewSearch(ctx context.Context, uAppId uint, dissociaton bool) *Search { 34 | object := new(Search) 35 | object.uAppId = uAppId 36 | object.ctx = ctx 37 | object.dissociaton = dissociaton 38 | return object 39 | } 40 | 41 | /** 42 | * @description: 43 | * @params {params} 44 | * @return: 45 | */ 46 | func (this *Search) Get(param params.TobReqSearch) (o []output.Order, total uint, err error) { 47 | ret := utils.CheckRules(param.Sorter) 48 | if !ret { 49 | err = errors.New("checkrules failed") 50 | return 51 | } 52 | 53 | header := http.Header{} 54 | if this.dissociaton == false { 55 | s := es.NewOrderInfoSearch(this.uAppId, header) 56 | o, total, err = s.Get(param) 57 | } else if constant.Detail == param.Fields[0] { 58 | s := es.NewOrderDetailSearch(this.uAppId, header) 59 | o, total, err = s.Get(param) 60 | } else { 61 | s := es.NewExtensionSearch(this.uAppId, header) 62 | o, total, err = s.Get(param) 63 | } 64 | return 65 | } 66 | -------------------------------------------------------------------------------- /app/model/toc/query.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-21 02:51:14 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-05 04:49:23 6 | * @Description: 7 | */ 8 | 9 | package toc 10 | 11 | import ( 12 | "context" 13 | logger "github.com/tal-tech/loggerX" 14 | "powerorder/app/dao/mysql" 15 | "powerorder/app/output" 16 | "powerorder/app/params" 17 | "powerorder/app/utils" 18 | "sort" 19 | "sync" 20 | "time" 21 | ) 22 | 23 | type Query struct { 24 | uAppId uint 25 | uUserId uint64 26 | ctx context.Context 27 | lock sync.RWMutex 28 | } 29 | 30 | /** 31 | * @description: 32 | * @params {type} 33 | * @return: 34 | */ 35 | func NewSearchQuery(ctx context.Context, uAppId uint, uUserId uint64) *Query { 36 | object := new(Query) 37 | object.uAppId = uAppId 38 | object.uUserId = uUserId 39 | object.ctx = ctx 40 | return object 41 | } 42 | 43 | /** 44 | * @description: 根据条件查询订单详情 45 | * @params {params} 46 | * @return: 订单详情列表 47 | */ 48 | func (this *Query) Get(param params.ReqQuery) (o []output.Order, total uint, err error) { 49 | 50 | startDate := time.Unix(param.StartDate, 0).Format("2006-01-02") 51 | endDate := time.Unix(param.EndDate, 0).Format("2006-01-02") 52 | 53 | var filterInfo *utils.Filter = nil //info筛选工具 54 | var filterDetail *utils.Filter = nil //detail筛选工具 55 | var sorter *utils.Sorter = nil //排序工具 56 | 57 | var t map[string]output.Order 58 | t = make(map[string]output.Order) 59 | 60 | filterInfo = nil 61 | 62 | ret := utils.CheckRules(param.Sorter) 63 | if !ret { 64 | err = logger.NewError("checkrules failed") 65 | return 66 | } 67 | 68 | // 校验向前提 69 | if len(param.InfoFilter) != 0 { 70 | filterInfo = utils.NewFilter(param.InfoFilter) 71 | if filterInfo == nil { 72 | logger.Ex(this.ctx, "model.toc.query.Get error", "init filterInfo error", "") 73 | return 74 | } 75 | } 76 | if len(param.DetailFilter) != 0 { 77 | filterDetail = utils.NewFilter(param.DetailFilter) 78 | if filterDetail == nil { 79 | logger.Ex(this.ctx, "model.toc.query.Get error", "init filterDetail error", "") 80 | return 81 | } 82 | } 83 | 84 | getModel := NewGet(this.ctx, this.uAppId, this.uUserId) 85 | OrderInfo, OrderIds, err := getModel.GetV2([]string{}, param.Fields, startDate, endDate) 86 | if err != nil { 87 | logger.Ex(this.ctx, "model.toc.query.Get error", "toc search in db error = %v", err) 88 | return 89 | } 90 | 91 | o = make([]output.Order, 0) 92 | OrderInfoMap := make(map[string]interface{}) 93 | OrderDetailMap := make(map[string]interface{}) 94 | 95 | var real bool 96 | var oi uint = 0 97 | EligibleOrderInfo := make([]interface{}, 0) 98 | 99 | for j := 0; j < len(OrderIds); j++ { 100 | OrderId := OrderIds[j] 101 | if _, ok := OrderInfo[OrderId]; !ok { 102 | continue 103 | } 104 | Value := OrderInfo[OrderId] 105 | if filterInfo != nil || len(param.Sorter) > 0 { 106 | OrderInfoMap, err = mysql.OrderInfo2Map(Value.Info) 107 | 108 | if err != nil { 109 | return 110 | } 111 | } 112 | real = false 113 | if filterInfo != nil { 114 | real, err = filterInfo.Execute(OrderInfoMap) 115 | 116 | if err != nil { 117 | return 118 | } 119 | 120 | if real == false { 121 | continue 122 | } 123 | } 124 | 125 | if filterDetail != nil { 126 | var i int 127 | for i = 0; i < len(Value.Detail); i++ { 128 | OrderDetailMap, err = mysql.OrderDetail2Map(Value.Detail[i]) 129 | 130 | real, err = filterDetail.Execute(OrderDetailMap) 131 | 132 | if err != nil { 133 | return 134 | } 135 | if real == true { 136 | break 137 | } 138 | } 139 | 140 | if i >= len(Value.Detail) { 141 | continue 142 | } 143 | } 144 | 145 | total++ 146 | if len(param.Sorter) > 0 { 147 | 148 | EligibleOrderInfo = append(EligibleOrderInfo, OrderInfoMap) 149 | t[OrderId] = Value 150 | } else { 151 | if oi >= param.Start && oi < param.End { 152 | o = append(o, Value) 153 | } 154 | oi++ 155 | } 156 | 157 | } 158 | 159 | if len(EligibleOrderInfo) == 0 { 160 | return 161 | } 162 | if len(param.Sorter) > 0 { 163 | sorter, err = utils.NewSorter(&EligibleOrderInfo, param.Sorter) 164 | if err != nil { 165 | return 166 | } 167 | if sorter == nil { 168 | logger.Ex(this.ctx, "model.toc.query error", "init sorter error", "") 169 | return 170 | } 171 | } 172 | 173 | if sorter != nil { 174 | sort.Sort(sorter) 175 | for i := 0; i < len(*sorter.Slice); i++ { 176 | if uint(i) >= param.Start && uint(i) < param.End { 177 | OrderId := (*sorter.Slice)[i].(map[string]interface{})["order_id"].(string) 178 | o = append(o, t[OrderId]) 179 | } 180 | } 181 | } 182 | 183 | return 184 | 185 | } 186 | -------------------------------------------------------------------------------- /app/model/update/update.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-19 23:54:27 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-12 09:19:38 6 | * @Description: 7 | */ 8 | 9 | package update 10 | 11 | import ( 12 | "context" 13 | "fmt" 14 | logger "github.com/tal-tech/loggerX" 15 | "github.com/tal-tech/torm" 16 | "powerorder/app/constant" 17 | "powerorder/app/dao/mysql" 18 | "powerorder/app/dao/redis" 19 | "powerorder/app/model/finishing" 20 | "powerorder/app/params" 21 | "powerorder/app/utils" 22 | "strconv" 23 | ) 24 | 25 | type Update struct { 26 | uAppId uint 27 | uUserId uint64 28 | ctx context.Context 29 | } 30 | 31 | /** 32 | * @description: 33 | * @params {type} 34 | * @return: 35 | */ 36 | func NewUpdate(ctx context.Context, uAppId uint, uUserId uint64) *Update { 37 | object := new(Update) 38 | object.ctx = ctx 39 | object.uAppId = uAppId 40 | object.uUserId = uUserId 41 | return object 42 | } 43 | 44 | /** 45 | * @description: 46 | * @params {params} 更新的参数 47 | * @return: err 48 | */ 49 | func (this *Update) Update(param params.ReqUpdate) (err error) { 50 | session := mysql.NewSession(this.ctx, param.AppId, param.UserId, constant.DBWriter) 51 | pts := utils.GetPts(this.ctx) // 压测标识 52 | if session == nil { 53 | err = logger.NewError("init session error") 54 | return 55 | } 56 | //logger.Dx(this.ctx,"model.update.update.Update","model update params = %+v", param) 57 | defer session.Close() 58 | OrderInfoDao := mysql.NewOrderInfoDao2(param.AppId, param.UserId, session, this.ctx) 59 | OrderDetailDao := mysql.NewOrderDetailDao2(param.AppId, param.UserId, session, this.ctx) 60 | 61 | session.Begin() 62 | var res int64 63 | //OrderDetailDao := mysql.NewOrderDetailDao2(params.AppId, params.UserId, session) 64 | for i := 0; i < len(param.Orders); i++ { 65 | var where *torm.Session 66 | 67 | for extName, ExtData := range param.Orders[i].Extensions { 68 | ExtDao := mysql.NewExtensionDao2(param.AppId, param.UserId, extName, session, this.ctx) 69 | 70 | for j := 0; j < len(ExtData.Updates); j++ { 71 | 72 | Version := ExtData.Updates[j].Version 73 | Update := ExtData.Updates[j].Update 74 | for field, value := range Update { 75 | switch value.(type) { 76 | case string: 77 | Update[field] = value.(string) 78 | case float64: 79 | Update[field], _ = strconv.Atoi(strconv.FormatFloat(value.(float64), 'f', -1, 64)) 80 | } 81 | } 82 | 83 | if Version <= 0 { 84 | err = logger.NewError("error version") 85 | session.Rollback() 86 | return 87 | } 88 | if Version > 0 { 89 | ExtDao.SetTableName() 90 | where = ExtDao.Where("order_id = ?", param.Orders[i].OrderId) 91 | 92 | if Id, exist := Update["id"]; exist && len(Id.(string)) > 0 { 93 | where = where.And("id = ?", Id) 94 | //delete(Update, "id") // 移除更新字段 id 会影响数据 95 | } 96 | if Version < 0 { 97 | delete(Update, "version") 98 | } else { 99 | Update["version"] = strconv.Itoa(Version + 1) 100 | if pts != true { // 压测去掉这个条件 101 | where = where.And("version = ?", Version) 102 | } 103 | } 104 | 105 | res, err = where.Update(Update) 106 | if Version > 0 { 107 | if res == 0 { 108 | err = logger.NewError(fmt.Sprintf("%s nothing updated", extName)) 109 | session.Rollback() 110 | return 111 | } 112 | } 113 | 114 | if err != nil { 115 | session.Rollback() 116 | return 117 | } 118 | } else { 119 | err = logger.NewError(fmt.Sprintf("%s error version", extName)) 120 | session.Rollback() 121 | return 122 | } 123 | } 124 | 125 | for j := 0; j < len(ExtData.Insertions); j++ { 126 | Insertion := ExtData.Insertions[j] 127 | for field, value := range Insertion { 128 | switch value.(type) { 129 | case string: 130 | Insertion[field] = value.(string) 131 | case float64: 132 | Insertion[field], _ = strconv.Atoi(strconv.FormatFloat(value.(float64), 'f', -1, 64)) 133 | } 134 | } 135 | Id := utils.GenId() 136 | ((param.Orders[i].Extensions[extName]).Insertions)[j]["id"] = Id // ID是生成的,因此需要回写 137 | Insertion["order_id"] = param.Orders[i].OrderId 138 | Insertion["id"] = Id 139 | Insertion["version"] = 1 140 | Insertion["user_id"] = param.UserId 141 | ExtDao.SetTableName() 142 | 143 | res, err = ExtDao.Create(Insertion) 144 | 145 | if err != nil { 146 | session.Rollback() 147 | return 148 | } 149 | } 150 | } 151 | 152 | for j := 0; j < len(param.Orders[i].Detail); j++ { 153 | Version := param.Orders[i].Detail[j].Version 154 | if Version <= 0 { 155 | err = logger.NewError("error version") 156 | session.Rollback() 157 | return 158 | } 159 | if Version > 0 { 160 | Detail := param.Orders[i].Detail[j].Detail 161 | 162 | OrderDetailDao.SetTableName() 163 | 164 | where = OrderDetailDao.Where("order_id = ? AND app_id = ?", param.Orders[i].OrderId, param.AppId) 165 | 166 | if Detail.Id > 0 { 167 | where = where.And("id = ?", Detail.Id) 168 | } 169 | if Detail.ProductId > 0 { 170 | where = where.And("product_id = ?", Detail.ProductId) 171 | } 172 | if Detail.ProductType > 0 { 173 | where = where.And("product_type = ?", Detail.ProductType) 174 | } 175 | if Version == -1 { 176 | Detail.Version = 0 177 | } else if Version > 0 { 178 | Detail.Version = uint(Version) + 1 179 | where = where.And("version = ?", Version) 180 | } 181 | res, err = where.Update(Detail) 182 | 183 | if Version > 0 { 184 | if res == 0 { 185 | err = logger.NewError("detail nothing updated") 186 | session.Rollback() 187 | return 188 | } 189 | } 190 | 191 | if err != nil { 192 | session.Rollback() 193 | return 194 | } 195 | } else { 196 | err = logger.NewError("detail error version") 197 | session.Rollback() 198 | return 199 | } 200 | } 201 | 202 | Version := param.Orders[i].Info.Version 203 | if Version <= 0 { 204 | err = logger.NewError("error version") 205 | session.Rollback() 206 | return 207 | } 208 | if Version > 0 { 209 | Info := param.Orders[i].Info.Info 210 | 211 | OrderInfoDao.SetTableName() 212 | where = OrderInfoDao.Where("user_id = ? AND app_id = ? AND order_id = ?", param.UserId, param.AppId, param.Orders[i].OrderId) 213 | 214 | if Version == -1 { 215 | Info.Version = 0 216 | } else if Version > 0 { 217 | Info.Version = uint(Version) + 1 218 | where = where.And("version = ?", Version) 219 | } 220 | 221 | res, err = where.Update(Info) 222 | 223 | if Version > 0 { 224 | if res == 0 { 225 | err = logger.NewError("info nothing updated") 226 | session.Rollback() 227 | return 228 | } 229 | } 230 | 231 | if err != nil { 232 | session.Rollback() 233 | return 234 | } 235 | } else { 236 | err = logger.NewError("info error version") 237 | logger.Ex(this.ctx, "model.update.update.Update", "info error version", "") 238 | session.Rollback() 239 | return 240 | } 241 | } 242 | 243 | err = session.Commit() 244 | this.updateCache(param) 245 | if err == nil { 246 | go this.finishing(param) 247 | } 248 | return 249 | } 250 | 251 | /** 252 | * @description: 253 | * @params {type} 254 | * @return: 255 | */ 256 | func (this *Update) finishing(param params.ReqUpdate) { 257 | 258 | finishing := finishing.NewUpdate(param.AppId, param.UserId, this.ctx, "") 259 | err := finishing.UpdateOrder(param) 260 | if err != nil { 261 | 262 | } 263 | } 264 | 265 | /** 266 | * 先设置缓存 267 | */ 268 | func (this *Update) updateCache(param params.ReqUpdate) { 269 | if len(param.Orders) < 1 { 270 | return 271 | } 272 | finishing := finishing.NewInsertion(param.AppId, param.UserId, this.ctx, "") 273 | var txInfo redis.TxInfo 274 | OrderIds := make([]string, len(param.Orders)) 275 | for i := 0; i < len(param.Orders); i++ { 276 | OrderIds[i] = param.Orders[i].OrderId 277 | } 278 | ok := finishing.SetCache(OrderIds, txInfo) 279 | logger.Ix(this.ctx, "model.update.update.updateCache", "update.updateCache", "ret:%+v", ok) 280 | return 281 | } 282 | -------------------------------------------------------------------------------- /app/output/order.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-13 22:43:14 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-02-18 01:24:42 6 | * @Description: 7 | */ 8 | package output 9 | 10 | import ( 11 | "bytes" 12 | "encoding/json" 13 | "powerorder/app/dao/mysql" 14 | ) 15 | 16 | type Order struct { 17 | Info mysql.OrderInfo `json:"info"` 18 | Detail []mysql.OrderDetail `json:"detail"` 19 | Extensions map[string][]map[string]interface{} `json:"extensions"` 20 | } 21 | 22 | /** 23 | * @description: 24 | * @params {type} 25 | * @return: 26 | */ 27 | 28 | func (o Order) MarshalJSON() (b []byte, err error) { 29 | b = make([]byte, 0) 30 | bs := make([][]byte, 0) 31 | bs = append(bs, []byte("{")) 32 | var str []byte 33 | var first = true 34 | if o.Info.UserId != 0 { 35 | str, err = json.Marshal(o.Info) 36 | if err != nil { 37 | return 38 | } 39 | 40 | bs = append(bs, []byte("\"info\":")) 41 | bs = append(bs, []byte(str)) 42 | 43 | first = false 44 | } 45 | 46 | if len(o.Detail) > 0 { 47 | str, err = json.Marshal(o.Detail) 48 | if err != nil { 49 | return 50 | } 51 | 52 | if first == false { 53 | bs = append(bs, []byte(",")) 54 | } 55 | bs = append(bs, []byte("\"detail\":")) 56 | bs = append(bs, []byte(str)) 57 | 58 | first = false 59 | } 60 | 61 | if o.Extensions != nil { 62 | 63 | if first == false { 64 | bs = append(bs, []byte(",")) 65 | } 66 | //o.Extensions = make(map[string][]map[string]interface{}, 0) 67 | bs = append(bs, []byte("\"extensions\":")) 68 | 69 | str, err = json.Marshal(o.Extensions) 70 | if err != nil { 71 | return 72 | } 73 | bs = append(bs, []byte(str)) 74 | } 75 | bs = append(bs, []byte("}")) 76 | 77 | b = bytes.Join(bs, []byte("")) 78 | return b, nil 79 | } 80 | -------------------------------------------------------------------------------- /app/output/orderid.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-13 22:43:14 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-13 08:50:47 6 | * @Description: 7 | */ 8 | package output 9 | 10 | type OrderId struct { 11 | OrderId interface{} `json:"order_ids"` 12 | } 13 | -------------------------------------------------------------------------------- /app/params/addition.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import "powerorder/app/dao/mysql" 4 | 5 | type ReqAddition struct { 6 | Info mysql.OrderInfo `form:"info" json:"info" validate:"required"` 7 | Detail []mysql.OrderDetail `form:"detail" json:"detail" validate:"required"` 8 | Extensions map[string][]map[string]interface{} `form:"extensions" json:"extensions" validate:"required"` 9 | } 10 | 11 | type ReqBegin struct { 12 | AppId uint `form:"app_id" json:"app_id" validate:"required"` 13 | UserId uint64 `form:"user_id" json:"user_id" validate:"required"` 14 | Additions []ReqAddition `form:"additions" json:"additions" validate:"required"` 15 | } 16 | 17 | type ReqRollback struct { 18 | AppId uint `form:"app_id" json:"app_id" validate:"required"` 19 | UserId uint64 `form:"user_id" json:"user_id" validate:"required"` 20 | TxId string `form:"tx_id" json:"tx_id" validate:"required"` 21 | TxStatus uint `form:"tx_status" json:"tx_status" validate:"required"` 22 | } 23 | 24 | type ReqCommit struct { 25 | ReqRollback 26 | } 27 | 28 | type extensions map[string]interface{} 29 | 30 | type ReqOrder struct { 31 | Info mysql.OrderInfo `json:"info"` 32 | Detail []mysql.OrderDetail `json:"detail"` 33 | Extensions map[string][]extensions `json:"extensions"` 34 | } 35 | -------------------------------------------------------------------------------- /app/params/tob.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | type ReqDSearch struct { 4 | AppId uint `form:"app_id" json:"app_id"` 5 | Filter [][][]interface{} `form:"filter" json:"filter"` 6 | Field string `form:"field" json:"field"` 7 | Sorter []string `form:"sorter" json:"sorter"` 8 | Start uint `form:"start" json:"start"` 9 | End uint `form:"end" json:"end"` 10 | } 11 | 12 | 13 | type TobReqSearch struct { 14 | ReqSearch 15 | ExtFilter map[string][][][]interface{} `form:"ext_filter" json:"ext_filter"` 16 | } 17 | 18 | -------------------------------------------------------------------------------- /app/params/toc.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | 4 | type ReqSearch struct { 5 | AppId uint `form:"app_id" json:"app_id" validate:"required"` 6 | UserId uint64 `form:"user_id" json:"user_id" validate:"required"` 7 | InfoFilter [][][]interface{} `form:"info_filter" json:"info_filter"` 8 | DetailFilter [][][]interface{} `form:"detail_filter" json:"detail_filter"` 9 | Fields []string `form:"fields" json:"fields"` 10 | Sorter []string `form:"sorter" json:"sorter"` 11 | Start uint `form:"start" json:"start"` 12 | End uint `form:"end" json:"end"` 13 | } 14 | 15 | 16 | type ReqGet struct { 17 | AppId uint `form:"app_id" json:"app_id"` 18 | UserId uint64 `form:"user_id" json:"user_id"` 19 | OrderIds []string `form:"order_ids" json:"order_ids"` 20 | Fields []string `form:"fields" json:"fields"` 21 | } 22 | 23 | type ReqGenerate struct { 24 | AppId uint `form:"app_id" json:"app_id"` 25 | UserId uint64 `form:"user_id" json:"user_id"` 26 | Num uint `form:"num" json:"num"` 27 | } 28 | 29 | type ReqOrderIdSearch struct { 30 | AppId uint `form:"app_id" json:"app_id"` 31 | Filter [][][]interface{} `form:"filter" json:"filter"` 32 | Table string `form:"table" json:"table"` 33 | Num int64 `form:"num" json:"num"` 34 | } 35 | 36 | type ReqBGet struct { 37 | AppId uint `form:"app_id" json:"app_id"` 38 | Get []ReqGet `form:"get" json:"get"` 39 | Fields []string `form:"fields" json:"fields"` 40 | } 41 | 42 | type ReqQuery struct { 43 | AppId uint `form:"app_id" json:"app_id"` 44 | StartDate int64 `form:"start_date" json:"start_date"` 45 | EndDate int64 `form:"end_date" json:"end_date"` 46 | UserId uint64 `form:"user_id" json:"user_id"` 47 | InfoFilter [][][]interface{} `form:"info_filter" json:"info_filter"` 48 | DetailFilter [][][]interface{} `form:"detail_filter" json:"detail_filter"` 49 | Fields []string `form:"fields" json:"fields"` 50 | Sorter []string `form:"sorter" json:"sorter"` 51 | Start uint `form:"start" json:"start"` 52 | End uint `form:"end" json:"end"` 53 | } 54 | 55 | -------------------------------------------------------------------------------- /app/params/update.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import "powerorder/app/dao/mysql" 4 | 5 | type ReqUpdate struct { 6 | AppId uint `json:"app_id"` 7 | UserId uint64 `json:"user_id"` 8 | Orders []order `json:"orders"` 9 | Consist bool `json:"consist"` 10 | } 11 | 12 | // 更新的数据结构 13 | type order struct { 14 | OrderId string `json:"order_id"` 15 | Info info `form:"info" json:"info"` 16 | Detail []detail `form:"details" json:"details"` 17 | Extensions map[string]extension `form:"extensions" json:"extensions"` 18 | } 19 | 20 | type info struct { 21 | Version int `json:"version"` 22 | Info mysql.OrderInfo `json:"info"` 23 | } 24 | 25 | type detail struct { 26 | Version int `json:"version"` 27 | Detail mysql.OrderDetail `json:"detail"` 28 | } 29 | 30 | type extension struct { 31 | Updates []extUpdate `json:"updates"` 32 | Insertions []map[string]interface{} `json:"insertions"` 33 | } 34 | 35 | type extUpdate struct { 36 | Version int `json:"version"` 37 | Update map[string]interface{} `json:"update"` 38 | } -------------------------------------------------------------------------------- /app/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "powerorder/app/controller/toc/order/addition" 6 | "powerorder/app/controller/tool" 7 | "powerorder/app/middlewares" 8 | ) 9 | 10 | //The routing method is exactly the same as Gin 11 | func RegisterRouter(router *gin.Engine) { 12 | 13 | //can be used to enable CORS with various options 14 | router.Use(middlewares.CorsMiddleware()) 15 | 16 | //健康检查 17 | Tool := router.Group("/healthcheck") 18 | Tool.GET("/ping", tool.HealthCheck) 19 | 20 | //order addition 21 | tocAddition := router.Group("toc/addition", middlewares.VerifySignMiddleware()) 22 | 23 | tocAdditionSync := &addition.Sync{} 24 | tocAddition.POST("/sync", tocAdditionSync.Index) 25 | tocAdditionBegin := &addition.Begin{} 26 | tocAddition.POST("/begin", tocAdditionBegin.Index) 27 | tocAdditionCommit := &addition.Commit{} 28 | tocAddition.POST("/commit", tocAdditionCommit.Index) 29 | tocAdditionRollback := &addition.Rollback{} 30 | tocAddition.POST("/rollback", tocAdditionRollback.Index) 31 | 32 | ////order get 33 | //tocSearchGet := &search.Get{} 34 | //tocSearch := router.Group("toc/search", middlewares.VerifySignMiddleware()) 35 | //tocSearch.POST("/get", tocSearchGet.Index) 36 | //tocSearchSearch := &search.Search{} 37 | //tocSearch.POST("/search", tocSearchSearch.Index) 38 | //tocSearchQuery := &search.Query{} 39 | //tocSearch.POST("/query", tocSearchQuery.Index) 40 | 41 | ////orderid 42 | //toOrderId := router.Group("toc/orderid", middlewares.VerifySignMiddleware()) 43 | //tocOrderIdGen := &orderid.Generate{} 44 | //toOrderId.POST("/generate", tocOrderIdGen.Index) 45 | //tocOrderIdSearch := &orderid.Search{} 46 | //toOrderId.POST("/search", tocOrderIdSearch.Index) 47 | // 48 | //tocUpdateUpdate := &update.Update{} 49 | //toUpdate := router.Group("toc/update", middlewares.VerifySignMiddleware()) 50 | //toUpdate.POST("/update", tocUpdateUpdate.Index) 51 | 52 | ////order get 53 | //tobSearchGet := &tobsearch.Get{} 54 | //tobSearch := router.Group("tob/search", middlewares.VerifySignMiddleware()) 55 | //tobSearch.POST("/get", tobSearchGet.Index) 56 | //tobSearchSearch := &tobsearch.Search{} 57 | //tobSearch.POST("/search", tobSearchSearch.Index) 58 | //tobSearchDSearch := &tobsearch.DSearch{} 59 | //tobSearch.POST("/dsearch", tobSearchDSearch.Index) 60 | 61 | } 62 | -------------------------------------------------------------------------------- /app/utils/app.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "powerorder/app/constant" 5 | ) 6 | 7 | /* 8 | * @Author: lichanglin@tal.com 9 | * @Date: 2020-01-14 01:36:30 10 | * @LastEditors: lichanglin@tal.com 11 | * @LastEditTime: 2020-04-12 23:31:50 12 | * @Description: 13 | */ 14 | 15 | //mysql扩展字段分表信息 第一级为业务线Id 第二级为扩展表名 16 | var ExtensionTableShardingInfo map[uint]strmap 17 | 18 | //Idxdb集合字段类型信息 第一级为业务线Id 第二级为集合名 19 | var IdxdbCollectionFieldInfo map[uint]map[string]map[string]int 20 | 21 | //Idxdb集合索引信息 第一级为业务线Id 第二级为集合名 22 | var IdxdbCollectionIndexInfo map[uint]map[string][][]string 23 | 24 | //Info集合字段类型信息 25 | var InfoCollectionFieldInfo = map[string]int{ 26 | "created_time": constant.TypeString, 27 | "updated_time": constant.TypeString, 28 | "cancelled_time": constant.TypeString, 29 | "pay_created_time": constant.TypeString, 30 | "paid_time": constant.TypeString, 31 | "expired_time": constant.TypeString, 32 | "order_id": constant.TypeString, 33 | "user_id": constant.TypeInt, 34 | "id": constant.TypeInt, 35 | } 36 | 37 | //Info集合索引信息 38 | var InfoCollectionIndexInfo = [][]string{ 39 | []string{"created_time"}, 40 | []string{"updated_time"}, 41 | []string{"cancelled_time"}, 42 | []string{"pay_created_time"}, 43 | []string{"paid_time"}, 44 | []string{"expired_time"}, 45 | } 46 | 47 | //Detail集合字段类型信息 48 | var DetailCollectionFieldInfo = map[string]int{ 49 | "created_time": constant.TypeString, 50 | "updated_time": constant.TypeString, 51 | "product_name": constant.TypeString, 52 | "parent_product_id": constant.TypeInt, 53 | "product_id": constant.TypeInt, 54 | "order_id": constant.TypeString, 55 | "user_id": constant.TypeInt, 56 | "id": constant.TypeInt, 57 | } 58 | 59 | //Detail集合索引信息 60 | var DetailCollectionIndexInfo = [][]string{ 61 | []string{"created_time"}, 62 | []string{"updated_time"}, 63 | []string{"product_name"}, 64 | []string{"parent_product_id"}, 65 | []string{"product_id"}, 66 | } 67 | 68 | type strmap map[string]uint 69 | 70 | //业务线Id 71 | var AppIds = [...]uint{constant.AppBus1, constant.AppBus2} 72 | 73 | func init() { 74 | ExtensionTableShardingInfo = make(map[uint]strmap) 75 | IdxdbCollectionFieldInfo = make(map[uint]map[string]map[string]int) 76 | IdxdbCollectionIndexInfo = make(map[uint]map[string][][]string) 77 | 78 | //todo 提供一种注册模式 79 | initAppBus1(&ExtensionTableShardingInfo, &IdxdbCollectionFieldInfo, &IdxdbCollectionIndexInfo) 80 | initAppBus2(&ExtensionTableShardingInfo, &IdxdbCollectionFieldInfo, &IdxdbCollectionIndexInfo) 81 | } 82 | 83 | func initAppBus2(extTable *map[uint]strmap, idxTableField *map[uint]map[string]map[string]int, idx *map[uint]map[string][][]string) { 84 | //扩展表分表信息 扩展表 85 | extTableInfo := map[string]uint{ 86 | constant.BUS2_ExtensionTableName: 1, 87 | constant.BUS2_ExtensionPromotionInfoTableName: 1, 88 | } 89 | (*extTable)[constant.AppBus2] = extTableInfo 90 | // 索引表字段信息 91 | 92 | //Idxdb集合字段类型信息 93 | idxFieldInfo := map[string]map[string]int{ 94 | constant.Info: InfoCollectionFieldInfo, 95 | constant.Detail: DetailCollectionFieldInfo, 96 | //ExtensionTableName : map[string]int{ 97 | //}, 98 | } 99 | (*idxTableField)[constant.AppBus2] = idxFieldInfo 100 | 101 | // 索引库索引表字段 102 | idxInfo := map[string][][]string{ 103 | constant.Info: InfoCollectionIndexInfo, 104 | constant.Detail: DetailCollectionIndexInfo, 105 | //ExtensionTableName : [][]string{ 106 | //}, 107 | } 108 | (*idx)[constant.AppBus2] = idxInfo 109 | } 110 | 111 | func initAppBus1(extTable *map[uint]strmap, idxTableField *map[uint]map[string]map[string]int, idx *map[uint]map[string][][]string) { 112 | //扩展表分表信息 扩展表 113 | extTableInfo := map[string]uint{ 114 | constant.BUS1_GrouponInfoTableName: 1, 115 | constant.BUS1_GrouponDetailTableName: 1, 116 | constant.BUS1_PromotionInfoTableName: 4, 117 | } 118 | (*extTable)[constant.AppBus1] = extTableInfo 119 | // 索引表字段信息 120 | 121 | //Idxdb集合字段类型信息 122 | idxFieldInfo := map[string]map[string]int{ 123 | constant.Info: InfoCollectionFieldInfo, 124 | constant.Detail: DetailCollectionFieldInfo, 125 | constant.BUS1_GrouponInfoTableName: map[string]int{ 126 | "create_time": constant.TypeString, 127 | "end_time": constant.TypeString, 128 | "success_time": constant.TypeString, 129 | }, 130 | constant.BUS1_GrouponDetailTableName: map[string]int{ 131 | "groupon_order_id": constant.TypeString, 132 | }, 133 | } 134 | (*idxTableField)[constant.AppBus1] = idxFieldInfo 135 | 136 | // 索引库索引表字段 137 | idxInfo := map[string][][]string{ 138 | constant.Info: InfoCollectionIndexInfo, 139 | constant.Detail: DetailCollectionIndexInfo, 140 | constant.BUS1_GrouponInfoTableName: [][]string{ 141 | []string{"create_time"}, 142 | []string{"end_time"}, 143 | []string{"success_time"}, 144 | }, 145 | constant.BUS1_GrouponDetailTableName: [][]string{ 146 | []string{"groupon_order_id"}, 147 | }, 148 | } 149 | (*idx)[constant.AppBus1] = idxInfo 150 | } 151 | -------------------------------------------------------------------------------- /app/utils/casting.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-22 15:37:32 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-13 15:57:16 6 | * @Description: 7 | */ 8 | package utils 9 | 10 | import ( 11 | "encoding/json" 12 | "fmt" 13 | "github.com/spf13/cast" 14 | logger "github.com/tal-tech/loggerX" 15 | "powerorder/app/constant" 16 | "strconv" 17 | "time" 18 | ) 19 | 20 | /** 21 | * @description: 类型转换 22 | * @params {from} 23 | * @return: 24 | */ 25 | func JsonInterface2Int(from interface{}) (to int, ret bool) { 26 | to = 0 27 | ret = true 28 | var err error 29 | switch from.(type) { 30 | case float64: 31 | to, err = strconv.Atoi(strconv.FormatFloat(from.(float64), 'f', -1, 64)) 32 | if err != nil { 33 | logger.E("error from type", "str to float64 error") 34 | ret = false 35 | return 36 | } 37 | case int64: 38 | to = int(from.(int64)) 39 | return 40 | default: 41 | logger.E("error from type", "") 42 | ret = false 43 | return 44 | } 45 | return 46 | } 47 | 48 | /** 49 | * @description: 类型转换 50 | * @params {from} 51 | * @return: 52 | */ 53 | func JsonInterface2UInt(from interface{}) (to uint, ret bool) { 54 | to = 0 55 | ret = true 56 | var temp int 57 | temp, ret = JsonInterface2Int(from) 58 | 59 | if ret == false { 60 | return 61 | } 62 | 63 | to = uint(temp) 64 | return 65 | } 66 | 67 | /** 68 | * @description: 69 | * @params {type} 70 | * @return: 71 | */ 72 | func JsonInterface2IntArray(from interface{}) (tos []int, ret bool) { 73 | tos = []int{} 74 | ret = true 75 | var ok bool 76 | var tmpArr []interface{} 77 | var tmpInt int 78 | switch from.(type) { 79 | case []interface{}: 80 | if tmpArr, ok = from.([]interface{}); ok { 81 | 82 | for i := 0; i < len(tmpArr); i++ { 83 | tmpInt, ret = JsonInterface2Int(tmpArr[i]) 84 | if ret == false { 85 | return 86 | } 87 | tos = append(tos, tmpInt) 88 | } 89 | } else { 90 | logger.E("error from type", "") 91 | ret = false 92 | 93 | } 94 | default: 95 | logger.E("error from type", "") 96 | ret = false 97 | return 98 | } 99 | return 100 | } 101 | 102 | /** 103 | * @description: 104 | * @params {type} 105 | * @return: 106 | */ 107 | func JsonInterface2String(from interface{}) (to string, ret bool) { 108 | to = "" 109 | ret = true 110 | switch from.(type) { 111 | case string: 112 | to = from.(string) 113 | return 114 | default: 115 | ret = false 116 | return 117 | } 118 | return 119 | } 120 | 121 | /** 122 | * @description: 123 | * @params {type} 124 | * @return: 125 | */ 126 | func JsonInterface2StringArray(from interface{}) (tos []string, ret bool) { 127 | tos = []string{} 128 | ret = true 129 | var ok bool 130 | var tmpArr []interface{} 131 | var tmpStr string 132 | switch from.(type) { 133 | case []interface{}: 134 | if tmpArr, ok = from.([]interface{}); ok { 135 | 136 | for i := 0; i < len(tmpArr); i++ { 137 | tmpStr, ret = JsonInterface2String(tmpArr[i]) 138 | if ret == false { 139 | return 140 | } 141 | tos = append(tos, tmpStr) 142 | } 143 | } else { 144 | logger.E("error from type", "") 145 | ret = false 146 | } 147 | default: 148 | logger.E("error from type", "") 149 | ret = false 150 | return 151 | } 152 | return 153 | } 154 | 155 | /** 156 | * @description: 157 | * @params {type} 158 | * @return: 159 | */ 160 | func Struct2Map(from interface{}) (to map[string]interface{}, err error) { 161 | var data []byte 162 | data, err = json.Marshal(from) 163 | if err != nil { 164 | return 165 | } 166 | to = make(map[string]interface{}) 167 | err = json.Unmarshal(data, &to) 168 | return 169 | 170 | } 171 | 172 | /** 173 | * @description: 174 | * @params {type} 175 | * @return: 176 | */ 177 | func StructArray2MapArray(from []interface{}) (to []map[string]interface{}, err error) { 178 | to = make([]map[string]interface{}, len(from)) 179 | for i := 0; i < len(from); i++ { 180 | to[i], err = Struct2Map(from[i]) 181 | if err != nil { 182 | return 183 | } 184 | } 185 | return 186 | } 187 | 188 | /** 189 | * @description: 190 | * @params {type} 191 | * @return: 192 | */ 193 | func JsonInterface2Timestamp(from interface{}) (to int64, ret bool) { 194 | strTime, ret := JsonInterface2String(from) 195 | if ret == false { 196 | return 197 | } 198 | var stamp time.Time 199 | var err error 200 | stamp, err = time.ParseInLocation("2006-01-02 15:04:05", strTime, time.Local) //使用parseInLocation将字符串格式化返回本地时区时间 201 | if err != nil { 202 | ret = false 203 | return 204 | } 205 | to = (stamp.Unix()) //输出:1546926630 206 | return 207 | } 208 | 209 | /** 210 | * @description: 211 | * @params {type} 212 | * @return: 213 | */ 214 | func JsonInterface2TimeString(from interface{}) (to string, ret bool) { 215 | var t int 216 | t, ret = JsonInterface2Int(from) 217 | if ret == false { 218 | return 219 | } 220 | to = time.Unix(int64(t), 0).Format("2006-01-02 15:04:05") 221 | return 222 | 223 | } 224 | 225 | /** 226 | * @description: 解析filter成为mysql可使用的 227 | * @params {type} 228 | * @return: 229 | */ 230 | func And2Where(from [][]interface{}) (whereStr string, whereValue []interface{}) { 231 | whereValue = make([]interface{}, 0) 232 | whereStr = " 1=1 " 233 | for i := 0; i < len(from); i++ { 234 | field, _ := JsonInterface2String(from[i][0]) 235 | comparer, _ := JsonInterface2UInt(from[i][1]) 236 | value := from[i][2] 237 | switch comparer { 238 | case constant.IntGreater: 239 | whereStr = fmt.Sprintf("%s and %s > ?", whereStr, field) 240 | whereValue = append(whereValue, value) 241 | case constant.IntGreaterOrEqual: 242 | whereStr = fmt.Sprintf("%s and %s >= ?", whereStr, field) 243 | whereValue = append(whereValue, value) 244 | case constant.IntLess: 245 | whereStr = fmt.Sprintf("%s and %s < ?", whereStr, field) 246 | whereValue = append(whereValue, value) 247 | case constant.IntLessOrEqual: 248 | whereStr = fmt.Sprintf("%s and %s <= ?", whereStr, field) 249 | whereValue = append(whereValue, value) 250 | case constant.IntEqual: 251 | whereStr = fmt.Sprintf("%s and %s = ?", whereStr, field) 252 | whereValue = append(whereValue, value) 253 | case constant.IntNotEqual: 254 | whereStr = fmt.Sprintf("%s and %s != ?", whereStr, field) 255 | whereValue = append(whereValue, value) 256 | } 257 | } 258 | return 259 | } 260 | 261 | func ToStringStringMap(data interface{}) (map[string]string, error) { 262 | if valueArr, ok := data.([]interface{}); ok { 263 | back := make(map[string]string) 264 | for i := 0; i < len(valueArr); i = i + 2 { 265 | back[cast.ToString(valueArr[i])] = cast.ToString(valueArr[i+1]) 266 | } 267 | return back, nil 268 | } 269 | return cast.ToStringMapString(data), nil 270 | } 271 | -------------------------------------------------------------------------------- /app/utils/database.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | logger "github.com/tal-tech/loggerX" 6 | "powerorder/app/constant" 7 | "strconv" 8 | ) 9 | 10 | /* 11 | * @Author: lichanglin@tal.com 12 | * @Date: 2020-02-16 13:18:25 13 | * @LastEditors: lichanglin@tal.com 14 | * @LastEditTime: 2020-03-12 00:14:56 15 | * @Description: 16 | */ 17 | var shardingOrderInfo, _ = NewSharding(constant.DatabaseNum, constant.TableOrderInfoNum, 0) 18 | var shardingOrderDetail, _ = NewSharding(constant.DatabaseNum, constant.TableOrderDetailNum, 0) 19 | 20 | /** 21 | * @description: 22 | * @params {type} 23 | * @return: 24 | */ 25 | func GenDatabaseName(iUserId uint64) string { 26 | var uUserId = uint(iUserId % 10000) 27 | 28 | return fmt.Sprintf("%s%02d", constant.DatabaseNamePrx, shardingOrderInfo.DatabaseNo(uUserId)) 29 | } 30 | 31 | /** 32 | * @description: 33 | * @params {type} 34 | * @return: 35 | */ 36 | func GenDatabaseNameByOrderId(strOrderId string) string { 37 | strTailNumber := strOrderId[len(strOrderId)-4:] 38 | iUserId, _ := strconv.Atoi(strTailNumber) 39 | 40 | return GenDatabaseName(uint64(iUserId)) 41 | } 42 | 43 | /** 44 | * @description: 45 | * @params {type} 46 | * @return: 47 | */ 48 | func GenOrderInfoTableName(iUserId uint64) string { 49 | var uUserId = uint(iUserId % 10000) 50 | 51 | return fmt.Sprintf("%s%02d", constant.TableOrderInfoNamePrx, shardingOrderInfo.TableNo(uUserId)) 52 | } 53 | 54 | /** 55 | * @description: 56 | * @params {type} 57 | * @return: 58 | */ 59 | func GenOrderInfoTableNameByOrderId(strOrderId string) string { 60 | strTailNumber := strOrderId[len(strOrderId)-4:] 61 | iUserId, _ := strconv.Atoi(strTailNumber) 62 | 63 | return GenOrderInfoTableName(uint64(iUserId)) 64 | } 65 | 66 | /** 67 | * @description: 68 | * @params {type} 69 | * @return: 70 | */ 71 | func GenOrderDetailTableName(iUserId uint64) string { 72 | var uUserId = uint(iUserId % 10000) 73 | 74 | return fmt.Sprintf("%s%02d", constant.TableOrderDetailNamePrx, shardingOrderDetail.TableNo(uUserId)) 75 | } 76 | 77 | /** 78 | * @description: 79 | * @params {type} 80 | * @return: 81 | */ 82 | func GenOrderExtensionTableName(uUserId uint64, uAppId uint, strExtension string) (tableName string, err error) { 83 | var AppInfo map[string]uint 84 | var ok bool 85 | var TableNum uint 86 | if AppInfo, ok = ExtensionTableShardingInfo[uAppId]; !ok { 87 | err = logger.NewError("error appid") 88 | return 89 | } 90 | if TableNum, ok = AppInfo[strExtension]; !ok { 91 | err = logger.NewError(fmt.Sprintf("error extension table name:%s", strExtension)) 92 | return 93 | } 94 | 95 | shardingObject, err := NewSharding(constant.DatabaseNum, TableNum, 0) 96 | 97 | if err != nil { 98 | return 99 | } 100 | 101 | return fmt.Sprintf("%s_%03d_%02d", strExtension, uAppId, shardingObject.TableNo((uint)(uUserId%10000))), nil 102 | 103 | } 104 | 105 | /** 106 | * @description: 107 | * @params {type} 108 | * @return: 109 | */ 110 | func GenDatabaseIdxName() string { 111 | return constant.DatabaseIdxName 112 | } 113 | 114 | /** 115 | * @description: 116 | * @params {type} 117 | * @return: 118 | */ 119 | func GenOrderInfoIdxTableName(uAppId uint) string { 120 | return fmt.Sprintf("%s%03d", constant.TableOrderInfoIdxNamePrx, uAppId) 121 | } 122 | 123 | /** 124 | * @description: 125 | * @params {type} 126 | * @return: 127 | */ 128 | func GenOrderDetailIdxTableName(uAppId uint) string { 129 | return fmt.Sprintf("%s%03d", constant.TableOrderDetailIdxNamePrx, uAppId) 130 | } 131 | 132 | /** 133 | * @description: 134 | * @params {type} 135 | * @return: 136 | */ 137 | func GenOrderExtensionIdxTableName(uAppId uint, strExtension string) (tableName string, err error) { 138 | var AppInfo map[string]uint 139 | var ok bool 140 | if AppInfo, ok = ExtensionTableShardingInfo[uAppId]; !ok { 141 | err = logger.NewError("error appid") 142 | return 143 | } 144 | if _, ok = AppInfo[strExtension]; !ok { 145 | err = logger.NewError(fmt.Sprintf("error extension table name:%s", strExtension)) 146 | return 147 | } 148 | tableName = fmt.Sprintf("%s_idx_%03d", strExtension, uAppId) 149 | return 150 | 151 | } 152 | -------------------------------------------------------------------------------- /app/utils/dbutil.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import logger "github.com/tal-tech/loggerX" 4 | 5 | /* 6 | * @Author: lichanglin@tal.com 7 | * @Date: 2019-12-31 15:58:23 8 | * @LastEditors : lichanglin@tal.com 9 | * @LastEditTime : 2020-01-06 19:24:22 10 | * @Description: 11 | 以示例说明分库分表原理 12 | 假设分成2库2表(每个库中有2张表),精度为1024 13 | 0------------------256------------------512------------------768------------------1024 14 | 0---------------Database 0--------------512---------------Database 1--------------1024 15 | 0-----Table 0------256------Table 1-----512-----Table 0------768-----Table 1------1024 16 | */ 17 | 18 | const ( 19 | iDefaultPrecision = 1024 20 | ) 21 | 22 | type Sharding struct { 23 | iDatabaseNum uint //分库的数目,只分表的话此值为1 24 | iTableNum uint //单库中分表的数目 25 | iDatabasePrecision uint //分库的精度 默认为1024 26 | iTablePrecision uint //分表的精度 为分库的精度/分库的数目 27 | } 28 | 29 | /** 30 | * @description: 生成分库分表的对象 31 | * @param {iDatabaseNum 分库的个数,只分表不分库,此值为1} 32 | * @param {iTableNum 分表的个数(每个库中表的个数)} 33 | * @param {iPrecision 计算的精度} 34 | * @return: error 是否有异常 35 | * @return: *Sharding 生成的对象 36 | */ 37 | func NewSharding(iDatabaseNum, iTableNum, iPrecision uint) (*Sharding, error) { 38 | if iPrecision == 0 { 39 | iPrecision = iDefaultPrecision 40 | } 41 | if iDatabaseNum == 0 || iTableNum == 0 || iPrecision == 0 { 42 | return nil, logger.NewError("参数异常") 43 | } 44 | if iPrecision%iDatabaseNum != 0 || (iPrecision/iDatabaseNum)%iTableNum != 0 { 45 | return nil, logger.NewError("参数异常") 46 | } 47 | 48 | sharding := new(Sharding) 49 | sharding.iDatabaseNum = iDatabaseNum 50 | sharding.iTableNum = iTableNum 51 | sharding.iDatabasePrecision = iPrecision 52 | sharding.iTablePrecision = sharding.iDatabasePrecision / iDatabaseNum 53 | return sharding, nil 54 | 55 | } 56 | 57 | /** 58 | * @description: 计算分库后的库编号(从0开始) 59 | * @param {iBreakPoint 计算的奇点} 60 | * @return: 库编号 61 | */ 62 | func (this *Sharding) DatabaseNo(iBreakPoint uint) uint { 63 | return (iBreakPoint % this.iDatabasePrecision) / (this.iDatabasePrecision / this.iDatabaseNum) 64 | } 65 | 66 | /** 67 | * @description: 计算分表后的表编号(从0开始) 68 | * @param {iBreakPoing 计算的奇点} 69 | * @return: 表编号 70 | */ 71 | func (this *Sharding) TableNo(iBreakPoint uint) uint { 72 | return (iBreakPoint % this.iTablePrecision) / (this.iTablePrecision / this.iTableNum) 73 | } 74 | -------------------------------------------------------------------------------- /app/utils/filter.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-21 21:07:19 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-01-25 19:08:19 6 | * @Description: 7 | */ 8 | package utils 9 | 10 | import ( 11 | logger "github.com/tal-tech/loggerX" 12 | "powerorder/app/constant" 13 | ) 14 | 15 | type Filter struct { 16 | Rules [][][]interface{} 17 | } 18 | 19 | /** 20 | * @description: 过滤条件初始化,校验 21 | * @params {Rules} 22 | * @return: 23 | */ 24 | func NewFilter(Rules [][][]interface{}) *Filter { 25 | object := new(Filter) 26 | object.Rules = Rules 27 | return object 28 | } 29 | 30 | /** 31 | * @description: 32 | * @params {data} 33 | * @return: 34 | */ 35 | func (this *Filter) Execute(data map[string]interface{}) (real bool, err error) { 36 | real = false 37 | err = nil 38 | 39 | for i := 0; i < len(this.Rules); i++ { 40 | AndRules := this.Rules[i] 41 | 42 | real = true 43 | for j := 0; j < len(AndRules); j++ { 44 | var key string 45 | var field interface{} 46 | var comparer uint 47 | var ok bool 48 | Rule := AndRules[j] 49 | 50 | if len(Rule) != 3 { 51 | logger.E("filter error", "rule params len != 3 ") 52 | return 53 | } 54 | if key, ok = Rule[0].(string); !ok { 55 | logger.E("filter error", "error rule params[0]") 56 | return 57 | } 58 | 59 | if field, ok = data[key]; !ok { 60 | logger.E("filter error", "error filed, key:%s", key) 61 | return 62 | } 63 | 64 | comparer, real = JsonInterface2UInt(Rule[1]) 65 | if real == false { 66 | return 67 | } 68 | 69 | switch field.(type) { 70 | case float64: 71 | default: 72 | comparer += 100 73 | } 74 | if comparer < constant.StrGreater { 75 | real, err = this.IntCompare(field, comparer, Rule[2]) 76 | } else { 77 | real, err = this.StrCompare(field, comparer, Rule[2]) 78 | } 79 | 80 | if err != nil { 81 | return 82 | } 83 | if real == false { 84 | break 85 | } 86 | } 87 | 88 | if real == true { 89 | return 90 | } 91 | } 92 | real = false 93 | err = nil 94 | return 95 | } 96 | 97 | /** 98 | * @description: 99 | * @params {field} 当前字段的值 100 | * @params {comparer} 运算符 101 | * @params {value} 参数对应的值 102 | * @return: 103 | */ 104 | func (this *Filter) IntCompare(field interface{}, comparer uint, value interface{}) (real bool, err error) { 105 | var c1 int 106 | var c2 []int 107 | var c3 int 108 | 109 | c1, real = JsonInterface2Int(field) 110 | if real == false { 111 | return 112 | } 113 | if comparer == constant.IntWithIn || comparer == constant.IntNotWithIn { 114 | c2, real = JsonInterface2IntArray(value) 115 | if real == false { 116 | return 117 | } 118 | } else { 119 | c3, real = JsonInterface2Int(value) 120 | 121 | if real == false { 122 | return 123 | } 124 | 125 | } 126 | switch comparer { 127 | case constant.IntGreater: 128 | return c1 > c3, nil 129 | case constant.IntGreaterOrEqual: 130 | return c1 >= c3, nil 131 | case constant.IntLess: 132 | return c1 < c3, nil 133 | case constant.IntLessOrEqual: 134 | return c1 <= c3, nil 135 | case constant.IntEqual: 136 | return c1 == c3, nil 137 | case constant.IntNotEqual: 138 | return c1 != c3, nil 139 | case constant.IntNotWithIn: 140 | for i := 0; i < len(c2); i++ { 141 | if c1 == c2[i] { 142 | return false, nil 143 | } 144 | } 145 | return true, nil 146 | case constant.IntWithIn: 147 | for i := 0; i < len(c2); i++ { 148 | if c1 == c2[i] { 149 | return true, nil 150 | } 151 | } 152 | return false, nil 153 | default: 154 | logger.E("comparer error", "%d", comparer) 155 | return false, nil 156 | } 157 | return false, nil 158 | } 159 | 160 | /** 161 | * @description: 162 | * @params {type} 163 | * @return: 164 | */ 165 | func (this *Filter) StrCompare(field interface{}, comparer uint, value interface{}) (real bool, err error) { 166 | var c1 string 167 | var c2 []string 168 | var c3 string 169 | var ok bool 170 | if c1, ok = field.(string); !ok { 171 | logger.E("error field type", "") 172 | real = false 173 | return 174 | } 175 | if comparer == constant.StrWithIn || comparer == constant.StrNotWithIn { 176 | if c2, real = JsonInterface2StringArray(value); real == false { 177 | return 178 | } 179 | } else { 180 | if c3, real = JsonInterface2String(value); real == false { 181 | return 182 | } 183 | } 184 | switch comparer { 185 | case constant.StrGreater: 186 | return c1 > c3, nil 187 | case constant.StrGreaterOrEqual: 188 | return c1 >= c3, nil 189 | case constant.StrLess: 190 | return c1 < c3, nil 191 | case constant.StrLessOrEqual: 192 | return c1 <= c3, nil 193 | case constant.StrEqual: 194 | return c1 == c3, nil 195 | case constant.StrNotEqual: 196 | return c1 != c3, nil 197 | case constant.StrNotWithIn: 198 | for i := 0; i < len(c2); i++ { 199 | if c1 == c2[i] { 200 | return false, nil 201 | } 202 | } 203 | return true, nil 204 | case constant.StrWithIn: 205 | for i := 0; i < len(c2); i++ { 206 | if c1 == c2[i] { 207 | return true, nil 208 | } 209 | } 210 | return false, nil 211 | default: 212 | logger.E("error comparer", "%d", comparer) 213 | return false, nil 214 | } 215 | return false, nil 216 | } 217 | -------------------------------------------------------------------------------- /app/utils/functions.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/tal-tech/xtools/kafkautil" 6 | "reflect" 7 | "runtime/debug" 8 | "strings" 9 | "time" 10 | 11 | "fmt" 12 | "math" 13 | 14 | "context" 15 | "github.com/spf13/cast" 16 | logger "github.com/tal-tech/loggerX" 17 | "github.com/tal-tech/xtools/jsutil" 18 | ) 19 | 20 | func RemoveDuplicateAndEmpty(arr []string) []string { 21 | m := make(map[string]bool, 0) 22 | result := make([]string, 0) 23 | for _, v := range arr { 24 | if strings.Trim(v, " ") != "" { 25 | if _, ok := m[v]; !ok { 26 | m[v] = true 27 | result = append(result, v) 28 | } 29 | } 30 | } 31 | return result 32 | } 33 | 34 | func Convert(oldType interface{}, newType interface{}) (err error) { 35 | logger.D("[Convert]", "params:%v,%v, types:%v, %v", oldType, newType, reflect.TypeOf(oldType), reflect.TypeOf(newType)) 36 | str, err := jsutil.Json.Marshal(oldType) 37 | if err != nil { 38 | return err 39 | } 40 | err = jsutil.Json.Unmarshal(str, &newType) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | logger.D("[Convert]", "result:%v", newType) 46 | return nil 47 | } 48 | 49 | func ConvertWithMarshaledInput(oldStr string, newType interface{}) (back interface{}, err error) { 50 | err = jsutil.Json.Unmarshal([]byte(oldStr), &newType) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return newType, nil 55 | } 56 | 57 | ///////////////////////////////////////////////////////////////////////////// 58 | ///////////////////////////////////////////////////////////////////////////// 59 | func MapUnion_Interface(dst *map[string]interface{}, src map[string]interface{}) { 60 | for index, value := range src { 61 | if _, ok := (*dst)[index]; !ok { 62 | (*dst)[index] = value 63 | } 64 | } 65 | } 66 | 67 | func MapUnion_Map(dst *map[string]map[string]interface{}, src map[string]map[string]interface{}) { 68 | for index, value := range src { 69 | if _, ok := (*dst)[index]; !ok { 70 | (*dst)[index] = value 71 | } 72 | } 73 | } 74 | 75 | func MapUnion_Int64Slice(dst *map[string][]int64, src map[string][]int64) { 76 | for index, value := range src { 77 | if _, ok := (*dst)[index]; !ok { 78 | (*dst)[index] = value 79 | } 80 | } 81 | } 82 | func MapUnion_StringSlice(dst *map[string][]string, src map[string][]string) { 83 | for index, value := range src { 84 | if _, ok := (*dst)[index]; !ok { 85 | (*dst)[index] = value 86 | } 87 | } 88 | } 89 | 90 | //todo: check entry existences 91 | func SliceUnion_Int64(dst *[]int64, src []int64) { 92 | for _, value := range src { 93 | *dst = append(*dst, value) 94 | } 95 | } 96 | 97 | func Int64SliceToInterfaceSlice(params []int64) []interface{} { 98 | logger.D("[Int64SliceToInterfaceSlice]", "params:%v", params) 99 | 100 | result := make([]interface{}, 0) 101 | for _, element := range params { 102 | result = append(result, element) 103 | } 104 | 105 | logger.D("[Int64Slicetointerfaceslice]", "result:%v", result) 106 | return result 107 | } 108 | func StringSliceToInterfaceSlice(params []string) []interface{} { 109 | logger.D("[StringSliceToInterfaceSlice]", "params:%v", params) 110 | 111 | result := make([]interface{}, 0) 112 | for _, element := range params { 113 | result = append(result, element) 114 | } 115 | 116 | logger.D("[Stringslicetointerfaceslice]", "result:%v", result) 117 | return result 118 | } 119 | 120 | func TimeCostDuration(tag string, start time.Time) { 121 | du := time.Now().Sub(start) 122 | logger.D(tag, "Cost %d", du.Nanoseconds()) 123 | } 124 | 125 | func JsonMustMarshal(v interface{}) string { 126 | result, err := jsutil.Json.Marshal(v) 127 | if err != nil { 128 | panic(logger.NewError("JsonMustMarshal failed, error:" + err.Error())) 129 | } 130 | 131 | return string(result) 132 | } 133 | 134 | /*** 135 | remove comment and whitespace 136 | ***/ 137 | func JsonMinify(str string) string { 138 | in_string := false 139 | in_single_comment := false 140 | in_multi_comment := false 141 | string_opener := string('x') 142 | 143 | var retStr string = "" 144 | 145 | size := len(str) 146 | for i := 0; i < size; i++ { 147 | 148 | c := fmt.Sprintf("%s", []byte{str[i]}) 149 | next := math.Min(cast.ToFloat64(i+2), cast.ToFloat64(size)) 150 | cc := fmt.Sprintf("%s", []byte(str[i:cast.ToInt64(next)])) 151 | 152 | if in_string { 153 | if c == string_opener { 154 | in_string = false 155 | retStr += c 156 | } else if c == "\\" { 157 | retStr += cc 158 | i++ 159 | } else { 160 | retStr += c 161 | } 162 | } else if in_single_comment { 163 | if c == "\r" || c == "\n" { 164 | in_single_comment = false 165 | } 166 | } else if in_multi_comment { 167 | if cc == "*/" { 168 | in_multi_comment = false 169 | i++ 170 | } 171 | } else { 172 | if cc == "/*" { 173 | in_multi_comment = true 174 | i++ 175 | } else if cc == "//" { 176 | in_single_comment = true 177 | i++ 178 | } else if c[0] == '"' || c[0] == '\'' { 179 | in_string = true 180 | string_opener = c 181 | retStr += c 182 | } else if c != " " && c != "\t" && c != "\n" && c != "\r" { 183 | retStr += c 184 | } 185 | } 186 | } 187 | return retStr 188 | } 189 | 190 | func MapIntersect(s1, s2 []string) []string { 191 | length1 := len(s1) 192 | length2 := len(s2) 193 | intersect := make([]string, 0) 194 | s1map := make(map[string]string, length1) 195 | for i := 0; i < length1; i++ { 196 | s1map[s1[i]] = s1[i] 197 | } 198 | 199 | for j := 0; j < length2; j++ { 200 | if _, ok := s1map[s2[j]]; ok { 201 | intersect = append(intersect, s1map[s2[j]]) 202 | } 203 | } 204 | return intersect 205 | } 206 | 207 | func TransferToContext(c *gin.Context) context.Context { 208 | ctx := context.Background() 209 | for k, v := range c.Keys { 210 | ctx = context.WithValue(ctx, k, v) 211 | } 212 | return ctx 213 | } 214 | 215 | func SendKafka(topic string, msg []byte) { 216 | err := kafkautil.Send2Proxy(topic, msg) 217 | if err != nil { 218 | logger.E("SendKafkaError", topic+" %v", err) 219 | } 220 | } 221 | 222 | // 日期转成秒 223 | func DateTime2Sec(str string) int64 { 224 | loc ,_ := time.LoadLocation("Asia/Shanghai") 225 | tm ,_ := time.ParseInLocation("2006-01-02 15:04:05",str,loc) 226 | return tm.Unix() 227 | } 228 | 229 | func Catch() { 230 | if rec := recover(); rec != nil { 231 | if err, ok := rec.(error); ok { 232 | logger.E("PanicRecover", "Unhandled error: %v\n stack:%v", err.Error(), cast.ToString(debug.Stack())) 233 | } else { 234 | logger.E("PanicRecover", "Panic: %v\n stack:%v", rec, cast.ToString(debug.Stack())) 235 | } 236 | } 237 | } -------------------------------------------------------------------------------- /app/utils/gpool.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "sync" 4 | 5 | // 协程池 6 | type Gpool struct { 7 | queue chan int 8 | wg *sync.WaitGroup 9 | } 10 | 11 | func NewGPool(size int) *Gpool { 12 | if size < 1 { 13 | size = 10 14 | } 15 | pool := new(Gpool) 16 | pool.queue = make(chan int , size) 17 | pool.wg = &sync.WaitGroup{} 18 | return pool 19 | } 20 | 21 | func (p *Gpool) Add(delta int) { 22 | for i := 0; i < delta; i++ { 23 | p.queue <- 1 24 | } 25 | for i := 0; i > delta; i-- { 26 | <-p.queue 27 | } 28 | p.wg.Add(delta) 29 | } 30 | 31 | func (p *Gpool) Done() { 32 | <-p.queue 33 | p.wg.Done() 34 | } 35 | 36 | func (p *Gpool) Wait() { 37 | p.wg.Wait() 38 | } 39 | -------------------------------------------------------------------------------- /app/utils/httpResponse.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import logger "github.com/tal-tech/loggerX" 4 | 5 | // Response the unified json structure 6 | type response struct { 7 | Code int `json:"code"` 8 | Stat int `json:"stat"` 9 | Message string `json:"msg"` 10 | Data interface{} `json:"data"` 11 | } 12 | 13 | func Success(v interface{}) interface{} { 14 | ret := response{Stat: 1, Code: 0, Message: "ok", Data: v} 15 | return ret 16 | } 17 | 18 | func Error(err error) interface{} { 19 | e := logger.NewError(err) 20 | ret := response{Stat: 0, Code: e.Code, Message: e.Message, Data: e.Info} 21 | return ret 22 | } 23 | -------------------------------------------------------------------------------- /app/utils/idworker.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-14 16:20:17 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-16 16:11:31 6 | * @Description: 7 | */ 8 | package utils 9 | 10 | import ( 11 | "errors" 12 | "fmt" 13 | logger "github.com/tal-tech/loggerX" 14 | "github.com/tal-tech/xredis" 15 | "math/rand" 16 | "os" 17 | "powerorder/app/constant" 18 | "strconv" 19 | "sync" 20 | "time" 21 | ) 22 | 23 | type IdWorker struct { 24 | startTime int64 25 | workerIdBits uint 26 | datacenterIdBits uint 27 | maxWorkerId int64 28 | maxDatacenterId int64 29 | sequenceBits uint 30 | workerIdLeftShift uint 31 | datacenterIdLeftShift uint 32 | timestampLeftShift uint 33 | sequenceMask int64 34 | workerId int64 35 | datacenterId int64 36 | sequence int64 37 | lastTimestamp int64 38 | signMask int64 39 | idLock *sync.Mutex 40 | } 41 | 42 | var idObj *IdWorker 43 | 44 | const workerKey = "xes_order_platform_workers" 45 | 46 | /** 47 | * @description: 生成64位随机int 48 | * @params {type} 49 | * @return: 50 | */ 51 | func GenId() (random int64) { 52 | currWorker, err := newIdWorker() 53 | if err != nil { 54 | logger.E("InitIdWorker failed", "err %+v", err) 55 | return 56 | } 57 | random, _ = currWorker.nextId() 58 | return 59 | } 60 | 61 | func newIdWorker() (obj *IdWorker, err error) { 62 | if idObj != nil && idObj.workerId > 0 { 63 | obj = idObj 64 | return 65 | } 66 | obj = &IdWorker{} 67 | 68 | // 获取workerId 和 datacenterId 69 | workerId, datacenterId := obj.getWorkerId() 70 | 71 | var baseValue int64 = -1 72 | obj.startTime = 1463834116272 73 | obj.workerIdBits = 5 74 | obj.datacenterIdBits = 5 75 | obj.maxWorkerId = baseValue ^ (baseValue << obj.workerIdBits) 76 | obj.maxDatacenterId = baseValue ^ (baseValue << obj.datacenterIdBits) 77 | obj.sequenceBits = 12 78 | obj.workerIdLeftShift = obj.sequenceBits 79 | obj.datacenterIdLeftShift = obj.workerIdBits + obj.workerIdLeftShift 80 | obj.timestampLeftShift = obj.datacenterIdBits + obj.datacenterIdLeftShift 81 | obj.sequenceMask = baseValue ^ (baseValue << obj.sequenceBits) 82 | obj.sequence = 0 83 | obj.lastTimestamp = -1 84 | obj.signMask = ^baseValue + 1 85 | 86 | obj.idLock = &sync.Mutex{} 87 | 88 | if obj.workerId < 0 || obj.workerId > obj.maxWorkerId { 89 | err = errors.New(fmt.Sprintf("workerId[%v] is less than 0 or greater than maxWorkerId[%v].", workerId, datacenterId)) 90 | return 91 | } 92 | if obj.datacenterId < 0 || obj.datacenterId > obj.maxDatacenterId { 93 | err = errors.New(fmt.Sprintf("datacenterId[%d] is less than 0 or greater than maxDatacenterId[%d].", workerId, datacenterId)) 94 | return 95 | } 96 | obj.workerId = workerId 97 | obj.datacenterId = datacenterId 98 | idObj = obj 99 | return 100 | } 101 | 102 | //方案一: 通过配置 103 | func (this *IdWorker) getWorkerId() (workerId, datacenterId int64) { 104 | datacenterId = rand.Int63n(31) 105 | workerId = rand.Int63n(31) 106 | hostName, err := os.Hostname() 107 | if err != nil || hostName == "" { 108 | return 109 | } 110 | rds := xredis.NewSimpleXesRedis(nil, constant.Order_Redis_Cluster) 111 | workers, err := rds.HGetAll(workerKey, nil) 112 | if err != nil { 113 | return 114 | } 115 | workerNum := len(workers) 116 | if _, ok := workers[hostName]; !ok { 117 | workerNum = workerNum + 1 118 | rds.HSet(workerKey, nil, hostName, workerNum) 119 | workerId = int64(workerNum) 120 | } else { 121 | workerId, _ = strconv.ParseInt(workers[hostName], 10, 64) 122 | } 123 | return 124 | } 125 | 126 | func (this *IdWorker) nextId() (int64, error) { 127 | this.idLock.Lock() 128 | defer this.idLock.Unlock() 129 | timestamp := this.genTime() 130 | if timestamp < this.lastTimestamp { 131 | return -1, errors.New(fmt.Sprintf("Clock moved backwards. Refusing to generate id for %d milliseconds", this.lastTimestamp-timestamp)) 132 | } 133 | 134 | if timestamp == this.lastTimestamp { 135 | this.sequence = (this.sequence + 1) & this.sequenceMask 136 | if this.sequence == 0 { 137 | timestamp = this.tilNextMillis() 138 | this.sequence = 0 139 | } 140 | } else { 141 | this.sequence = 0 142 | } 143 | 144 | this.lastTimestamp = timestamp 145 | 146 | id := ((timestamp - this.startTime) << this.timestampLeftShift) | 147 | (this.datacenterId << this.datacenterIdLeftShift) | 148 | (this.workerId << this.workerIdLeftShift) | 149 | this.sequence 150 | 151 | if id < 0 { 152 | id = -id 153 | } 154 | 155 | return id, nil 156 | } 157 | 158 | func (this *IdWorker) tilNextMillis() int64 { 159 | timestamp := this.genTime() 160 | if timestamp <= this.lastTimestamp { 161 | timestamp = this.genTime() 162 | } 163 | return timestamp 164 | } 165 | 166 | func (this *IdWorker) genTime() int64 { 167 | return time.Now().UnixNano() / int64(time.Millisecond) 168 | } 169 | -------------------------------------------------------------------------------- /app/utils/jstime.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-25 18:43:52 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-04-13 09:46:48 6 | * @Description: 7 | */ 8 | package utils 9 | 10 | import ( 11 | "fmt" 12 | "time" 13 | 14 | "go.mongodb.org/mongo-driver/bson" 15 | "go.mongodb.org/mongo-driver/bson/bsontype" 16 | ) 17 | 18 | type Time time.Time 19 | 20 | const ( 21 | timeFormart = "2006-01-02 15:04:05" 22 | ) 23 | 24 | /** 25 | * @description: 26 | * @params {type} 27 | * @return: 28 | */ 29 | func (t *Time) UnmarshalJSON(data []byte) (err error) { 30 | now, err := time.ParseInLocation(`"`+timeFormart+`"`, string(data), time.Local) 31 | *t = Time(now) 32 | return 33 | } 34 | 35 | /** 36 | * @description: 37 | * @params {type} 38 | * @return: 39 | */ 40 | func (t Time) MarshalJSON() ([]byte, error) { 41 | return []byte(`"` + time.Time(t).Format(timeFormart) + `"`), nil 42 | 43 | b := make([]byte, 0, len(timeFormart)+2) 44 | b = append(b, '"') 45 | b = time.Time(t).AppendFormat(b, timeFormart) 46 | b = append(b, '"') 47 | return b, nil 48 | } 49 | 50 | /** 51 | * @description: 52 | * @params {type} 53 | * @return: 54 | */ 55 | func (t Time) String() string { 56 | 57 | return time.Time(t).Format(timeFormart) 58 | } 59 | 60 | /** 61 | * @description: 62 | * @params {type} 63 | * @return: 64 | */ 65 | func (t Time) Unix() int64 { 66 | return time.Time(t).Unix() 67 | } 68 | 69 | /** 70 | * @description: MarshalBSON marshal bson 71 | * @params {type} 72 | * @return: 73 | */ 74 | func (t Time) MarshalBSON() ([]byte, error) { 75 | txt, err := time.Time(t).MarshalText() 76 | if err != nil { 77 | return nil, err 78 | } 79 | b, err := bson.Marshal(map[string]string{"t": string(txt)}) 80 | return b, err 81 | } 82 | 83 | /** 84 | * @description:MarshalBSONValue marshal bson value 85 | * @params {type} 86 | * @return: 87 | */ 88 | func (t *Time) MarshalBSONValue() (bsontype.Type, []byte, error) { 89 | b, err := bson.Marshal(time.Time(*t)) 90 | return bson.TypeEmbeddedDocument, b, err 91 | } 92 | 93 | /** 94 | * @description: UnmarshalBSON unmarshal bson 95 | * @params {type} 96 | * @return: 97 | */ 98 | func (t *Time) UnmarshalBSON(data []byte) error { 99 | var err error 100 | var d bson.D 101 | err = bson.Unmarshal(data, &d) 102 | if err != nil { 103 | return err 104 | } 105 | if v, ok := d.Map()["t"]; ok { 106 | tt := time.Time{} 107 | err = tt.UnmarshalText([]byte(v.(string))) 108 | *t = Time(tt) 109 | return err 110 | } 111 | return fmt.Errorf("key 't' missing") 112 | } 113 | -------------------------------------------------------------------------------- /app/utils/message/message.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-27 18:40:38 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-04 01:41:41 6 | * @Description: 7 | */ 8 | package message 9 | 10 | import ( 11 | "fmt" 12 | "github.com/spf13/cast" 13 | "github.com/tal-tech/xtools/kafkautil" 14 | "powerorder/app/constant" 15 | ) 16 | 17 | type Message struct { 18 | strTopic string 19 | uUserId uint64 20 | uAppId uint 21 | } 22 | 23 | /** 24 | * @description: 25 | * @params {type} 26 | * @return: 27 | */ 28 | func NewMessage(uUserId uint64, uAppId uint) *Message { 29 | object := new(Message) 30 | object.uUserId = uUserId 31 | object.uAppId = uAppId 32 | object.InitTopic() 33 | return object 34 | } 35 | 36 | /** 37 | * @description: 38 | * @params {type} 39 | * @return: 40 | */ 41 | func (this *Message) InitTopic() (err error) { 42 | 43 | this.strTopic = fmt.Sprintf("%s%03d", constant.TopicPrex, this.uAppId) 44 | err = nil 45 | return 46 | } 47 | 48 | /** 49 | * @description: 50 | * @params {type} 51 | * @return: 52 | */ 53 | func (this *Message) Send(param interface{}) (err error) { 54 | err = nil 55 | err = kafkautil.Send2Proxy(this.strTopic, []byte("kafka "+cast.ToString(param))) 56 | 57 | return 58 | } 59 | -------------------------------------------------------------------------------- /app/utils/order.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-07 18:47:22 4 | * @LastEditors: lichanglin@tal.com 5 | * @LastEditTime: 2020-03-13 01:02:50 6 | * @Description: 7 | */ 8 | package utils 9 | 10 | import ( 11 | "fmt" 12 | "math/rand" 13 | "time" 14 | ) 15 | 16 | /** 17 | * @description: 生成订单Id,生成格式:时间秒(12位)+ appid(3位)+ 随机数(5位)+ 用户Id后四位(4位)=24 18 | * @params {uAppId} 业务线Id 19 | * @params {time} 订单创建时间 20 | * @params {uUserId} 用户Id 21 | * @return: 生成的订单Id 22 | */ 23 | func GenOrderId(uAppId uint, time time.Time, uUserId uint64) string { 24 | return fmt.Sprintf("%s%03d%05d%04d", time.Format("060102150405"), uAppId%1000, rand.Int63n(100000), uUserId%10000) 25 | } 26 | -------------------------------------------------------------------------------- /app/utils/shadow.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: xiaotao@tal.com 3 | * @Date: 2020-05-13 16:20:17 4 | * @Description: 5 | */ 6 | package utils 7 | 8 | import ( 9 | "context" 10 | ) 11 | 12 | const ( 13 | APP_SEA_MAXSIZE = 50 // 大海业务线需求 14 | 15 | TOC_QUERY_MAX_DAY = 366 * 86400 16 | TOC_QUERY_MIN_DAY = 1 17 | 18 | SQL_WHERE_IN_MAX = 100 // mysql in查询最大个数 19 | ) 20 | const ( 21 | DB_SHADOW = "shadow" 22 | ) 23 | 24 | // 查询压测标识 25 | func GetPts(ctx context.Context) bool { 26 | var pts bool 27 | pts = ctx.Value("pts").(bool) 28 | return pts 29 | } 30 | 31 | // 影子库 读写替换 32 | func DbShadowHandler(ctx context.Context, handler string) string { 33 | var pts bool 34 | pts = ctx.Value("pts").(bool) 35 | if pts == true { 36 | return handler + "_" + DB_SHADOW 37 | } 38 | return handler 39 | } 40 | -------------------------------------------------------------------------------- /app/utils/sorter.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: lichanglin@tal.com 3 | * @Date: 2020-01-26 06:37:14 4 | * @LastEditors : lichanglin@tal.com 5 | * @LastEditTime : 2020-02-04 01:38:30 6 | * @Description: 7 | */ 8 | 9 | package utils 10 | 11 | import ( 12 | "fmt" 13 | logger "github.com/tal-tech/loggerX" 14 | "powerorder/app/constant" 15 | ) 16 | 17 | type Sorter struct { 18 | //承载以任意结构体为元素构成的Slice 19 | Slice *[]interface{} 20 | //排序规则,比如{"status", "desc", "type", "asc"},以order by status desc, type asc排序 21 | Rules []string 22 | } 23 | 24 | /** 25 | * @description: 校验排序方式参数 26 | * @params {rules} 27 | * @return: err 28 | */ 29 | func CheckRules(rules []string) (ret bool) { 30 | if len(rules)%2 != 0 { 31 | logger.I("CheckRules", "len of rules is not even number") 32 | return false 33 | } 34 | for i := 0; i < len(rules); i += 2 { 35 | if rules[i+1] != constant.Desc && rules[i+1] != constant.Asc { 36 | logger.E("CheckRules", "rules[%d] is wrong(not desc and not asc)", i) 37 | return false 38 | } 39 | } 40 | return true 41 | } 42 | 43 | /** 44 | * @description: 45 | * @params {type} 46 | * @return: 47 | */ 48 | func NewSorter(data *[]interface{}, rules []string) (object *Sorter, err error) { 49 | 50 | ret := CheckRules(rules) 51 | if !ret { 52 | err = logger.NewError("checkrules failed") 53 | return nil, err 54 | } 55 | for i := 0; i < len(*data); i++ { 56 | switch (*data)[i].(type) { 57 | case map[string]interface{}: 58 | element, _ := (*data)[i].(map[string]interface{}) 59 | for j := 0; j < len(rules); j += 2 { 60 | 61 | key := rules[j] 62 | if _, ok := element[key]; !ok { 63 | return nil, logger.NewError(fmt.Sprintf("data [%d] has not %s", i, key)) 64 | 65 | } 66 | } 67 | default: 68 | return nil, logger.NewError(fmt.Sprintf("data [%d] type is not map[string]interface{}", i)) 69 | } 70 | } 71 | 72 | object = new(Sorter) 73 | 74 | object.Slice = data 75 | object.Rules = rules 76 | 77 | return 78 | } 79 | 80 | /** 81 | * @description: 82 | * @params {type} 83 | * @return: 84 | */ 85 | func (this Sorter) Len() int { 86 | return len(*this.Slice) 87 | } 88 | 89 | /** 90 | * @description: 91 | * @params {type} 92 | * @return: 93 | */ 94 | func (this Sorter) Swap(i, j int) { 95 | (*this.Slice)[i], (*this.Slice)[j] = (*this.Slice)[j], (*this.Slice)[i] 96 | } 97 | 98 | /** 99 | * @description: 100 | * @params {type} 101 | * @return: 102 | */ 103 | func (this Sorter) Less(i, j int) bool { 104 | 105 | elementi, _ := (*this.Slice)[i].(map[string]interface{}) 106 | elementj, _ := (*this.Slice)[j].(map[string]interface{}) 107 | 108 | for k := 0; k < len(this.Rules); k += 2 { 109 | 110 | key := this.Rules[k] 111 | valuei, _ := elementi[key] 112 | valuej, _ := elementj[key] 113 | 114 | if valuei == valuej { 115 | continue 116 | } 117 | 118 | switch valuei.(type) { 119 | case float64: 120 | vali, _ := JsonInterface2Int(valuei) 121 | valj, _ := JsonInterface2Int(valuej) 122 | if this.Rules[k+1] == constant.Asc { 123 | 124 | return vali < valj 125 | } else { 126 | 127 | return vali > valj 128 | } 129 | default: 130 | vali, _ := JsonInterface2String(valuei) 131 | valj, _ := JsonInterface2String(valuej) 132 | if this.Rules[k+1] == constant.Asc { 133 | 134 | return vali < valj 135 | } else { 136 | 137 | return vali > valj 138 | } 139 | } 140 | 141 | } 142 | return false 143 | } 144 | -------------------------------------------------------------------------------- /cmd/powerorder/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "powerorder/app/router" 6 | "runtime/debug" 7 | "time" 8 | 9 | "powerorder/version" 10 | 11 | bs "github.com/tal-tech/hera/bootstrap" 12 | "github.com/tal-tech/hera/ginhttp" 13 | "github.com/tal-tech/xtools/confutil" 14 | "github.com/tal-tech/xtools/flagutil" 15 | 16 | "github.com/spf13/cast" 17 | ) 18 | 19 | func main() { 20 | //show version 21 | ver := flagutil.GetVersion() 22 | if *ver { 23 | version.Version() 24 | return 25 | } 26 | 27 | //init conf 28 | confutil.InitConfig() 29 | 30 | defer recovery() 31 | s := ginhttp.NewServer() 32 | engine := s.GetGinEngine() 33 | 34 | //Add middleware 35 | //You can customize the middleware according to your actual needs 36 | engine.Use() 37 | 38 | router.RegisterRouter(engine) 39 | 40 | //Front hook for service startup 41 | s.AddBeforeServerStartFunc( 42 | bs.InitLoggerWithConf(), 43 | bs.InitTraceLogger("Powerorder", "1.0"), 44 | s.InitConfig(), 45 | ) 46 | 47 | //Exec hook Funcs before the service to closing 48 | s.AddAfterServerStopFunc(bs.CloseLogger()) 49 | 50 | er := s.Serve() 51 | if er != nil { 52 | log.Printf("Server stop err:%v", er) 53 | } else { 54 | log.Printf("Server exit") 55 | } 56 | } 57 | 58 | func recovery() { 59 | //panic cause program exit quickly,Some logs may not have time to be written to disk 60 | time.Sleep(time.Second) 61 | 62 | if rec := recover(); rec != nil { 63 | log.Printf("Panic Panic occur") 64 | if err, ok := rec.(error); ok { 65 | log.Printf("PanicRecover Unhandled error: %v\n stack:%v", err.Error(), cast.ToString(debug.Stack())) 66 | } else { 67 | log.Printf("PanicRecover Panic: %v\n stack:%v", rec, cast.ToString(debug.Stack())) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cmd/powerorder/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "powerorder/app/router" 7 | "io/ioutil" 8 | "net/http" 9 | "os" 10 | "syscall" 11 | "testing" 12 | "time" 13 | 14 | "github.com/spf13/cast" 15 | "github.com/tal-tech/hera/ginhttp" 16 | logger "github.com/tal-tech/loggerX" 17 | "github.com/tal-tech/xtools/confutil" 18 | "github.com/tal-tech/xtools/flagutil" 19 | ) 20 | 21 | func TestServer(t *testing.T) { 22 | flagutil.SetConfig("conf/conf.ini") 23 | confutil.SetConfPathPrefix(os.Getenv("GOPATH") + "/src/powerorder") 24 | logger.InitLogger(os.Getenv("GOPATH") + "/src/powerorder/conf/log.xml") 25 | confutil.InitConfig() 26 | //signal.Notify(exit, os.Interrupt, syscall.SIGTERM) 27 | s := ginhttp.NewServer() 28 | router.RegisterRouter(s.GetGinEngine()) 29 | s.AddServerBeforeFunc(s.InitConfig()) 30 | defer logger.Close() 31 | 32 | go func() { 33 | err := s.Serve() 34 | if err != nil { 35 | t.Fatalf("TestServer start failed,err:%v", err) 36 | } 37 | }() 38 | time.Sleep(1 * time.Second) 39 | send(t) 40 | /* 41 | <-exit 42 | s.Stop() 43 | */ 44 | } 45 | 46 | func send(t *testing.T) { 47 | t.Log("sending") 48 | httpclient := http.DefaultClient 49 | var resp *http.Response 50 | var req *http.Request 51 | port := confutil.GetConf("Server", "addr") 52 | req, err := http.NewRequest("GET", "http://127.0.0.1"+port+"/demo/test", nil) 53 | if err != nil { 54 | t.Fatalf("TestServer new request failed,err:%v", err) 55 | } 56 | resp, err = httpclient.Do(req) 57 | if err != nil { 58 | t.Fatalf("TestServer do request failed,err:%v", err) 59 | } 60 | 61 | if resp.StatusCode == 200 { 62 | ret, err := ioutil.ReadAll(resp.Body) 63 | if err != nil { 64 | t.Fatalf("TestServer parse response failed,err:%v", err) 65 | } 66 | result := make(map[string]interface{}, 0) 67 | fmt.Println(string(ret)) 68 | if err = json.Unmarshal(ret, &result); err != nil { 69 | t.Fatalf("TestServer parse response failed,err:%v", err) 70 | } else { 71 | code := cast.ToInt(result["code"]) 72 | if code != 0 { 73 | t.Fatalf("TestServer response failed,code:%v", code) 74 | } 75 | } 76 | resp.Body.Close() 77 | } else { 78 | t.Fatalf("TestServer response failed,http code type err, code:%v", resp.StatusCode) 79 | } 80 | 81 | pid := os.Getpid() 82 | process, err := os.FindProcess(pid) 83 | if err != nil { 84 | t.Fatalf("TestServer failed,err:%v", err) 85 | } 86 | /*once := &sync.Once{} 87 | once.Do(func() { 88 | t.Log("restart") 89 | process.Signal(syscall.SIGUSR2) 90 | time.Sleep(10 * time.Second) 91 | }) 92 | */ 93 | t.Log("close") 94 | process.Signal(syscall.SIGTERM) 95 | 96 | time.Sleep(1 * time.Second) 97 | } 98 | -------------------------------------------------------------------------------- /conf/conf.ini: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | 3 | [Redis] 4 | order=127.0.0.1:6379 5 | 6 | [RedisConfig] 7 | order.password= 8 | order.idletimeout=240 9 | order.poolsize=100 10 | order.db=0 11 | 12 | [Server] 13 | ;Gin启动的模式,可选debug/release 14 | mode=debug 15 | addr=:9898 16 | grace=true 17 | readTimeout=3s 18 | writeTimeout=3s 19 | 20 | [Log] 21 | logPath=/tmp/powerorder.log 22 | ; level :DEBUG/INFO/WARNING/ERROR 23 | level=INFO 24 | rotateSize=1G 25 | rotateHourly=true 26 | rotate=true 27 | ;保留24小时,特别注意下,此处配置需要修改,如果升级了logger版本,此处还是配置1,那么日志只保留1小时 28 | retention=12 29 | 30 | [Registry] 31 | ;Registry Address 32 | addrs=127.0.0.1:1009 33 | 34 | [ElasticSearch] 35 | ;es地址,多个地址以空格分割 36 | url=http://es.platform.com 37 | ;是否开启集群嗅探,注意集群低版本开启会失败 38 | sinff=false 39 | ;是否开启集群健康检查,不设置默认开启 40 | healthCheck=true -------------------------------------------------------------------------------- /conf/conf_dev.ini: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | 3 | [Redis] 4 | order=127.0.0.1:6379 5 | 6 | [RedisConfig] 7 | order.password= 8 | order.idletimeout=240 9 | order.poolsize=100 10 | order.db=0 11 | 12 | [Server] 13 | ;Gin启动的模式,可选debug/release 14 | mode=debug 15 | addr=:9898 16 | grace=true 17 | readTimeout=3s 18 | writeTimeout=3s 19 | 20 | [Log] 21 | logPath=/tmp/powerorder.log 22 | ; level :DEBUG/INFO/WARNING/ERROR 23 | level=INFO 24 | rotateSize=1G 25 | rotateHourly=true 26 | rotate=true 27 | ;保留24小时,特别注意下,此处配置需要修改,如果升级了logger版本,此处还是配置1,那么日志只保留1小时 28 | retention=12 29 | 30 | [Registry] 31 | ;Registry Address 32 | addrs=127.0.0.1:1009 33 | 34 | [ElasticSearch] 35 | ;es地址,多个地址以空格分割 36 | url=http://es.platform.com 37 | ;是否开启集群嗅探,注意集群低版本开启会失败 38 | sinff=false 39 | ;是否开启集群健康检查,不设置默认开启 40 | healthCheck=true -------------------------------------------------------------------------------- /conf/order.ini: -------------------------------------------------------------------------------- 1 | ;This file is the configuration managed by the supervisord 2 | [program:powerorder] 3 | directory=/home/dev/powerorder 4 | command=/home/dev/powerorder/bin/powerorder 5 | autostart=true 6 | autorestart=true 7 | redirect_stderr=true 8 | stopsignal=INT 9 | stdout_logfile_maxbytes=20MB 10 | stdout_logfile_backups=20 11 | stdout_logfile=/home/logs/powerorder/stdout.log 12 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module powerorder 2 | 3 | replace github.com/smallnest/rpcx v0.0.0 => github.com/smallnest/rpcx v0.0.0-20200214051052-c65a6415f3d1 4 | 5 | require ( 6 | github.com/Unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8 // indirect 7 | github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // indirect 8 | github.com/bitly/go-simplejson v0.5.0 9 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect 10 | github.com/fortytw2/leaktest v1.3.0 // indirect 11 | github.com/gin-gonic/gin v1.6.3 12 | github.com/go-redis/redis v6.15.9+incompatible // indirect 13 | github.com/go-redis/redis/v8 v8.4.0 // indirect 14 | github.com/henrylee2cn/goutil v0.0.0-20200801052108-cbf313aea969 // indirect 15 | github.com/henrylee2cn/teleport v5.0.0+incompatible // indirect 16 | github.com/jpillora/overseer v1.1.6 // indirect 17 | github.com/olivere/elastic v6.2.35+incompatible 18 | github.com/opentracing/opentracing-go v1.2.0 // indirect 19 | github.com/openzipkin/zipkin-go v0.2.2 20 | github.com/pkg/errors v0.9.1 21 | github.com/sirupsen/logrus v1.5.0 // indirect 22 | github.com/smallnest/rpcx v0.0.0 // indirect 23 | github.com/spf13/cast v1.3.1 24 | github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e // indirect 25 | github.com/tal-tech/connPool v0.0.0-20200806112113-738c408fe6ae // indirect 26 | github.com/tal-tech/hera v1.0.1 27 | github.com/tal-tech/loggerX v1.0.1 28 | github.com/tal-tech/routinePool v0.0.0-20200806121001-477db7bdba8a // indirect 29 | github.com/tal-tech/torm v1.0.2 30 | github.com/tal-tech/xredis v1.0.0 31 | github.com/tal-tech/xtools v0.0.0-20200925092432-5d398cc834e0 32 | go.mongodb.org/mongo-driver v1.4.4 33 | go.uber.org/zap v1.16.0 // indirect 34 | 35 | ) 36 | 37 | go 1.13 38 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | var ( 9 | TAG string = "" 10 | VERSION string = "" 11 | BUILD_DATE string = "" 12 | AUTHOR string = "" 13 | BUILD_INFO string = "" 14 | ) 15 | 16 | func Version() { 17 | fmt.Fprintf(os.Stderr, "tag\t%s\n", TAG) 18 | fmt.Fprintf(os.Stderr, "version\t%s\n", VERSION) 19 | fmt.Fprintf(os.Stderr, "build\t%s\n", BUILD_DATE) 20 | fmt.Fprintf(os.Stderr, "author\t%s\n", AUTHOR) 21 | fmt.Fprintf(os.Stderr, "info\t%s\n", BUILD_INFO) 22 | } 23 | --------------------------------------------------------------------------------