| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- package services
- import (
- "app/commons/constant"
- "app/commons/core"
- "app/commons/core/exchange"
- "app/commons/core/redisclient"
- "app/commons/model/entity"
- "app/commons/utils"
- "fmt"
- "github.com/shopspring/decimal"
- "gorm.io/gorm"
- )
- func (s *CommonService) BatchStakeUserCurrentOrder(db *gorm.DB) ([]*entity.StakeUserCurrentOrder, error) {
- return FindAllWithBatch[entity.StakeUserCurrentOrder](db)
- }
- func (s *CommonService) FirstStakeUserCurrentOrder(db *gorm.DB) (*entity.StakeUserCurrentOrder, error) {
- return GetOne[entity.StakeUserCurrentOrder](db)
- }
- func (s *CommonService) FirstStakeProduct(db *gorm.DB) (*entity.StakeProduct, error) {
- return GetOne[entity.StakeProduct](db)
- }
- // 创建/获取用户活期质押记录
- func (s *CommonService) CheckUserCurrentStakeUserOrder(txDb *gorm.DB, userId int64, userUid string) (*entity.StakeUserCurrentOrder, error) {
- order, err := s.FirstStakeUserCurrentOrder(txDb.Where("uid", userUid).Where("user_id", userId))
- if err == nil && order.Id != 0 {
- return order, nil
- }
- currentProduct, err := s.FirstStakeProduct(txDb.Where("pledge_mode", entity.StakeProductTypeCurrent))
- if err != nil {
- return nil, err
- }
- order = &entity.StakeUserCurrentOrder{
- UserId: userId,
- Uid: userUid,
- ProductId: currentProduct.Id,
- ProductName: currentProduct.Name,
- Symbol: currentProduct.Symbol,
- FirstPledgeDate: utils.TimeDate(),
- LastClaimPeriod: utils.NowPeriodNo(),
- }
- err = txDb.Create(&order).Error
- return order, err
- }
- // 获取用户产品纬度订单 -- 聚合订单
- // 资产修改 -- 必须加资产锁定
- // 使用场景 1 api质押 2 管理后台直接增加订单
- // 活期 -- 随进随出
- func (s *CommonService) CurrentStake(user *entity.User, product *entity.StakeProduct, quantity decimal.Decimal, adminId int64) error {
- // 获取分布式锁 存在用户购买商品 发放奖励等情况 可能会影响资产变化
- lockKey := fmt.Sprintf("%s:%d", constant.UserAssetLocker, user.Id)
- lock, err := redisclient.Lock(lockKey)
- if err != nil {
- return fmt.Errorf("failed to acquire lock: %v", err)
- }
- core.Log.Infof("用户ID:%s 资产锁获取", lockKey)
- defer func() {
- err = redisclient.UnlockSafe(lock)
- if err != nil {
- core.Log.Error(err)
- }
- core.Log.Infof("用户ID:%s 资产锁释放", lockKey)
- }()
- // 为用户质押
- symbolPrice, err := exchange.GetCurrentSymbolUsdPrice(constant.CoinSystemSymbol)
- if err != nil {
- core.Log.Error(err)
- return err
- }
- txDb := s.DB().Begin()
- bs := constant.BsById(constant.BsCurrentStake)
- // todo: 1 创建质押记录 magic_stake_user_ops_record -- 排队的逻辑
- ops := s.buildCurrentStakeOps(user, quantity, symbolPrice, bs, adminId)
- if err = txDb.Create(&ops).Error; err != nil {
- txDb.Rollback()
- return err
- }
- order, err := s.CheckUserCurrentStakeUserOrder(txDb, user.Id, user.Uid)
- if err != nil {
- txDb.Rollback()
- return err
- }
- // 未开启排队
- if !product.IsQueue {
- err = txDb.Model(&entity.StakeUserCurrentOpsRecord{}).
- Where("id", ops.Id).
- Updates(map[string]interface{}{
- "before_quantity": order.Quantity,
- "before_usd_amount": order.UsdAmount,
- "after_quantity": order.Quantity.Add(ops.Quantity),
- "after_usd_amount": order.UsdAmount.Add(ops.UsdValue),
- "queue_state": entity.StateQueueStateSuccess,
- }).Error
- if err != nil {
- txDb.Rollback()
- return err
- }
- err = txDb.Model(&entity.StakeUserCurrentOrder{}).
- Where("id", order.Id).
- Updates(map[string]interface{}{
- "cum_quantity": gorm.Expr("cum_quantity+?", ops.Quantity),
- "quantity": gorm.Expr("quantity+?", ops.Quantity),
- "cum_usd_amount": gorm.Expr("cum_usd_amount+?", ops.UsdValue),
- "usd_amount": gorm.Expr("usd_amount+?", ops.UsdValue),
- "version": gorm.Expr("version + 1"),
- }).Error
- if err != nil {
- txDb.Rollback()
- return err
- }
- }
- // 全局操作资产方法 -- 当为管理员后台操作 则表示不需要扣除资产
- if adminId == 0 {
- if err = s.GenBillAndActionAsset(txDb, user.Id, constant.CoinSystemSymbol, decimal.Zero.Sub(quantity), decimal.Zero, bs); err != nil {
- txDb.Rollback()
- return err
- }
- }
- txDb.Commit()
- // todo: 2 生效后写入聚合订单 -- magic_stake_user_order -- 排队逻辑
- return nil
- }
- // 赎回
- func (s *CommonService) CurrentRedeem(user *entity.User, amount decimal.Decimal, adminId int64) error {
- // 获取分布式锁 存在用户购买商品 发放奖励等情况 可能会影响资产变化
- lockKey := fmt.Sprintf("%s:%d", constant.UserAssetLocker, user.Id)
- lock, err := redisclient.Lock(lockKey)
- if err != nil {
- return fmt.Errorf("failed to acquire lock: %v", err)
- }
- core.Log.Infof("用户ID:%s 资产锁获取", lockKey)
- defer func() {
- err = redisclient.UnlockSafe(lock)
- if err != nil {
- core.Log.Error(err)
- }
- core.Log.Infof("用户ID:%s 资产锁释放", lockKey)
- }()
- // 为用户质押
- return nil
- }
- // 领取
- func (s *CommonService) CurrentProfitClaim(user *entity.User) error {
- txDb := s.DB().Begin()
- currentOrder, err := s.FirstStakeUserCurrentOrder(txDb.Where("user_id", user.Id))
- if err != nil {
- txDb.Rollback()
- return err
- }
- if currentOrder.AvailableQuantity.LessThanOrEqual(decimal.Zero) {
- txDb.Rollback()
- return fmt.Errorf("可领取为:%s", currentOrder.AvailableQuantity)
- }
- bs := constant.BsById(constant.BsClaimCurrentProfit)
- bs.ContextName = currentOrder.TableName()
- bs.ContextValue = fmt.Sprintf("%d", currentOrder.Id)
- // 修改资产与构建流水
- err = s.GenBillAndActionAsset(txDb, user.Id, constant.CoinSymbolTD, decimal.Zero.Add(currentOrder.AvailableQuantity), decimal.Zero, bs)
- if err != nil {
- txDb.Rollback()
- return err
- }
- // AvailableQuantity decimal.Decimal `json:"availableQuantity" gorm:"type:decimal(25,8);default:0;comment:待领取数量"`
- // AvailableUsdAmount decimal.Decimal `json:"availableUsdAmount" gorm:"type:decimal(25,8);default:0;comment:待领取价值"`
- // CumClaimQuantity decimal.Decimal `json:"cumClaimQuantity" gorm:"type:decimal(25,8);default:0;comment:累计领取数量"`
- // CumClaimUsdAmount decimal.Decimal `json:"cumClaimUsdAmount" gorm:"type:decimal(25,8);default:0;comment:累计领取价值"`
- // LastClaimPeriod string `json:"lastClaimPeriod" gorm:"type:varchar(32);comment:最近领取期号"`
- updateStm := txDb.Model(&entity.StakeUserCurrentOrder{}).
- Where("id", currentOrder.Id).
- Where("version", currentOrder.Version).
- Updates(map[string]interface{}{
- "available_quantity": gorm.Expr("available_quantity - ?", currentOrder.AvailableQuantity),
- "available_usd_amount": gorm.Expr("available_usd_amount - ?", currentOrder.AvailableUsdAmount),
- "cum_claim_quantity": gorm.Expr("cum_claim_quantity + ?", currentOrder.AvailableQuantity),
- "cum_claim_usd_amount": gorm.Expr("cum_claim_usd_amount + ?", currentOrder.AvailableUsdAmount),
- "last_claim_period": utils.NowPeriodNo(),
- "version": gorm.Expr("version + 1"),
- })
- if updateStm.RowsAffected != 1 || updateStm.Error != nil {
- txDb.Rollback()
- return err
- }
- // todo:修改收益指标
- txDb.Commit()
- return nil
- }
- // todo: 构建活期操作记录
- func (s *CommonService) buildCurrentStakeOps(user *entity.User, quantity, price decimal.Decimal, bs *constant.BusinessType, adminId int64) *entity.StakeUserCurrentOpsRecord {
- return &entity.StakeUserCurrentOpsRecord{
- UserId: user.Id,
- Uid: user.Uid,
- Price: price,
- BusinessNumber: bs.BusinessNumber,
- BusinessName: bs.BusinessName,
- ContextName: bs.ContextName,
- ContextValue: bs.ContextValue,
- BeforeQuantity: decimal.Zero,
- BeforeUsdAmount: decimal.Zero,
- Quantity: quantity,
- UsdValue: quantity.Mul(price),
- AfterUsdAmount: decimal.Zero,
- AfterQuantity: decimal.Zero,
- QueueState: entity.StakeQueueStateWaiting,
- AdminId: adminId,
- EffTime: 0,
- Remark: "",
- }
- }
|