merchant.active.config.go 14 KB

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