package service import ( "context" "fmt" "strings" "time" "go-common/app/admin/main/growup/dao" "go-common/app/admin/main/growup/dao/resource" "go-common/app/admin/main/growup/model" "go-common/library/log" ) const ( _datetimeLayout = "2006-01-02 15:04:05" _dateLayout = "2006-01-02" ) func (s *Service) tradeDao() dao.TradeDao { return s.dao } // SyncGoods . secret portal... func (s *Service) SyncGoods(c context.Context, gt int) (eff int64, err error) { if gt != int(model.GoodsVIP) { err = fmt.Errorf("unsupported goodsType(%v)", gt) return } // query vips, err := resource.VipProducts(c) if err != nil { return } existing := make(map[string]*model.GoodsInfo) allGoods, err := s.tradeDao().GoodsList(c, fmt.Sprintf("is_deleted=0 AND goods_type=%d", gt), 0, 200) if err != nil { return } for _, v := range allGoods { existing[v.ProductID] = v } // hardcoded k-v pairs m := map[int32]int64{ 1: 16, 3: 17, 12: 18, } // add incrementally newGoods := make([]string, 0) for k, v := range vips { if _, ok := existing[k]; ok { continue } rid, ok := m[v.Month] if !ok { err = fmt.Errorf("unkonwn vip goods, month(%d)", v.Month) } newGoods = append(newGoods, fmt.Sprintf("('%s', %d, %d, %d, %d)", v.ProductID, rid, gt, model.DisplayOff, 100)) } if len(newGoods) == 0 { return } fields := "ex_product_id, ex_resource_id, goods_type, is_display, discount" return s.tradeDao().AddGoods(c, fields, strings.Join(newGoods, ",")) } // GoodsList . func (s *Service) GoodsList(c context.Context, from, limit int) (total int64, res []*model.GoodsInfo, err error) { if total, err = s.tradeDao().GoodsCount(c, "is_deleted=0"); err != nil { return } if total == 0 { res = make([]*model.GoodsInfo, 0) return } if res, err = s.tradeDao().GoodsList(c, "is_deleted=0", from, limit); err != nil || len(res) == 0 { return } // external information of vip goods var vips map[string]*model.GoodsInfo if vips, err = resource.VipProducts(c); err != nil { return } for _, target := range res { if src, ok := vips[target.ProductID]; ok { model.MergeExternal(target, src) } target.GoodsTypeDesc = target.GoodsType.Desc() } return } // UpdateGoodsInfo by ID func (s *Service) UpdateGoodsInfo(c context.Context, discount int, ID int64) (int64, error) { // select and diff before update? set := fmt.Sprintf("discount=%d", discount) return s.tradeDao().UpdateGoods(c, set, "", []int64{ID}) } // UpdateGoodsDisplay by IDs func (s *Service) UpdateGoodsDisplay(c context.Context, status model.DisplayStatus, IDs []int64) (eff int64, err error) { switch status { case model.DisplayOn: eff, err = s.onlineGoods(c, IDs) case model.DisplayOff: eff, err = s.offlineGoods(c, IDs) default: err = fmt.Errorf("illegal display status(%v)", status) } return } // onlineGoods by IDs func (s *Service) onlineGoods(c context.Context, IDs []int64) (int64, error) { now := time.Now().Format(_datetimeLayout) set := fmt.Sprintf("is_display=%d, display_on_time='%s'", model.DisplayOn, now) return s.tradeDao().UpdateGoods(c, set, "is_deleted=0", IDs) } // offlineGoods by IDs func (s *Service) offlineGoods(c context.Context, IDs []int64) (int64, error) { now := time.Now().Format(_datetimeLayout) set := fmt.Sprintf("is_display=%d, display_off_time='%s'", model.DisplayOff, now) return s.tradeDao().UpdateGoods(c, set, "is_deleted=0", IDs) } // OrderStatistics . func (s *Service) OrderStatistics(c context.Context, arg *model.OrderQueryArg) (data interface{}, err error) { if pass := preprocess(c, arg); !pass { return } where := orderQueryStr(arg) var orders []*model.OrderInfo if orders, err = s.orderAll(c, where); err != nil { return } for _, v := range orders { v.GenDerived() } data = orderStatistics(orders, arg.StartTime, arg.EndTime, arg.TimeType) return } // orderAll . be careful using this func (s *Service) orderAll(c context.Context, where string) (orders []*model.OrderInfo, err error) { offset, size := 0, 2000 for { list, err := s.tradeDao().OrderList(c, where, offset, size) if err != nil { return nil, err } orders = append(orders, list...) if len(list) < size { break } offset += len(list) } return } // orderStatistics . ugly... func orderStatistics(orders []*model.OrderInfo, start, end time.Time, timeType model.TimeType) interface{} { type orderStatUnit struct { orderNum int64 totalPrice int64 totalCost int64 } m := make(map[time.Time]*orderStatUnit) for _, v := range orders { date := timeType.RangeStart(v.OrderTime) if _, ok := m[date]; !ok { m[date] = &orderStatUnit{} } m[date].orderNum += v.GoodsNum m[date].totalCost += v.TotalCost m[date].totalPrice += v.TotalPrice } dates, orderNums, totalCost, totalPrice := make([]string, 0), make([]int64, 0), make([]int64, 0), make([]int64, 0) for start.Before(end) { next := timeType.Next()(start) dates = append(dates, timeType.RangeDesc(start, next)) if v, ok := m[start]; ok { orderNums = append(orderNums, v.orderNum) totalCost = append(totalCost, v.totalCost) totalPrice = append(totalPrice, v.totalPrice) } else { orderNums = append(orderNums, 0) totalCost = append(totalCost, 0) totalPrice = append(totalPrice, 0) } start = next } // result data := map[string]interface{}{ "xAxis": dates, "order_num": orderNums, "total_cost": totalCost, "total_income": totalPrice, } return data } // OrderExport . func (s *Service) OrderExport(c context.Context, arg *model.OrderQueryArg, from, limit int) (res []byte, err error) { _, orders, err := s.OrderList(c, arg, from, limit) if err != nil { return } records := make([][]string, 0, len(orders)+1) records = append(records, model.OrderExportFields()) for _, v := range orders { records = append(records, v.ExportStrings()) } if res, err = FormatCSV(records); err != nil { log.Error("FormatCSV error(%v)", err) } return } // OrderList . func (s *Service) OrderList(c context.Context, arg *model.OrderQueryArg, from, limit int) (total int64, list []*model.OrderInfo, err error) { list = make([]*model.OrderInfo, 0) if pass := preprocess(c, arg); !pass { return } where := orderQueryStr(arg) if total, err = s.tradeDao().OrderCount(c, where); err != nil || total == 0 { return } if list, err = s.tradeDao().OrderList(c, where, from, limit); err != nil || len(list) == 0 { return } // fetch names mids := make([]int64, 0) for _, v := range list { mids = append(mids, v.MID) } m, err := resource.NamesByMIDs(c, mids) if err != nil { return } // generate & merge for _, v := range list { v.GenDerived().GenDesc() if name, ok := m[v.MID]; ok { v.Nickname = name } } return } func preprocess(c context.Context, arg *model.OrderQueryArg) bool { if arg.Nickname != "" { mid, err := resource.MidByNickname(c, arg.Nickname) if err != nil || mid == 0 { return false } log.Info("resource.MidByNickname name(%s) mid(%d)", arg.Nickname, arg.MID) if arg.MID == 0 { arg.MID = mid } if arg.MID != mid { log.Error("illegal mid(%d) and nickname(%s) pair", arg.MID, arg.Nickname) return false } } arg.StartTime = arg.TimeType.RangeStart(time.Unix(arg.FromTime, 0)) arg.EndTime = arg.TimeType.RangeEnd(time.Unix(arg.ToTime, 0)) return true } func orderQueryStr(arg *model.OrderQueryArg) string { var where []string where = append(where, "is_deleted=0") { // 左开右闭 where = append(where, fmt.Sprintf("order_time >= '%s'", arg.StartTime.Format(_dateLayout))) where = append(where, fmt.Sprintf("order_time < '%s'", arg.EndTime.Format(_dateLayout))) } if arg.GoodsType > 0 { where = append(where, fmt.Sprintf("goods_type=%d", arg.GoodsType)) } if arg.GoodsID != "" { where = append(where, fmt.Sprintf("goods_id='%s'", arg.GoodsID)) } if arg.GoodsName != "" { where = append(where, fmt.Sprintf("goods_name='%s'", arg.GoodsName)) } if arg.OrderNO != "" { where = append(where, fmt.Sprintf("order_no='%s'", arg.OrderNO)) } if arg.MID > 0 { where = append(where, fmt.Sprintf("mid=%d", arg.MID)) } return strings.Join(where, " AND ") }