merchant.active.config.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  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 outData models.DrawReply
  232. err := c.ShouldBindJSON(&inData)
  233. if err != nil {
  234. app.Error(c, 400, err, err.Error())
  235. return
  236. }
  237. // 先校验是否有必中抽奖
  238. sqlData.ID = inData.ActiveConfigID
  239. config, err := sqlData.GetConfigInfoById()
  240. if err != nil {
  241. app.Error(c, 500, err, err.Error())
  242. return
  243. }
  244. if config.DrawOneBiZhong > 0 {
  245. // 必中抽奖需要产看中奖记录是否中过,没有中过直接中,中过了走正常的中奖概率
  246. var drawConfig shanghu.ClientActiveDrawLog
  247. drawConfig.ActiveConfigID = inData.ActiveConfigID
  248. drawConfig.ClientOpenID = inData.ClientOpenID
  249. drawConfig.DrawProductID = config.DrawOneBiZhong
  250. count, err := drawConfig.GetClientActiveDrawLogByBiZHong()
  251. if err != nil {
  252. app.Error(c, 500, err, err.Error())
  253. return
  254. }
  255. // 为0直接中奖 写中奖记录并且返回 查询中奖
  256. if count == 0 {
  257. //var draw shanghu.ClientActiveDrawLog
  258. var drawProduct shanghu.MerchantActiveDrawProduct
  259. drawProduct.ID = config.DrawOneBiZhong
  260. drawProductInfo, err := drawProduct.GetDrawProductById()
  261. if err != nil {
  262. app.Error(c, 500, err, err.Error())
  263. return
  264. }
  265. if drawProductInfo.ActiveConfigID != inData.ActiveConfigID {
  266. app.Error(c, 500, errors.New("必中配置活动不一致"), "必中配置活动不一致")
  267. return
  268. }
  269. if drawProductInfo.Stock <= 0 {
  270. app.Error(c, 500, errors.New("库存不足"), "库存不足")
  271. return
  272. }
  273. fmt.Println(drawProductInfo)
  274. //新建log 减库存
  275. err = subStockAddLog(drawProductInfo, inData.ClientOpenID)
  276. if err != nil {
  277. app.Error(c, 500, err, err.Error())
  278. return
  279. }
  280. //draw.DrawProductID = config.DrawOneBiZhong
  281. //draw.ActiveConfigID = inData.ActiveConfigID
  282. //draw.ClientOpenID = inData.ClientOpenID
  283. //
  284. //if drawProductInfo.IsPrize {
  285. // draw.IsPrize = models.NotClaimed //未兑奖 必中一定是要能兑奖的。
  286. //} else {
  287. // draw.IsPrize = models.NotWon //未中奖
  288. //}
  289. //
  290. //draw.CreatedAt = time.Now()
  291. //draw.UpdatedAt = time.Now()
  292. //_, err = draw.Create() //创建完成
  293. //if err != nil {
  294. // app.Error(c, 500, err, err.Error())
  295. // return
  296. //}
  297. outData.DrawProductName = drawProductInfo.DrawProductName
  298. outData.DrawUrl = drawProductInfo.DrawUrl
  299. outData.ID = config.DrawOneBiZhong
  300. app.OK(c, outData, app.Success)
  301. return
  302. }
  303. }
  304. var drawProductList shanghu.MerchantActiveDrawProduct
  305. var prizeList []Prize
  306. drawProductList.ActiveConfigID = inData.ActiveConfigID
  307. drawProductInfo, _, err := drawProductList.GetDrawProductListByActiveId()
  308. if err != nil {
  309. app.Error(c, 500, err, err.Error())
  310. return
  311. }
  312. for _, v := range drawProductInfo {
  313. var prize Prize
  314. if v.Stock <= 0 {
  315. continue
  316. }
  317. prize.Name = v.DrawProductName
  318. prize.ID = v.ID
  319. prize.Weight = v.DrawOdds
  320. prize.InitStock = v.TotalStock
  321. prize.Stock = v.Stock
  322. prizeList = append(prizeList, prize)
  323. }
  324. //fmt.Println(prizeList)
  325. // 创建抽奖实例
  326. lottery := NewLottery(prizeList)
  327. // 执行1次抽奖
  328. results, err := lottery.Draw(1)
  329. if err != nil {
  330. app.Error(c, 500, err, err.Error())
  331. return
  332. }
  333. fmt.Println(results)
  334. var drawProduct shanghu.MerchantActiveDrawProduct
  335. for _, v := range drawProductInfo {
  336. if results[0].ID == v.ID {
  337. outData.ID = v.ID
  338. outData.DrawUrl = v.DrawUrl
  339. outData.DrawProductName = v.DrawProductName
  340. drawProduct = v
  341. break
  342. }
  343. }
  344. //库存减一 并且新增记录
  345. err = subStockAddLog(drawProduct, inData.ClientOpenID)
  346. if err != nil {
  347. app.Error(c, 500, err, err.Error())
  348. return
  349. }
  350. app.OK(c, outData, app.Success)
  351. }
  352. type Prize struct {
  353. ID int64 // 奖品ID
  354. Name string // 奖品名称
  355. Weight int // 权重
  356. Stock int // 库存数量
  357. InitStock int // 初始库存(用于展示)
  358. }
  359. // 带权重和库存的抽奖
  360. type Lottery struct {
  361. Prizes []Prize
  362. TotalWeight int
  363. }
  364. func NewLottery(prizes []Prize) *Lottery {
  365. totalWeight := 0
  366. for _, prize := range prizes {
  367. totalWeight += prize.Weight
  368. }
  369. // 深拷贝奖品列表并初始化初始库存
  370. prizeCopy := make([]Prize, len(prizes))
  371. for i, p := range prizes {
  372. prizeCopy[i] = p
  373. prizeCopy[i].InitStock = p.Stock
  374. }
  375. return &Lottery{
  376. Prizes: prizeCopy,
  377. TotalWeight: totalWeight,
  378. }
  379. }
  380. func (l *Lottery) Draw(times int) ([]Prize, error) {
  381. // 检查总库存是否足够
  382. totalStock := 0
  383. for _, prize := range l.Prizes {
  384. totalStock += prize.Stock
  385. }
  386. if totalStock < times {
  387. return nil, fmt.Errorf("剩余总库存(%d)不足以支持%d次抽奖", totalStock, times)
  388. }
  389. rand.Seed(time.Now().UnixNano())
  390. results := make([]Prize, 0, times)
  391. for i := 0; i < times; i++ {
  392. // 如果没有可用奖品了就停止
  393. if l.TotalWeight <= 0 {
  394. break
  395. }
  396. randNum := rand.Intn(l.TotalWeight)
  397. currentWeight := 0
  398. for j := range l.Prizes {
  399. if l.Prizes[j].Stock <= 0 {
  400. continue
  401. }
  402. currentWeight += l.Prizes[j].Weight
  403. if randNum < currentWeight {
  404. // 中奖
  405. results = append(results, l.Prizes[j])
  406. // 减少库存
  407. l.Prizes[j].Stock--
  408. // 如果库存耗尽,更新总权重
  409. if l.Prizes[j].Stock == 0 {
  410. l.TotalWeight -= l.Prizes[j].Weight
  411. }
  412. break
  413. }
  414. }
  415. }
  416. return results, nil
  417. }
  418. // 新建记录,减库存
  419. func subStockAddLog(drawProductInfo shanghu.MerchantActiveDrawProduct, clientOpenId string) error {
  420. var draw shanghu.ClientActiveDrawLog
  421. //var drawProduct shanghu.MerchantActiveDrawProduct
  422. if drawProductInfo.Stock <= 0 {
  423. return errors.New("库存不足")
  424. }
  425. //减库存
  426. err := drawProductInfo.SubStock()
  427. if err != nil {
  428. return err
  429. }
  430. // 新建记录
  431. draw.DrawProductID = drawProductInfo.ID
  432. draw.ActiveConfigID = drawProductInfo.ActiveConfigID
  433. draw.ClientOpenID = clientOpenId
  434. if drawProductInfo.IsPrize {
  435. draw.IsPrize = models.NotClaimed //未兑奖 必中一定是要能兑奖的。
  436. } else {
  437. draw.IsPrize = models.NotWon //未中奖
  438. }
  439. draw.CreatedAt = time.Now()
  440. draw.UpdatedAt = time.Now()
  441. _, err = draw.Create() //创建完成
  442. if err != nil {
  443. return err
  444. }
  445. return nil
  446. }