business.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. package dao
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/md5"
  6. "encoding/hex"
  7. "encoding/json"
  8. "fmt"
  9. "io"
  10. "net/http"
  11. "net/url"
  12. "sort"
  13. "strconv"
  14. "strings"
  15. xtime "time"
  16. "go-common/app/job/main/vip/model"
  17. "go-common/library/ecode"
  18. "go-common/library/log"
  19. "go-common/library/time"
  20. "go-common/library/xstr"
  21. "github.com/google/uuid"
  22. "github.com/pkg/errors"
  23. )
  24. const (
  25. _cleanCache = "/notify/cleanCache"
  26. _payOrder = "/payplatform/pay/pay"
  27. _message = "/api/notify/send.user.notify.do"
  28. _addBcoin = "/api/coupon/regular/add"
  29. _sendVipbuyTicket = "/mall-marketing/coupon_code/create"
  30. _sendMedal = "/api/nameplate/get/v2"
  31. _pushData = "/x/internal/push-strategy/task/add"
  32. _retry = 3
  33. _minRead = 1024 * 64
  34. _alreadySend = -804
  35. _alreadyGet = -663
  36. _alreadySendVipbuy = 83110005
  37. _ok = 1
  38. //push
  39. appID = 1
  40. )
  41. //PushData http push data
  42. func (d *Dao) PushData(c context.Context, mids []int64, pushData *model.VipPushData, curtime string) (rel *model.VipPushResq, err error) {
  43. var (
  44. pushTime xtime.Time
  45. expireTime xtime.Time
  46. params = url.Values{}
  47. )
  48. rel = new(model.VipPushResq)
  49. if pushTime, err = xtime.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%v %v", curtime, pushData.PushStartTime), xtime.Local); err != nil {
  50. err = errors.WithStack(err)
  51. return
  52. }
  53. if expireTime, err = xtime.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%v %v", curtime, pushData.PushEndTime), xtime.Local); err != nil {
  54. err = errors.WithStack(err)
  55. return
  56. }
  57. page := len(mids) / d.c.Property.SplitPush
  58. if len(mids)%d.c.Property.SplitPush != 0 {
  59. page++
  60. }
  61. for i := 0; i < page; i++ {
  62. startID := i * d.c.Property.SplitPush
  63. endID := (i + 1) * d.c.Property.SplitPush
  64. if endID > len(mids) {
  65. endID = len(mids)
  66. }
  67. tempMids := mids[startID:endID]
  68. params.Set("app_id", fmt.Sprintf("%v", appID))
  69. params.Set("business_id", fmt.Sprintf("%v", d.c.Property.BusinessID))
  70. params.Set("alert_title", pushData.Title)
  71. params.Set("alert_body", pushData.Content)
  72. params.Set("mids", xstr.JoinInts(tempMids))
  73. params.Set("link_type", fmt.Sprintf("%v", pushData.LinkType))
  74. params.Set("link_value", pushData.LinkURL)
  75. params.Set("builds", pushData.Platform)
  76. params.Set("group", pushData.GroupName)
  77. params.Set("uuid", uuid.New().String())
  78. params.Set("push_time", fmt.Sprintf("%v", pushTime.Unix()))
  79. params.Set("expire_time", fmt.Sprintf("%v", expireTime.Unix()))
  80. header := make(map[string]string)
  81. header["Authorization"] = fmt.Sprintf("token=%v", d.c.Property.PushToken)
  82. header["Content-Type"] = "application/x-www-form-urlencoded"
  83. for i := 0; i < _retry; i++ {
  84. if err = d.doNomalSend(c, d.c.URLConf.APICoURL, _pushData, "127.0.0.1", header, params, rel); err != nil {
  85. log.Error("send error(%v) url(%v)", err, d.c.URLConf.APICoURL+_pushData)
  86. return
  87. }
  88. if rel.Code == int64(ecode.OK.Code()) {
  89. log.Info("send url:%v params:%+v return:%+v error(%+v)", d.c.URLConf.APICoURL+_pushData, params, rel, err)
  90. break
  91. }
  92. }
  93. }
  94. return
  95. }
  96. //SendMedal send medal
  97. func (d *Dao) SendMedal(c context.Context, mid, medalID int64) (status int64) {
  98. var (
  99. err error
  100. )
  101. params := url.Values{}
  102. params.Set("mid", fmt.Sprintf("%v", mid))
  103. params.Set("nid", fmt.Sprintf("%v", medalID))
  104. rel := new(struct {
  105. Code int64 `json:"code"`
  106. Data string `json:"data"`
  107. })
  108. defer func() {
  109. if err == nil {
  110. log.Info("send url:%+v params:%+v return:%+v", d.c.URLConf.AccountURL+_sendMedal, params, rel)
  111. }
  112. }()
  113. for i := 0; i < _retry; i++ {
  114. if err = d.client.Get(c, d.c.URLConf.AccountURL+_sendMedal, "127.0.0.1", params, rel); err != nil {
  115. log.Error("send error(%v) url(%v)", err, d.c.URLConf.AccountURL+_sendMedal)
  116. continue
  117. }
  118. if rel.Code == int64(ecode.OK.Code()) || rel.Code == _alreadyGet {
  119. status = 1
  120. if rel.Code == _alreadyGet {
  121. status = 2
  122. }
  123. return
  124. }
  125. }
  126. return
  127. }
  128. //SendVipBuyTicket send vipbuy ticket
  129. func (d *Dao) SendVipBuyTicket(c context.Context, mid int64, couponID string) (status int64) {
  130. var (
  131. err error
  132. )
  133. header := make(map[string]string)
  134. header["Content-Type"] = "application/json"
  135. params := make(map[string]string)
  136. params["mid"] = fmt.Sprintf("%v", mid)
  137. params["couponId"] = fmt.Sprintf("%v", couponID)
  138. for i := 0; i < _retry; i++ {
  139. repl := new(struct {
  140. Code int64 `json:"code"`
  141. Message string `json:"message"`
  142. })
  143. if err = d.doSend(c, d.c.URLConf.MallURL, _sendVipbuyTicket, "127.0.0.1", header, params, repl); err != nil {
  144. log.Error("send vip buy ticket(%+v) error(%+v)", params, err)
  145. continue
  146. }
  147. if repl.Code == int64(ecode.OK.Code()) || repl.Code == _alreadySendVipbuy {
  148. status = 1
  149. if repl.Code == _alreadySendVipbuy {
  150. status = 2
  151. }
  152. return
  153. }
  154. }
  155. return
  156. }
  157. //SendCleanCache clean cache
  158. func (d *Dao) SendCleanCache(c context.Context, hv *model.HandlerVip) (err error) {
  159. params := url.Values{}
  160. params.Set("mid", strconv.FormatInt(int64(hv.Mid), 10))
  161. if err = d.client.Get(c, d.c.VipURI+_cleanCache, "127.0.0.1", params, nil); err != nil {
  162. log.Error("SendCleanCache error(%v) url(%v)", err, d.c.VipURI+_cleanCache)
  163. return
  164. }
  165. return
  166. }
  167. //SendBcoin send bcoin http
  168. func (d *Dao) SendBcoin(c context.Context, mids []int64, money int32, dueTime time.Time, ip string) (err error) {
  169. if len(mids) <= 0 {
  170. return
  171. }
  172. var midStrs []string
  173. for _, v := range mids {
  174. midStrs = append(midStrs, fmt.Sprintf("%v", v))
  175. }
  176. params := url.Values{}
  177. params.Add("activity_id", fmt.Sprintf("%v", d.c.Property.ActivityID))
  178. params.Add("mids", strings.Join(midStrs, ","))
  179. params.Add("money", fmt.Sprintf("%v", money*100))
  180. params.Add("due_time", dueTime.Time().Format("2006-01-02"))
  181. res := new(struct {
  182. Code int64 `json:"code"`
  183. Message string `json:"message"`
  184. TS int64 `json:"ts"`
  185. Data struct {
  186. CouponMoney int64 `json:"coupon_money"`
  187. Status int8 `json:"status"`
  188. } `json:"data"`
  189. })
  190. if err = d.client.Post(c, d.c.URLConf.PayCoURL+_addBcoin, ip, params, res); err != nil {
  191. err = errors.WithStack(err)
  192. return
  193. }
  194. if res.Code == int64(_alreadySend) {
  195. return
  196. }
  197. if int(res.Code) != ecode.OK.Code() {
  198. err = fmt.Errorf("发放B币失败 message: %s mid: %s resp(%+v)", res.Message, strings.Join(midStrs, ","), res)
  199. return
  200. }
  201. if res.Data.Status != _ok {
  202. err = fmt.Errorf("发放B币失败 message: %s mid: %s resp(%+v)", res.Message, strings.Join(midStrs, ","), res)
  203. return
  204. }
  205. log.Info("发放B币成功 mids %v resp(%+v)", mids, res)
  206. return
  207. }
  208. //SendAppCleanCache notice app clean cache
  209. func (d *Dao) SendAppCleanCache(c context.Context, hv *model.HandlerVip, app *model.VipAppInfo) (err error) {
  210. params := url.Values{}
  211. params.Set("modifiedAttr", "updateVip")
  212. params.Set("mid", fmt.Sprintf("%v", hv.Mid))
  213. params.Set("status", fmt.Sprintf("%v", hv.Type))
  214. params.Set("buyMonths", fmt.Sprintf("%v", hv.Months))
  215. params.Set("days", fmt.Sprintf("%v", hv.Days))
  216. if err = d.client.Get(c, app.PurgeURL, "127.0.0.1", params, nil); err != nil {
  217. log.Error("SendAppCleanCache error(%v) url(%v) params(%v)", err, app.PurgeURL, params)
  218. return
  219. }
  220. return
  221. }
  222. //SendMultipMsg send multip msg
  223. func (d *Dao) SendMultipMsg(c context.Context, mids, content, title, mc string, dataType int) (err error) {
  224. params := url.Values{}
  225. params.Set("mc", mc)
  226. params.Set("title", title)
  227. params.Set("context", content)
  228. params.Set("data_type", strconv.FormatInt(int64(dataType), 10))
  229. params.Set("mid_list", mids)
  230. defer func() {
  231. log.Info("SendMultipMsg(%v) params(%+v) error(%+v)", d.c.URLConf.MsgURL+_message, params, err)
  232. }()
  233. if err = d.client.Post(c, d.c.URLConf.MsgURL+_message, "127.0.0.1", params, nil); err != nil {
  234. log.Error("SendMultipMsg params(%+v) error(%v)", err, params)
  235. return
  236. }
  237. log.Info("cur send mid(%+v)", mids)
  238. return
  239. }
  240. // PayOrder pay order.
  241. func (d *Dao) PayOrder(c context.Context, paramsMap map[string]interface{}) (err error) {
  242. params := make(map[string]string)
  243. for k, v := range paramsMap {
  244. params[k] = fmt.Sprintf("%v", v)
  245. }
  246. header := make(map[string]string)
  247. header["Content-Type"] = "application/json"
  248. success := false
  249. for i := 0; i < 3; i++ {
  250. repl := new(struct {
  251. ErrNo int64 `json:"errno"`
  252. Msg string `json:"msg"`
  253. Data interface{} `json:"data"`
  254. })
  255. if err = d.doPaySend(c, d.c.URLConf.PayURL, _payOrder, "127.0.0.1", header, params, repl); err != nil {
  256. continue
  257. }
  258. if repl.ErrNo == 0 {
  259. success = true
  260. break
  261. }
  262. }
  263. if !success {
  264. err = fmt.Errorf("下单失败")
  265. }
  266. return
  267. }
  268. func (d *Dao) sortParamsKey(v map[string]string) string {
  269. if v == nil {
  270. return ""
  271. }
  272. var buf bytes.Buffer
  273. keys := make([]string, 0, len(v))
  274. for k := range v {
  275. keys = append(keys, k)
  276. }
  277. sort.Strings(keys)
  278. for _, k := range keys {
  279. vs := v[k]
  280. prefix := k + "="
  281. if buf.Len() > 0 {
  282. buf.WriteByte('&')
  283. }
  284. buf.WriteString(prefix)
  285. buf.WriteString(vs)
  286. }
  287. return buf.String()
  288. }
  289. //PaySign pay sign
  290. func (d *Dao) PaySign(params map[string]string, token string) (sign string) {
  291. tmp := d.sortParamsKey(params)
  292. var b bytes.Buffer
  293. b.WriteString(tmp)
  294. b.WriteString(fmt.Sprintf("&token=%s", token))
  295. log.Info("sign params:%v ", b.String())
  296. mh := md5.Sum(b.Bytes())
  297. // query
  298. var qb bytes.Buffer
  299. qb.WriteString(tmp)
  300. qb.WriteString("&sign=")
  301. qb.WriteString(hex.EncodeToString(mh[:]))
  302. sign = hex.EncodeToString(mh[:])
  303. log.Info("sign params(%v) and sign(%v)", b.String(), sign)
  304. return
  305. }
  306. func (d *Dao) doNomalSend(c context.Context, basePath, path, ip string, header map[string]string, params url.Values, data interface{}) (err error) {
  307. var (
  308. req *http.Request
  309. client = new(http.Client)
  310. resp *http.Response
  311. bs []byte
  312. marshal string
  313. )
  314. url := basePath + path
  315. if req, err = d.client.NewRequest(http.MethodPost, url, ip, params); err != nil {
  316. err = errors.WithStack(err)
  317. return
  318. }
  319. for k, v := range header {
  320. req.Header.Add(k, v)
  321. }
  322. if resp, err = client.Do(req); err != nil {
  323. log.Error("call url:%v params:%v", basePath+path, string(marshal))
  324. err = errors.WithStack(err)
  325. return
  326. }
  327. defer resp.Body.Close()
  328. defer func() {
  329. log.Info("call url:%v params:(%v) result:(%+v) header:(%+v)", url, string(marshal), data, header)
  330. }()
  331. if resp.StatusCode >= http.StatusBadRequest {
  332. err = errors.Errorf("incorrect http status:%d host:%s, url:%s", resp.StatusCode, req.URL.Host, req.URL.String())
  333. return
  334. }
  335. if bs, err = readAll(resp.Body, _minRead); err != nil {
  336. err = errors.Wrapf(err, "host:%s, url:%s", req.URL.Host, req.URL.String())
  337. return
  338. }
  339. if err = json.Unmarshal(bs, data); err != nil {
  340. err = errors.WithStack(err)
  341. return
  342. }
  343. return
  344. }
  345. func (d *Dao) doSend(c context.Context, basePath, path, IP string, header map[string]string, params map[string]string, data interface{}) (err error) {
  346. var (
  347. req *http.Request
  348. client = new(http.Client)
  349. resp *http.Response
  350. bs []byte
  351. )
  352. url := basePath + path
  353. marshal, _ := json.Marshal(params)
  354. if req, err = http.NewRequest(http.MethodPost, url, strings.NewReader(string(marshal))); err != nil {
  355. err = errors.WithStack(err)
  356. return
  357. }
  358. for k, v := range header {
  359. req.Header.Add(k, v)
  360. }
  361. if resp, err = client.Do(req); err != nil {
  362. log.Error("call url:%v params:%v", basePath+path, string(marshal))
  363. err = errors.WithStack(err)
  364. return
  365. }
  366. defer resp.Body.Close()
  367. defer func() {
  368. log.Info("call url:%v params:(%v) result:(%+v) header:(%+v)", url, string(marshal), data, header)
  369. }()
  370. if resp.StatusCode >= http.StatusBadRequest {
  371. err = errors.Errorf("incorrect http status:%d host:%s, url:%s", resp.StatusCode, req.URL.Host, req.URL.String())
  372. return
  373. }
  374. if bs, err = readAll(resp.Body, _minRead); err != nil {
  375. err = errors.Wrapf(err, "host:%s, url:%s", req.URL.Host, req.URL.String())
  376. return
  377. }
  378. if err = json.Unmarshal(bs, data); err != nil {
  379. err = errors.WithStack(err)
  380. return
  381. }
  382. return
  383. }
  384. func (d *Dao) doPaySend(c context.Context, basePath, path, IP string, header map[string]string, params map[string]string, data interface{}) (err error) {
  385. sign := d.PaySign(params, d.c.PayConf.Token)
  386. params["sign"] = sign
  387. return d.doSend(c, basePath, path, IP, header, params, data)
  388. }
  389. func readAll(r io.Reader, capacity int64) (b []byte, err error) {
  390. buf := bytes.NewBuffer(make([]byte, 0, capacity))
  391. // If the buffer overflows, we will get bytes.ErrTooLarge.
  392. // Return that as an error. Any other panic remains.
  393. defer func() {
  394. e := recover()
  395. if e == nil {
  396. return
  397. }
  398. if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
  399. err = panicErr
  400. } else {
  401. panic(e)
  402. }
  403. }()
  404. _, err = buf.ReadFrom(r)
  405. return buf.Bytes(), err
  406. }
  407. // SalaryCoupon salary coupon.
  408. func (d *Dao) SalaryCoupon(c context.Context, mid int64, couponType int8, count int64, token string) (err error) {
  409. params := url.Values{}
  410. params.Set("mid", fmt.Sprintf("%d", mid))
  411. params.Set("type", fmt.Sprintf("%d", couponType))
  412. params.Set("count", fmt.Sprintf("%d", count))
  413. params.Set("batch_no", token)
  414. var resp struct {
  415. Code int64 `json:"code"`
  416. }
  417. if err = d.client.Post(c, d.c.Property.SalaryCouponURL, "", params, &resp); err != nil {
  418. log.Error("message url(%s) error(%v)", d.c.Property.SalaryCouponURL+"?"+params.Encode(), err)
  419. return
  420. }
  421. if resp.Code != 0 {
  422. err = fmt.Errorf("POST SalaryCoupon url resp(%v)", resp)
  423. return
  424. }
  425. return
  426. }