wallet.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. package daytask
  2. import (
  3. "app/commons/model/entity"
  4. "app/commons/services"
  5. "encoding/json"
  6. "fmt"
  7. "github.com/gin-gonic/gin"
  8. "time"
  9. )
  10. // WalletInfo 钱包信息
  11. func (s *Server) WalletInfo(c *gin.Context) {
  12. ctx := s.FromContext(c)
  13. db := s.DB()
  14. userId := ctx.UserId()
  15. user := &entity.DtUser{}
  16. if err := db.Where("id = ?", userId).First(user).Error; err != nil {
  17. ctx.Fail("user_not_found")
  18. return
  19. }
  20. ctx.OK(gin.H{
  21. "balance": user.Balance,
  22. "frozenBalance": user.FrozenBalance,
  23. "totalRecharge": user.TotalRecharge,
  24. "totalWithdraw": user.TotalWithdraw,
  25. "totalTaskIncome": user.TotalTaskIncome,
  26. "totalInviteIncome": user.TotalInviteIncome,
  27. })
  28. }
  29. // BalanceLog 余额流水
  30. func (s *Server) BalanceLog(c *gin.Context) {
  31. ctx := s.FromContext(c)
  32. db := s.DB()
  33. userId := ctx.UserId()
  34. logType := ctx.QueryInt64("type", 0) // 0表示全部
  35. paging := &services.Pagination{
  36. Current: ctx.QueryInt64("current", 1),
  37. Size: ctx.QueryInt64("size", 20),
  38. }
  39. query := db.Model(&entity.DtBalanceLog{}).Where("user_id = ?", userId)
  40. if logType > 0 {
  41. query = query.Where("type = ?", logType)
  42. }
  43. query.Count(&paging.Total)
  44. paging.Computer()
  45. logs := make([]*entity.DtBalanceLog, 0)
  46. query.Order("created_at DESC").
  47. Offset(int(paging.Start)).
  48. Limit(int(paging.Size)).
  49. Find(&logs)
  50. ctx.OK(gin.H{
  51. "list": logs,
  52. "paging": paging,
  53. })
  54. }
  55. // WalletWithdraw 申请提现
  56. func (s *Server) WalletWithdraw(c *gin.Context) {
  57. ctx := s.FromContext(c)
  58. db := s.DB()
  59. userId := ctx.UserId()
  60. type WithdrawRequest struct {
  61. Amount float64 `json:"amount" binding:"required"`
  62. PaymentId int64 `json:"paymentId" binding:"required"`
  63. }
  64. var req WithdrawRequest
  65. if err := c.ShouldBindJSON(&req); err != nil {
  66. ctx.Fail("invalid_params")
  67. return
  68. }
  69. // 获取用户
  70. user := &entity.DtUser{}
  71. if err := db.Where("id = ?", userId).First(user).Error; err != nil {
  72. ctx.Fail("user_not_found")
  73. return
  74. }
  75. // 检查余额
  76. if user.Balance < req.Amount {
  77. ctx.Fail("balance_not_enough")
  78. return
  79. }
  80. // 获取最低提现金额配置
  81. minWithdraw := 100.0 // 默认100
  82. config := &entity.DtConfig{}
  83. if err := db.Where("`key` = ?", "min_withdraw").First(config).Error; err == nil {
  84. fmt.Sscanf(config.Value, "%f", &minWithdraw)
  85. }
  86. if req.Amount < minWithdraw {
  87. ctx.Fail("amount_too_small")
  88. return
  89. }
  90. // 检查金额是否是10的倍数
  91. if int(req.Amount)%10 != 0 {
  92. ctx.Fail("amount_must_be_multiple_of_10")
  93. return
  94. }
  95. // 获取收款账户
  96. payment := &entity.DtUserPayment{}
  97. if err := db.Where("id = ? AND user_id = ?", req.PaymentId, userId).First(payment).Error; err != nil {
  98. ctx.Fail("payment_not_found")
  99. return
  100. }
  101. // 获取手续费率
  102. feeRate := 0.02 // 默认2%
  103. feeConfig := &entity.DtConfig{}
  104. if err := db.Where("`key` = ?", "withdraw_fee").First(feeConfig).Error; err == nil {
  105. fmt.Sscanf(feeConfig.Value, "%f", &feeRate)
  106. }
  107. fee := req.Amount * feeRate
  108. actualAmount := req.Amount - fee
  109. // 生成订单号
  110. orderNo := fmt.Sprintf("W%d%d", time.Now().UnixNano(), userId)
  111. // 序列化收款账户信息
  112. paymentInfo, _ := json.Marshal(payment)
  113. // 开始事务
  114. tx := db.Begin()
  115. // 扣除余额
  116. if err := tx.Model(&entity.DtUser{}).
  117. Where("id = ? AND balance >= ?", userId, req.Amount).
  118. Update("balance", db.Raw("balance - ?", req.Amount)).Error; err != nil {
  119. tx.Rollback()
  120. ctx.Fail("deduct_balance_failed")
  121. return
  122. }
  123. // 创建提现订单
  124. order := &entity.DtWithdrawOrder{
  125. OrderNo: orderNo,
  126. UserId: userId,
  127. Amount: req.Amount,
  128. Fee: fee,
  129. ActualAmount: actualAmount,
  130. PaymentType: payment.Type,
  131. PaymentId: payment.Id,
  132. PaymentInfo: string(paymentInfo),
  133. Status: entity.WithdrawStatusPending,
  134. }
  135. if err := tx.Create(order).Error; err != nil {
  136. tx.Rollback()
  137. ctx.Fail("create_order_failed")
  138. return
  139. }
  140. // 记录余额变动
  141. balanceLog := &entity.DtBalanceLog{
  142. UserId: userId,
  143. Type: entity.BalanceLogTypeWithdraw,
  144. Amount: -req.Amount,
  145. BeforeBalance: user.Balance,
  146. AfterBalance: user.Balance - req.Amount,
  147. RelatedId: order.Id,
  148. Remark: fmt.Sprintf("提现申请 %s", orderNo),
  149. }
  150. tx.Create(balanceLog)
  151. tx.Commit()
  152. ctx.OK(order)
  153. }
  154. // WithdrawConfig 提现配置
  155. func (s *Server) WithdrawConfig(c *gin.Context) {
  156. ctx := s.FromContext(c)
  157. db := s.DB()
  158. // 获取最低提现金额
  159. minWithdraw := 100.0
  160. minConfig := &entity.DtConfig{}
  161. if err := db.Where("`key` = ?", "min_withdraw").First(minConfig).Error; err == nil {
  162. fmt.Sscanf(minConfig.Value, "%f", &minWithdraw)
  163. }
  164. // 获取手续费率
  165. feeRate := 0.02
  166. feeConfig := &entity.DtConfig{}
  167. if err := db.Where("`key` = ?", "withdraw_fee").First(feeConfig).Error; err == nil {
  168. fmt.Sscanf(feeConfig.Value, "%f", &feeRate)
  169. }
  170. // 获取最大提现金额
  171. maxWithdraw := 50000.0
  172. maxConfig := &entity.DtConfig{}
  173. if err := db.Where("`key` = ?", "max_withdraw").First(maxConfig).Error; err == nil {
  174. fmt.Sscanf(maxConfig.Value, "%f", &maxWithdraw)
  175. }
  176. ctx.OK(gin.H{
  177. "minAmount": minWithdraw,
  178. "maxAmount": maxWithdraw,
  179. "feeRate": feeRate,
  180. "multiple": 10, // 提现金额必须是10的倍数
  181. })
  182. }
  183. // WithdrawLog 提现记录
  184. func (s *Server) WithdrawLog(c *gin.Context) {
  185. ctx := s.FromContext(c)
  186. db := s.DB()
  187. userId := ctx.UserId()
  188. status := ctx.QueryInt64("status", -99) // -99表示全部
  189. paging := &services.Pagination{
  190. Current: ctx.QueryInt64("current", 1),
  191. Size: ctx.QueryInt64("size", 20),
  192. }
  193. query := db.Model(&entity.DtWithdrawOrder{}).Where("user_id = ?", userId)
  194. if status != -99 {
  195. query = query.Where("status = ?", status)
  196. }
  197. query.Count(&paging.Total)
  198. paging.Computer()
  199. orders := make([]*entity.DtWithdrawOrder, 0)
  200. query.Order("created_at DESC").
  201. Offset(int(paging.Start)).
  202. Limit(int(paging.Size)).
  203. Find(&orders)
  204. ctx.OK(gin.H{
  205. "list": orders,
  206. "paging": paging,
  207. })
  208. }