merchant.active.config.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. package shanghu
  2. import (
  3. "duoduo/apis/shanghu/models"
  4. "duoduo/models/shanghu"
  5. "duoduo/tools"
  6. "duoduo/tools/app"
  7. "errors"
  8. "fmt"
  9. "github.com/gin-gonic/gin"
  10. "math/rand"
  11. "time"
  12. )
  13. // 一个活动对应一个抽奖,一个抽奖对应多个奖品
  14. // 一个活动对应多个拼团,一个拼团对应一个张主图,一个拼团对应多个商品(多个商品是为了核销)
  15. func ActiveConfigCreate(c *gin.Context) {
  16. var inData models.CreateActiveConfigRequest
  17. var sqlData shanghu.MerchantActiveConfig
  18. var drawProduct shanghu.MerchantActiveDrawProduct
  19. err := c.ShouldBindJSON(&inData)
  20. if err != nil {
  21. app.Error(c, 400, err, err.Error())
  22. return
  23. }
  24. if inData.DrawMode != 0 {
  25. if len(inData.DrawId) <= 0 {
  26. app.Error(c, 500, errors.New("奖品未配置"), "奖品未配置")
  27. return
  28. }
  29. //校验总概率 总概率相加不能大于100
  30. drawList, _, err := drawProduct.GetDrawProductListById(inData.DrawId)
  31. if err != nil {
  32. app.Error(c, 500, err, err.Error())
  33. return
  34. }
  35. sumOdds := 0
  36. for _, v := range drawList {
  37. sumOdds = sumOdds + v.DrawOdds
  38. }
  39. if sumOdds != 100 {
  40. app.Error(c, 500, errors.New("配置的总中奖率不满100%"), "配置的总中奖率不满100%")
  41. return
  42. }
  43. }
  44. if inData.GroupBuyMode == 0 && inData.DrawMode == 0 {
  45. app.Error(c, 500, errors.New("请选择抽奖与开团"), "请选择抽奖与开团")
  46. return
  47. }
  48. sqlData.ActivityEndTime, err = tools.TimeToInt64(inData.ActivityEndTime, "2006-01-02")
  49. if err != nil {
  50. app.Error(c, 400, err, err.Error())
  51. return
  52. }
  53. sqlData.ActivityStartTime, err = tools.TimeToInt64(inData.ActivityStartTime, "2006-01-02")
  54. if err != nil {
  55. app.Error(c, 400, err, err.Error())
  56. return
  57. }
  58. //必中校验
  59. //if inData.DrawOneBiZhong > 0 {
  60. // var checkBZ shanghu.MerchantActiveDrawProduct
  61. // checkBZ.ID = inData.DrawOneBiZhong
  62. // bZInfo, err := checkBZ.GetDrawProductById()
  63. // if err != nil {
  64. // app.Error(c, 500, err, err.Error())
  65. // return
  66. // }
  67. //
  68. // if !bZInfo.IsPrize { //
  69. // app.Error(c, 500, err, err.Error())
  70. // return
  71. // }
  72. //}
  73. sqlData.ActiveName = inData.ActiveName
  74. sqlData.MerchantOpenID = inData.MerchantOpenID
  75. sqlData.CreatedAt = time.Now()
  76. sqlData.UpdatedAt = time.Now()
  77. sqlData.ConfigMode = 1
  78. sqlData.DrawOneBiZhong = inData.DrawOneBiZhong
  79. sqlData.DrawMode = inData.DrawMode
  80. sqlData.GroupBuyMode = inData.GroupBuyMode
  81. sqlData.GroupBuyUrl = inData.GroupBuyUrl
  82. sqlData.GroupBuy = `{}`
  83. sqlData.DrawProduct = `{}`
  84. sqlData.BackgroundImage = inData.BackgroundImage
  85. configData, err := sqlData.Create()
  86. if err != nil {
  87. app.Error(c, 500, err, err.Error())
  88. return
  89. }
  90. for _, v := range inData.DrawId {
  91. var drawData shanghu.MerchantActiveDrawProduct
  92. drawData.ActiveConfigID = configData.ID
  93. err = drawData.UpdateConfigId(v)
  94. if err != nil {
  95. app.Error(c, 500, err, err.Error())
  96. return
  97. }
  98. }
  99. for _, v := range inData.GroupBuyId {
  100. var groupBuy shanghu.MerchantActiveGroupBuy
  101. groupBuy.ActiveConfigID = configData.ID
  102. err = groupBuy.UpdateConfigId(v)
  103. if err != nil {
  104. app.Error(c, 500, err, err.Error())
  105. return
  106. }
  107. }
  108. app.OK(c, nil, app.Success)
  109. }
  110. // 活动配置信息
  111. func ActiveConfigInfo(c *gin.Context) {
  112. var inData models.ActiveConfigRequest
  113. var sqlData shanghu.MerchantActiveConfig
  114. var outData models.ActiveConfigReply
  115. var drawInfoSql shanghu.MerchantActiveDrawProduct
  116. var groupBuy shanghu.MerchantActiveGroupBuy
  117. var merchant shanghu.Merchant
  118. err := c.ShouldBindJSON(&inData)
  119. if err != nil {
  120. app.Error(c, 400, err, err.Error())
  121. return
  122. }
  123. sqlData.MerchantOpenID = inData.MerchantOpenID
  124. sqlData.ID = inData.ActiveConfigId
  125. configInfo, err := sqlData.GetConfigInfoById()
  126. if err != nil {
  127. app.Error(c, 500, err, err.Error())
  128. return
  129. }
  130. outData.GroupBuyMode = configInfo.GroupBuyMode
  131. outData.DrawMode = configInfo.DrawMode
  132. outData.GroupBuyUrl = configInfo.GroupBuyUrl
  133. outData.DrawOneBiZhong = configInfo.DrawOneBiZhong
  134. outData.ActivityEndTime = tools.TimeToStr(configInfo.ActivityEndTime)
  135. outData.ActivityStartTime = tools.TimeToStr(configInfo.ActivityStartTime)
  136. outData.ActiveName = configInfo.ActiveName
  137. //中奖商品
  138. drawInfoSql.ActiveConfigID = configInfo.ID
  139. drawInfo, _, err := drawInfoSql.GetDrawProductList()
  140. if err != nil {
  141. app.Error(c, 500, err, err.Error())
  142. return
  143. }
  144. for _, v := range drawInfo {
  145. var drawProduct models.DrawProduct
  146. drawProduct.DrawProductName = v.DrawProductName
  147. drawProduct.DrawUrl = v.DrawUrl
  148. drawProduct.DrawOdds = v.DrawOdds
  149. drawProduct.IsPrize = v.IsPrize
  150. drawProduct.TotalStock = v.TotalStock
  151. drawProduct.Stock = v.Stock
  152. outData.DrawProduct = append(outData.DrawProduct, drawProduct)
  153. }
  154. // 查询拼团列表
  155. groupBuy.ActiveConfigID = configInfo.ID
  156. groupBuyInfoList, _, err := groupBuy.GetGroupBuyList()
  157. if err != nil {
  158. app.Error(c, 500, err, err.Error())
  159. return
  160. }
  161. for _, v := range groupBuyInfoList {
  162. var groupBuyInfo models.DrawGroupBuy
  163. var groupBuyProject shanghu.MerchantActiveGroupByProject
  164. groupBuyInfo.GroupBuyName = v.GroupBuyName
  165. groupBuyInfo.GroupBuyMode = v.GroupBuyMode
  166. groupBuyInfo.GroupBuyUrl = v.GroupBuyUrl
  167. groupBuyInfo.GroupBuyThreeNum = v.GroupBuyThreeNum
  168. groupBuyInfo.GroupBuyThreePrice = v.GroupBuyThreePrice
  169. groupBuyInfo.GroupBuyFourNum = v.GroupBuyFourNum
  170. groupBuyInfo.GroupBuyFourPrice = v.GroupBuyFourPrice
  171. groupBuyInfo.GroupBuyTwoNum = v.GroupBuyTwoNum
  172. groupBuyInfo.GroupBuyTwoPrice = v.GroupBuyTwoPrice
  173. groupBuyInfo.GroupBuyOneNum = v.GroupBuyOneNum
  174. groupBuyInfo.GroupBuyOnePrice = v.GroupBuyOnePrice
  175. groupBuyInfo.MerchantOpenID = v.MerchantOpenID
  176. groupBuyInfo.OriginalPrice = v.OriginalPrice
  177. groupBuyInfo.RebateRate = v.RebateRate
  178. groupBuyProject.GroupBuyID = v.ID
  179. projectList, _, err := groupBuyProject.GetGroupBuyProjectList()
  180. if err != nil {
  181. app.Error(c, 500, err, err.Error())
  182. return
  183. }
  184. for _, v := range projectList {
  185. var groupBuyProjectInfo models.GroupBuyProject
  186. groupBuyProjectInfo.ProjectName = v.ProjectName
  187. groupBuyProjectInfo.CancelNum = v.CancelNum
  188. groupBuyInfo.GroupBuyProject = append(groupBuyInfo.GroupBuyProject, groupBuyProjectInfo)
  189. }
  190. outData.DrawGroupBuy = append(outData.DrawGroupBuy, groupBuyInfo)
  191. }
  192. merchant.OpenId = inData.MerchantOpenID
  193. merchantInfo, err := merchant.GetMerchant()
  194. if err != nil {
  195. app.Error(c, 500, err, err.Error())
  196. return
  197. }
  198. outData.MerchantName = merchantInfo.MerchantName
  199. app.OK(c, outData, app.Success)
  200. }
  201. // 活动列表
  202. func ActiveConfigList(c *gin.Context) {
  203. var inData models.ActiveConfigListRequest
  204. var sqlData shanghu.MerchantActiveConfig
  205. var outData []models.ActiveConfigListReply
  206. err := c.ShouldBindJSON(&inData)
  207. if err != nil {
  208. app.Error(c, 400, err, err.Error())
  209. return
  210. }
  211. sqlData.MerchantOpenID = inData.OpenId
  212. var pageSize = 10
  213. var pageIndex = 1
  214. if inData.PageSize != 0 {
  215. pageSize = inData.PageSize
  216. }
  217. if inData.PageIndex != 0 {
  218. pageIndex = inData.PageIndex
  219. }
  220. activeConfigList, count, err := sqlData.GetActiveConfigList(pageSize, pageIndex)
  221. if err != nil {
  222. app.Error(c, 500, err, err.Error())
  223. return
  224. }
  225. for _, v := range activeConfigList {
  226. var activeConfig models.ActiveConfigListReply
  227. activeConfig.ID = v.ID
  228. activeConfig.ActivityStart = tools.TimeToStr(v.ActivityStartTime)
  229. activeConfig.ActivityEnd = tools.TimeToStr(v.ActivityEndTime)
  230. activeConfig.ActiveName = v.ActiveName
  231. activeConfig.CreatedAt = v.CreatedAt.Format(time.DateTime)
  232. outData = append(outData, activeConfig)
  233. }
  234. app.PageOK(c, outData, count, pageIndex, pageSize, app.Success)
  235. }
  236. // 抽奖
  237. func Draw(c *gin.Context) {
  238. var inData models.DrawRequest
  239. var sqlData shanghu.MerchantActiveConfig
  240. var drawNumSql shanghu.ClientActiveDrawNum
  241. var draw shanghu.ClientActiveDrawLog
  242. var outData models.DrawReply
  243. err := c.ShouldBindJSON(&inData)
  244. if err != nil {
  245. app.Error(c, 400, err, err.Error())
  246. return
  247. }
  248. // 校验抽奖次数
  249. drawNumSql.ActiveConfigID = inData.ActiveConfigID
  250. drawNumSql.ClientOpenID = inData.ClientOpenID
  251. drawNum, err := drawNumSql.GetDrawNum()
  252. if err != nil {
  253. app.Error(c, 500, err, err.Error())
  254. return
  255. }
  256. //校验数量
  257. draw.ActiveConfigID = inData.ActiveConfigID
  258. draw.ClientOpenID = inData.ClientOpenID
  259. clientDrawNum, err := draw.GetClientActiveDrawLogNum()
  260. if err != nil {
  261. app.Error(c, 500, err, err.Error())
  262. return
  263. }
  264. if drawNum+1 < clientDrawNum {
  265. app.Error(c, 500, errors.New("抽奖次数用完"), "抽奖次数用完")
  266. return
  267. }
  268. // 先校验是否有必中抽奖
  269. sqlData.ID = inData.ActiveConfigID
  270. config, err := sqlData.GetConfigInfoById()
  271. if err != nil {
  272. app.Error(c, 500, err, err.Error())
  273. return
  274. }
  275. if config.DrawOneBiZhong > 0 {
  276. // 必中抽奖需要产看中奖记录是否中过,没有中过直接中,中过了走正常的中奖概率
  277. var drawConfig shanghu.ClientActiveDrawLog
  278. drawConfig.ActiveConfigID = inData.ActiveConfigID
  279. drawConfig.ClientOpenID = inData.ClientOpenID
  280. drawConfig.DrawProductID = config.DrawOneBiZhong
  281. count, err := drawConfig.GetClientActiveDrawLogByBiZHong()
  282. if err != nil {
  283. app.Error(c, 500, err, err.Error())
  284. return
  285. }
  286. // 为0直接中奖 写中奖记录并且返回 查询中奖
  287. if count == 0 {
  288. //var draw shanghu.ClientActiveDrawLog
  289. var drawProduct shanghu.MerchantActiveDrawProduct
  290. drawProduct.ID = config.DrawOneBiZhong
  291. drawProductInfo, err := drawProduct.GetDrawProductById()
  292. if err != nil {
  293. app.Error(c, 500, err, err.Error())
  294. return
  295. }
  296. if drawProductInfo.ActiveConfigID != inData.ActiveConfigID {
  297. app.Error(c, 500, errors.New("必中配置活动不一致"), "必中配置活动不一致")
  298. return
  299. }
  300. if drawProductInfo.Stock <= 0 {
  301. app.Error(c, 500, errors.New("库存不足"), "库存不足")
  302. return
  303. }
  304. fmt.Println(drawProductInfo)
  305. //新建log 减库存
  306. err = subStockAddLog(drawProductInfo, inData.ClientOpenID)
  307. if err != nil {
  308. app.Error(c, 500, err, err.Error())
  309. return
  310. }
  311. //draw.DrawProductID = config.DrawOneBiZhong
  312. //draw.ActiveConfigID = inData.ActiveConfigID
  313. //draw.ClientOpenID = inData.ClientOpenID
  314. //
  315. //if drawProductInfo.IsPrize {
  316. // draw.IsPrize = models.NotClaimed //未兑奖 必中一定是要能兑奖的。
  317. //} else {
  318. // draw.IsPrize = models.NotWon //未中奖
  319. //}
  320. //
  321. //draw.CreatedAt = time.Now()
  322. //draw.UpdatedAt = time.Now()
  323. //_, err = draw.Create() //创建完成
  324. //if err != nil {
  325. // app.Error(c, 500, err, err.Error())
  326. // return
  327. //}
  328. outData.DrawProductName = drawProductInfo.DrawProductName
  329. outData.DrawUrl = drawProductInfo.DrawUrl
  330. outData.ID = config.DrawOneBiZhong
  331. app.OK(c, outData, app.Success)
  332. return
  333. }
  334. }
  335. var drawProductList shanghu.MerchantActiveDrawProduct
  336. var prizeList []Prize
  337. drawProductList.ActiveConfigID = inData.ActiveConfigID
  338. drawProductInfo, _, err := drawProductList.GetDrawProductListByActiveId()
  339. if err != nil {
  340. app.Error(c, 500, err, err.Error())
  341. return
  342. }
  343. for _, v := range drawProductInfo {
  344. var prize Prize
  345. if v.Stock <= 0 {
  346. continue
  347. }
  348. prize.Name = v.DrawProductName
  349. prize.ID = v.ID
  350. prize.Weight = v.DrawOdds
  351. prize.InitStock = v.TotalStock
  352. prize.Stock = v.Stock
  353. prizeList = append(prizeList, prize)
  354. }
  355. //fmt.Println(prizeList)
  356. // 创建抽奖实例
  357. lottery := NewLottery(prizeList)
  358. // 执行1次抽奖
  359. results, err := lottery.Draw(1)
  360. if err != nil {
  361. app.Error(c, 500, err, err.Error())
  362. return
  363. }
  364. fmt.Println(results)
  365. var drawProduct shanghu.MerchantActiveDrawProduct
  366. for _, v := range drawProductInfo {
  367. if results[0].ID == v.ID {
  368. outData.ID = v.ID
  369. outData.DrawUrl = v.DrawUrl
  370. outData.DrawProductName = v.DrawProductName
  371. drawProduct = v
  372. break
  373. }
  374. }
  375. //库存减一 并且新增记录
  376. err = subStockAddLog(drawProduct, inData.ClientOpenID)
  377. if err != nil {
  378. app.Error(c, 500, err, err.Error())
  379. return
  380. }
  381. app.OK(c, outData, app.Success)
  382. }
  383. type Prize struct {
  384. ID int64 // 奖品ID
  385. Name string // 奖品名称
  386. Weight int // 权重
  387. Stock int // 库存数量
  388. InitStock int // 初始库存(用于展示)
  389. }
  390. // 带权重和库存的抽奖
  391. type Lottery struct {
  392. Prizes []Prize
  393. TotalWeight int
  394. }
  395. func NewLottery(prizes []Prize) *Lottery {
  396. totalWeight := 0
  397. for _, prize := range prizes {
  398. totalWeight += prize.Weight
  399. }
  400. // 深拷贝奖品列表并初始化初始库存
  401. prizeCopy := make([]Prize, len(prizes))
  402. for i, p := range prizes {
  403. prizeCopy[i] = p
  404. prizeCopy[i].InitStock = p.Stock
  405. }
  406. return &Lottery{
  407. Prizes: prizeCopy,
  408. TotalWeight: totalWeight,
  409. }
  410. }
  411. func (l *Lottery) Draw(times int) ([]Prize, error) {
  412. // 检查总库存是否足够
  413. totalStock := 0
  414. for _, prize := range l.Prizes {
  415. totalStock += prize.Stock
  416. }
  417. if totalStock < times {
  418. return nil, fmt.Errorf("剩余总库存(%d)不足以支持%d次抽奖", totalStock, times)
  419. }
  420. rand.Seed(time.Now().UnixNano())
  421. results := make([]Prize, 0, times)
  422. for i := 0; i < times; i++ {
  423. // 如果没有可用奖品了就停止
  424. if l.TotalWeight <= 0 {
  425. break
  426. }
  427. randNum := rand.Intn(l.TotalWeight)
  428. currentWeight := 0
  429. for j := range l.Prizes {
  430. if l.Prizes[j].Stock <= 0 {
  431. continue
  432. }
  433. currentWeight += l.Prizes[j].Weight
  434. if randNum < currentWeight {
  435. // 中奖
  436. results = append(results, l.Prizes[j])
  437. // 减少库存
  438. l.Prizes[j].Stock--
  439. // 如果库存耗尽,更新总权重
  440. if l.Prizes[j].Stock == 0 {
  441. l.TotalWeight -= l.Prizes[j].Weight
  442. }
  443. break
  444. }
  445. }
  446. }
  447. return results, nil
  448. }
  449. // 新建记录,减库存
  450. func subStockAddLog(drawProductInfo shanghu.MerchantActiveDrawProduct, clientOpenId string) error {
  451. var draw shanghu.ClientActiveDrawLog
  452. //var drawProduct shanghu.MerchantActiveDrawProduct
  453. if drawProductInfo.Stock <= 0 {
  454. return errors.New("库存不足")
  455. }
  456. //减库存
  457. err := drawProductInfo.SubStock()
  458. if err != nil {
  459. return err
  460. }
  461. // 新建记录
  462. draw.DrawProductID = drawProductInfo.ID
  463. draw.ActiveConfigID = drawProductInfo.ActiveConfigID
  464. draw.ClientOpenID = clientOpenId
  465. if drawProductInfo.IsPrize {
  466. draw.IsPrize = models.NotClaimed //未兑奖 必中一定是要能兑奖的。
  467. } else {
  468. draw.IsPrize = models.NotWon //未中奖
  469. }
  470. draw.CreatedAt = time.Now()
  471. draw.UpdatedAt = time.Now()
  472. _, err = draw.Create() //创建完成
  473. if err != nil {
  474. return err
  475. }
  476. return nil
  477. }