trade.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "time"
  7. "go-common/app/admin/main/growup/dao"
  8. "go-common/app/admin/main/growup/dao/resource"
  9. "go-common/app/admin/main/growup/model"
  10. "go-common/library/log"
  11. )
  12. const (
  13. _datetimeLayout = "2006-01-02 15:04:05"
  14. _dateLayout = "2006-01-02"
  15. )
  16. func (s *Service) tradeDao() dao.TradeDao {
  17. return s.dao
  18. }
  19. // SyncGoods . secret portal...
  20. func (s *Service) SyncGoods(c context.Context, gt int) (eff int64, err error) {
  21. if gt != int(model.GoodsVIP) {
  22. err = fmt.Errorf("unsupported goodsType(%v)", gt)
  23. return
  24. }
  25. // query
  26. vips, err := resource.VipProducts(c)
  27. if err != nil {
  28. return
  29. }
  30. existing := make(map[string]*model.GoodsInfo)
  31. allGoods, err := s.tradeDao().GoodsList(c, fmt.Sprintf("is_deleted=0 AND goods_type=%d", gt), 0, 200)
  32. if err != nil {
  33. return
  34. }
  35. for _, v := range allGoods {
  36. existing[v.ProductID] = v
  37. }
  38. // hardcoded <vip_product_duration_in_month, external_resource_id> k-v pairs
  39. m := map[int32]int64{
  40. 1: 16,
  41. 3: 17,
  42. 12: 18,
  43. }
  44. // add incrementally
  45. newGoods := make([]string, 0)
  46. for k, v := range vips {
  47. if _, ok := existing[k]; ok {
  48. continue
  49. }
  50. rid, ok := m[v.Month]
  51. if !ok {
  52. err = fmt.Errorf("unkonwn vip goods, month(%d)", v.Month)
  53. }
  54. newGoods = append(newGoods, fmt.Sprintf("('%s', %d, %d, %d, %d)", v.ProductID, rid, gt, model.DisplayOff, 100))
  55. }
  56. if len(newGoods) == 0 {
  57. return
  58. }
  59. fields := "ex_product_id, ex_resource_id, goods_type, is_display, discount"
  60. return s.tradeDao().AddGoods(c, fields, strings.Join(newGoods, ","))
  61. }
  62. // GoodsList .
  63. func (s *Service) GoodsList(c context.Context, from, limit int) (total int64, res []*model.GoodsInfo, err error) {
  64. if total, err = s.tradeDao().GoodsCount(c, "is_deleted=0"); err != nil {
  65. return
  66. }
  67. if total == 0 {
  68. res = make([]*model.GoodsInfo, 0)
  69. return
  70. }
  71. if res, err = s.tradeDao().GoodsList(c, "is_deleted=0", from, limit); err != nil || len(res) == 0 {
  72. return
  73. }
  74. // external information of vip goods
  75. var vips map[string]*model.GoodsInfo
  76. if vips, err = resource.VipProducts(c); err != nil {
  77. return
  78. }
  79. for _, target := range res {
  80. if src, ok := vips[target.ProductID]; ok {
  81. model.MergeExternal(target, src)
  82. }
  83. target.GoodsTypeDesc = target.GoodsType.Desc()
  84. }
  85. return
  86. }
  87. // UpdateGoodsInfo by ID
  88. func (s *Service) UpdateGoodsInfo(c context.Context, discount int, ID int64) (int64, error) {
  89. // select and diff before update?
  90. set := fmt.Sprintf("discount=%d", discount)
  91. return s.tradeDao().UpdateGoods(c, set, "", []int64{ID})
  92. }
  93. // UpdateGoodsDisplay by IDs
  94. func (s *Service) UpdateGoodsDisplay(c context.Context, status model.DisplayStatus, IDs []int64) (eff int64, err error) {
  95. switch status {
  96. case model.DisplayOn:
  97. eff, err = s.onlineGoods(c, IDs)
  98. case model.DisplayOff:
  99. eff, err = s.offlineGoods(c, IDs)
  100. default:
  101. err = fmt.Errorf("illegal display status(%v)", status)
  102. }
  103. return
  104. }
  105. // onlineGoods by IDs
  106. func (s *Service) onlineGoods(c context.Context, IDs []int64) (int64, error) {
  107. now := time.Now().Format(_datetimeLayout)
  108. set := fmt.Sprintf("is_display=%d, display_on_time='%s'", model.DisplayOn, now)
  109. return s.tradeDao().UpdateGoods(c, set, "is_deleted=0", IDs)
  110. }
  111. // offlineGoods by IDs
  112. func (s *Service) offlineGoods(c context.Context, IDs []int64) (int64, error) {
  113. now := time.Now().Format(_datetimeLayout)
  114. set := fmt.Sprintf("is_display=%d, display_off_time='%s'", model.DisplayOff, now)
  115. return s.tradeDao().UpdateGoods(c, set, "is_deleted=0", IDs)
  116. }
  117. // OrderStatistics .
  118. func (s *Service) OrderStatistics(c context.Context, arg *model.OrderQueryArg) (data interface{}, err error) {
  119. if pass := preprocess(c, arg); !pass {
  120. return
  121. }
  122. where := orderQueryStr(arg)
  123. var orders []*model.OrderInfo
  124. if orders, err = s.orderAll(c, where); err != nil {
  125. return
  126. }
  127. for _, v := range orders {
  128. v.GenDerived()
  129. }
  130. data = orderStatistics(orders, arg.StartTime, arg.EndTime, arg.TimeType)
  131. return
  132. }
  133. // orderAll . be careful using this
  134. func (s *Service) orderAll(c context.Context, where string) (orders []*model.OrderInfo, err error) {
  135. offset, size := 0, 2000
  136. for {
  137. list, err := s.tradeDao().OrderList(c, where, offset, size)
  138. if err != nil {
  139. return nil, err
  140. }
  141. orders = append(orders, list...)
  142. if len(list) < size {
  143. break
  144. }
  145. offset += len(list)
  146. }
  147. return
  148. }
  149. // orderStatistics . ugly...
  150. func orderStatistics(orders []*model.OrderInfo, start, end time.Time, timeType model.TimeType) interface{} {
  151. type orderStatUnit struct {
  152. orderNum int64
  153. totalPrice int64
  154. totalCost int64
  155. }
  156. m := make(map[time.Time]*orderStatUnit)
  157. for _, v := range orders {
  158. date := timeType.RangeStart(v.OrderTime)
  159. if _, ok := m[date]; !ok {
  160. m[date] = &orderStatUnit{}
  161. }
  162. m[date].orderNum += v.GoodsNum
  163. m[date].totalCost += v.TotalCost
  164. m[date].totalPrice += v.TotalPrice
  165. }
  166. dates, orderNums, totalCost, totalPrice := make([]string, 0), make([]int64, 0), make([]int64, 0), make([]int64, 0)
  167. for start.Before(end) {
  168. next := timeType.Next()(start)
  169. dates = append(dates, timeType.RangeDesc(start, next))
  170. if v, ok := m[start]; ok {
  171. orderNums = append(orderNums, v.orderNum)
  172. totalCost = append(totalCost, v.totalCost)
  173. totalPrice = append(totalPrice, v.totalPrice)
  174. } else {
  175. orderNums = append(orderNums, 0)
  176. totalCost = append(totalCost, 0)
  177. totalPrice = append(totalPrice, 0)
  178. }
  179. start = next
  180. }
  181. // result
  182. data := map[string]interface{}{
  183. "xAxis": dates,
  184. "order_num": orderNums,
  185. "total_cost": totalCost,
  186. "total_income": totalPrice,
  187. }
  188. return data
  189. }
  190. // OrderExport .
  191. func (s *Service) OrderExport(c context.Context, arg *model.OrderQueryArg, from, limit int) (res []byte, err error) {
  192. _, orders, err := s.OrderList(c, arg, from, limit)
  193. if err != nil {
  194. return
  195. }
  196. records := make([][]string, 0, len(orders)+1)
  197. records = append(records, model.OrderExportFields())
  198. for _, v := range orders {
  199. records = append(records, v.ExportStrings())
  200. }
  201. if res, err = FormatCSV(records); err != nil {
  202. log.Error("FormatCSV error(%v)", err)
  203. }
  204. return
  205. }
  206. // OrderList .
  207. func (s *Service) OrderList(c context.Context, arg *model.OrderQueryArg, from, limit int) (total int64, list []*model.OrderInfo, err error) {
  208. list = make([]*model.OrderInfo, 0)
  209. if pass := preprocess(c, arg); !pass {
  210. return
  211. }
  212. where := orderQueryStr(arg)
  213. if total, err = s.tradeDao().OrderCount(c, where); err != nil || total == 0 {
  214. return
  215. }
  216. if list, err = s.tradeDao().OrderList(c, where, from, limit); err != nil || len(list) == 0 {
  217. return
  218. }
  219. // fetch names
  220. mids := make([]int64, 0)
  221. for _, v := range list {
  222. mids = append(mids, v.MID)
  223. }
  224. m, err := resource.NamesByMIDs(c, mids)
  225. if err != nil {
  226. return
  227. }
  228. // generate & merge
  229. for _, v := range list {
  230. v.GenDerived().GenDesc()
  231. if name, ok := m[v.MID]; ok {
  232. v.Nickname = name
  233. }
  234. }
  235. return
  236. }
  237. func preprocess(c context.Context, arg *model.OrderQueryArg) bool {
  238. if arg.Nickname != "" {
  239. mid, err := resource.MidByNickname(c, arg.Nickname)
  240. if err != nil || mid == 0 {
  241. return false
  242. }
  243. log.Info("resource.MidByNickname name(%s) mid(%d)", arg.Nickname, arg.MID)
  244. if arg.MID == 0 {
  245. arg.MID = mid
  246. }
  247. if arg.MID != mid {
  248. log.Error("illegal mid(%d) and nickname(%s) pair", arg.MID, arg.Nickname)
  249. return false
  250. }
  251. }
  252. arg.StartTime = arg.TimeType.RangeStart(time.Unix(arg.FromTime, 0))
  253. arg.EndTime = arg.TimeType.RangeEnd(time.Unix(arg.ToTime, 0))
  254. return true
  255. }
  256. func orderQueryStr(arg *model.OrderQueryArg) string {
  257. var where []string
  258. where = append(where, "is_deleted=0")
  259. {
  260. // 左开右闭
  261. where = append(where, fmt.Sprintf("order_time >= '%s'", arg.StartTime.Format(_dateLayout)))
  262. where = append(where, fmt.Sprintf("order_time < '%s'", arg.EndTime.Format(_dateLayout)))
  263. }
  264. if arg.GoodsType > 0 {
  265. where = append(where, fmt.Sprintf("goods_type=%d", arg.GoodsType))
  266. }
  267. if arg.GoodsID != "" {
  268. where = append(where, fmt.Sprintf("goods_id='%s'", arg.GoodsID))
  269. }
  270. if arg.GoodsName != "" {
  271. where = append(where, fmt.Sprintf("goods_name='%s'", arg.GoodsName))
  272. }
  273. if arg.OrderNO != "" {
  274. where = append(where, fmt.Sprintf("order_no='%s'", arg.OrderNO))
  275. }
  276. if arg.MID > 0 {
  277. where = append(where, fmt.Sprintf("mid=%d", arg.MID))
  278. }
  279. return strings.Join(where, " AND ")
  280. }