Browse Source

add 结算

master
dengbiao 2 months ago
parent
commit
d83279d6b6
17 changed files with 846 additions and 11 deletions
  1. +25
    -0
      app/enum/enum_agent.go
  2. +55
    -0
      app/enum/enum_agent_settlement.go
  3. +25
    -0
      app/enum/enum_agent_settlement_with_flow.go
  4. +25
    -0
      app/enum/enum_medium.go
  5. +55
    -0
      app/enum/enum_medium_settlement.go
  6. +25
    -0
      app/enum/enum_medium_settlement_with_flow.go
  7. +0
    -2
      app/md/app_redis_key.go
  8. +37
    -0
      app/md/fin_agent_flow.go
  9. +36
    -0
      app/md/fin_medium_flow.go
  10. +5
    -1
      app/md/md_generate_wx_ad_data.go
  11. +144
    -0
      app/svc/svc_deal_agent_amount.go
  12. +144
    -0
      app/svc/svc_deal_medium_amount.go
  13. +3
    -2
      app/svc/svc_medium_agent.go
  14. +24
    -0
      app/svc/svc_redis_mutex_lock_for_agent.go
  15. +3
    -3
      app/svc/svc_redis_mutex_lock_for_medium.go
  16. +238
    -2
      app/svc/svc_wx_data.go
  17. +2
    -1
      go.mod

+ 25
- 0
app/enum/enum_agent.go View File

@@ -0,0 +1,25 @@
package enum

type AgentSettlementType int32

const (
AgentSettlementTypeForDay = 1
AgentSettlementTypeForWeek = 2
AgentSettlementTypeForMonth = 3
AgentSettlementTypeForPaymentInAdvance = 4
)

func (gt AgentSettlementType) String() string {
switch gt {
case AgentSettlementTypeForDay:
return "日结"
case AgentSettlementTypeForWeek:
return "周结"
case AgentSettlementTypeForMonth:
return "月结"
case AgentSettlementTypeForPaymentInAdvance:
return "预付"
default:
return "未知"
}
}

+ 55
- 0
app/enum/enum_agent_settlement.go View File

@@ -0,0 +1,55 @@
package enum

type AgentSettlementPayState int32

const (
AgentSettlementPayStateForNotStarted = 0
AgentSettlementPayStateForPendingInvoiceReview = 1
AgentSettlementPayStateForInvoiceReviewProgress = 2
AgentSettlementPayStateForInvoiceReviewRejected = 3
AgentSettlementPayStateForPaymentIng = 4
AgentSettlementPayStateForPaymentAlready = 5
)

func (gt AgentSettlementPayState) String() string {
switch gt {
case AgentSettlementPayStateForNotStarted:
return "未开始"
case AgentSettlementPayStateForPendingInvoiceReview:
return "待审核发票"
case AgentSettlementPayStateForInvoiceReviewProgress:
return "发票审核中"
case AgentSettlementPayStateForInvoiceReviewRejected:
return "发票审核拒绝"
case AgentSettlementPayStateForPaymentIng:
return "付款中"
case AgentSettlementPayStateForPaymentAlready:
return "已付款"
default:
return "未知"
}
}

type AgentSettlementState int32

const (
AgentSettlementStateForNotStarted = 0
AgentSettlementStateForAccountingInProgress = 1
AgentSettlementStateForToBeSign = 2
AgentSettlementStateForCompleteSign = 3
)

func (gt AgentSettlementState) String() string {
switch gt {
case AgentSettlementStateForNotStarted:
return "未开始"
case AgentSettlementStateForAccountingInProgress:
return "核算中"
case AgentSettlementStateForToBeSign:
return "待签订"
case AgentSettlementStateForCompleteSign:
return "已完成签订"
default:
return "未知"
}
}

+ 25
- 0
app/enum/enum_agent_settlement_with_flow.go View File

@@ -0,0 +1,25 @@
package enum

type AgentSettlementWithFlowKind int32

const (
AgentSettlementWithFlowKindForBasicIncome = 1
AgentSettlementWithFlowKindForOtherIncome = 2
AgentSettlementWithFlowKindForAddOtherIncome = 3
AgentSettlementWithFlowKindForSubOtherIncome = 4
)

func (gt AgentSettlementWithFlowKind) String() string {
switch gt {
case AgentSettlementWithFlowKindForBasicIncome:
return "基础收益"
case AgentSettlementWithFlowKindForOtherIncome:
return "其他收益"
case AgentSettlementWithFlowKindForAddOtherIncome:
return "管理员增加其他收益"
case AgentSettlementWithFlowKindForSubOtherIncome:
return "管理员减少其他收益"
default:
return "未知"
}
}

+ 25
- 0
app/enum/enum_medium.go View File

@@ -0,0 +1,25 @@
package enum

type MediumSettlementType int32

const (
MediumSettlementTypeForDay = 1
MediumSettlementTypeForWeek = 2
MediumSettlementTypeForMonth = 3
MediumSettlementTypeForPaymentInAdvance = 4
)

func (gt MediumSettlementType) String() string {
switch gt {
case MediumSettlementTypeForDay:
return "日结"
case MediumSettlementTypeForWeek:
return "周结"
case MediumSettlementTypeForMonth:
return "月结"
case MediumSettlementTypeForPaymentInAdvance:
return "预付"
default:
return "未知"
}
}

+ 55
- 0
app/enum/enum_medium_settlement.go View File

@@ -0,0 +1,55 @@
package enum

type MediumSettlementPayState int32

const (
MediumSettlementPayStateForNotStarted = 0
MediumSettlementPayStateForPendingInvoiceReview = 1
MediumSettlementPayStateForInvoiceReviewProgress = 2
MediumSettlementPayStateForInvoiceReviewRejected = 3
MediumSettlementPayStateForPaymentIng = 4
MediumSettlementPayStateForPaymentAlready = 5
)

func (gt MediumSettlementPayState) String() string {
switch gt {
case MediumSettlementPayStateForNotStarted:
return "未开始"
case MediumSettlementPayStateForPendingInvoiceReview:
return "待审核发票"
case MediumSettlementPayStateForInvoiceReviewProgress:
return "发票审核中"
case MediumSettlementPayStateForInvoiceReviewRejected:
return "发票审核拒绝"
case MediumSettlementPayStateForPaymentIng:
return "付款中"
case MediumSettlementPayStateForPaymentAlready:
return "已付款"
default:
return "未知"
}
}

type MediumSettlementState int32

const (
MediumSettlementStateForNotStarted = 0
MediumSettlementStateForAccountingInProgress = 1
MediumSettlementStateForToBeSign = 2
MediumSettlementStateForCompleteSign = 3
)

func (gt MediumSettlementState) String() string {
switch gt {
case MediumSettlementStateForNotStarted:
return "未开始"
case MediumSettlementStateForAccountingInProgress:
return "核算中"
case MediumSettlementStateForToBeSign:
return "待签订"
case MediumSettlementStateForCompleteSign:
return "已完成签订"
default:
return "未知"
}
}

+ 25
- 0
app/enum/enum_medium_settlement_with_flow.go View File

@@ -0,0 +1,25 @@
package enum

type MediumSettlementWithFlowKind int32

const (
MediumSettlementWithFlowKindForBasicIncome = 1
MediumSettlementWithFlowKindForOtherIncome = 2
MediumSettlementWithFlowKindForAddOtherIncome = 3
MediumSettlementWithFlowKindForSubOtherIncome = 4
)

func (gt MediumSettlementWithFlowKind) String() string {
switch gt {
case MediumSettlementWithFlowKindForBasicIncome:
return "基础收益"
case MediumSettlementWithFlowKindForOtherIncome:
return "其他收益"
case MediumSettlementWithFlowKindForAddOtherIncome:
return "管理员增加其他收益"
case MediumSettlementWithFlowKindForSubOtherIncome:
return "管理员减少其他收益"
default:
return "未知"
}
}

+ 0
- 2
app/md/app_redis_key.go View File

@@ -8,8 +8,6 @@ const (


AppCfgCacheKey = "%s:cfg_cache:%s" // 占位符: masterId, key的第一个字母 AppCfgCacheKey = "%s:cfg_cache:%s" // 占位符: masterId, key的第一个字母


UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额)

AdminRolePermissionCacheTime = 3600 * 24 * 0.5 AdminRolePermissionCacheTime = 3600 * 24 * 0.5


KEY_SYS_CFG_CACHE = "sys_cfg_cache" KEY_SYS_CFG_CACHE = "sys_cfg_cache"


+ 37
- 0
app/md/fin_agent_flow.go View File

@@ -0,0 +1,37 @@
package md

const (
FinAgentFlowDirectionIncome = 1 //流水 - 收入
FinAgentFlowDirectionExpenditure = 2 //流水 - 支出
)

const (
FinAgentFlowRedisKey = "%s:fin_agent_flow:%d:user:%d"
)

const (
AdminUpdateAddTitleForAgentFlow = "管理员修改增加"
AdminUpdateSubTitleForAgentFlow = "管理员修改减少"
SettlementSubTitleForAgentFlow = "结算款扣除"
)

const (
AdminUpdateAddKindForAgentFlow = 1 // 管理员修改增加
AdminUpdateSubKindForAgentFlow = 2 // 管理员修改增加
SettlementSubKindForAgentFlow = 3 // 结算款扣除
)

const DealAgentAmountRequestIdPrefix = "%s:deal_agent_amount:%d"
const AgentFinValidUpdateLock = "%s:agent_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额)
const AgentAmountRedisKey = "%s:cache_agent_amount:%d"

type DealAgentAmount struct {
Mid string `json:"mid"`
Type int `json:"type"`
Kind int `json:"kind"`
Title string `json:"title"`
OrdId string `json:"ord_id"`
AgentId int `json:"agent_id"`
Amount float64 `json:"amount"`
Memo string `json:"memo"`
}

+ 36
- 0
app/md/fin_medium_flow.go View File

@@ -0,0 +1,36 @@
package md

const (
FinMediumFlowDirectionIncome = 1 //流水 - 收入
FinMediumFlowDirectionExpenditure = 2 //流水 - 支出
)

const (
FinMediumFlowRedisKey = "%s:fin_medium_flow:%d:user:%d"
)

const (
AdminUpdateAddTitleForMediumFlow = "管理员修改增加"
AdminUpdateSubTitleForMediumFlow = "管理员修改减少"
SettlementSubTitleForMediumFlow = "结算款扣除"
)

const (
AdminUpdateAddKindForMediumFlow = 1 // 管理员修改增加
AdminUpdateSubKindForMediumFlow = 2 // 管理员修改增加
SettlementSubKindForMediumFlow = 3 // 结算款扣除
)

const DealMediumAmountRequestIdPrefix = "%s:deal_medium_amount:%d"
const MediumFinValidUpdateLock = "%s:medium_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额)
const MediumAmountRedisKey = "%s:cache_medium_amount:%d"

type DealMediumAmount struct {
Mid string `json:"mid"`
Type int `json:"type"`
Kind int `json:"kind"`
OrdId string `json:"ord_id"`
MediumId int `json:"medium_id"`
Amount float64 `json:"amount"`
Memo string `json:"memo"`
}

+ 5
- 1
app/md/md_generate_wx_ad_data.go View File

@@ -1,7 +1,7 @@
package md package md


type GenerateWxAdData struct { type GenerateWxAdData struct {
GenerateDataId int `json:"generate_data_id" example:"原始数据id"`
OriginalDataId int `json:"original_data_id" example:"原始数据id"`
OriginalExposureCount int `json:"original_exposure_count" example:"原-曝光量"` OriginalExposureCount int `json:"original_exposure_count" example:"原-曝光量"`
OriginalEcpm string `json:"original_ecpm" example:"原-广告千次曝光收益(分)"` OriginalEcpm string `json:"original_ecpm" example:"原-广告千次曝光收益(分)"`
NowExposureCount int `json:"now_exposure_count" example:"现-曝光量"` NowExposureCount int `json:"now_exposure_count" example:"现-曝光量"`
@@ -13,3 +13,7 @@ type ClacEcpmReq struct {
OriginalEcpm string `json:"original_ecpm" example:"原-广告千次曝光收益(分)"` OriginalEcpm string `json:"original_ecpm" example:"原-广告千次曝光收益(分)"`
GenerateDataId int `json:"generate_data_id" example:"原始数据id"` GenerateDataId int `json:"generate_data_id" example:"原始数据id"`
} }

type SettlementWxAdData struct {
GenerateDataId int `json:"generate_data_id" example:"生成数据id"`
}

+ 144
- 0
app/svc/svc_deal_agent_amount.go View File

@@ -0,0 +1,144 @@
package svc

import (
"applet/app/md"
"applet/app/utils/cache"
db "code.fnuoos.com/zhimeng/model.git/src"
"code.fnuoos.com/zhimeng/model.git/src/super/implement"
"code.fnuoos.com/zhimeng/model.git/src/super/model"
zhios_order_relate_utils "code.fnuoos.com/zhimeng/model.git/utils"
zhios_order_relate_logx "code.fnuoos.com/zhimeng/model.git/utils/logx"
"errors"
"fmt"
"github.com/shopspring/decimal"
"strconv"
"time"
"xorm.io/xorm"
)

// DealAgentAmount 处理给代理余额
func DealAgentAmount(session *xorm.Session, req md.DealAgentAmount) (err error) {
if req.Amount < 0 {
req.Amount = 0
}
//1、分布式锁阻拦
requestIdPrefix := fmt.Sprintf(md.DealAgentAmountRequestIdPrefix, req.Mid, req.AgentId)
cb, err := HandleBalanceDistributedLockForAgent(req.Mid, strconv.Itoa(req.AgentId), requestIdPrefix)
if err != nil {
return err
}
if cb != nil {
defer cb() // 释放锁
}

//2、计算&&组装数据
now := time.Now()
userAmount, err := GetAgentAmount(session, req.Mid, req.AgentId, true)
if err != nil {
return err
}
userAmountValue := decimal.NewFromFloat(zhios_order_relate_utils.StrToFloat64(userAmount))
amountValue := decimal.NewFromFloat(req.Amount).RoundFloor(4)

var finUserFlow = model.FinAgentFlow{
AgentId: req.AgentId,
Type: req.Type,
Amount: amountValue.String(),
BeforeAmount: userAmount,
OrdId: req.OrdId,
Kind: req.Kind,
Memo: req.Memo,
CreateAt: now.Format("2006-01-02 15:04:05"),
UpdateAt: now.Format("2006-01-02 15:04:05"),
}
if req.Type == md.FinAgentFlowDirectionIncome {
finUserFlow.AfterAmount = userAmountValue.Add(amountValue).RoundFloor(4).String()
} else if req.Type == md.FinAgentFlowDirectionExpenditure {
finUserFlow.AfterAmount = userAmountValue.Sub(amountValue).RoundFloor(4).String()
} else {
err = errors.New("错误的kind类型")
return err
}
if zhios_order_relate_utils.StrToFloat64(finUserFlow.AfterAmount) < 0 {
zhios_order_relate_utils.FilePutContents("agent_amount_not", zhios_order_relate_utils.SerializeStr(map[string]interface{}{
"agent_id": finUserFlow.AgentId,
"amount": finUserFlow.Amount,
"before_amount": finUserFlow.BeforeAmount,
"memo": finUserFlow.Memo,
"mid": req.Mid,
}))
return errors.New("代理余额不足")
}

//3、插入 `fin_user_flow` 记录
affected, err := session.Insert(&finUserFlow)
if affected == 0 || err != nil {
_ = zhios_order_relate_logx.Warn(err)
return err
}

//4、修改 `user_profile`的fin_valid值 && 及缓存
err = SetCacheAgentAmount(session, req.Mid, finUserFlow.AfterAmount, req.AgentId, true)
if err != nil {
return err
}

return nil
}

// GetAgentAmount 获取代理余额
func GetAgentAmount(session *xorm.Session, masterId string, agentId int, isForceUpdate bool) (amount string, err error) {
if isForceUpdate {
agentListDb := implement.NewAgentListDb(db.Db)
agent, err1 := agentListDb.GetAgentListBySession(session, agentId)
if err1 != nil {
return amount, err1
}
if agent == nil {
amount = "0"
} else {
amount = agent.Amount
}
//将获取到的余额值缓存至redis
_ = SetCacheAgentAmount(session, masterId, amount, agentId, false)
} else {
redisKey := fmt.Sprintf(md.AgentAmountRedisKey, masterId, agentId)
amount, err = cache.GetString(redisKey)
if err != nil && err.Error() != "redigo: nil returned" {
return
}
agentListDb := implement.NewAgentListDb(db.DBs[masterId])
agent, err1 := agentListDb.GetAgentListBySession(session, agentId)
if err1 != nil {
return amount, err1
}
if agent == nil {
amount = "0"
} else {
amount = agent.Amount
}
//将获取到的余额值缓存至redis
_ = SetCacheAgentAmount(session, masterId, amount, agentId, false)
}
return amount, nil
}

// SetCacheAgentAmount 设置缓存的用户余额
func SetCacheAgentAmount(session *xorm.Session, masterId, amount string, agentId int, isUpdateDb bool) error {
redisKey := fmt.Sprintf(md.AgentAmountRedisKey, masterId, agentId)
if isUpdateDb {
_, err := session.Where("agent_id=?", agentId).Update(model.AgentList{
AgentId: agentId,
Amount: amount,
})
if err != nil {
return err
}
}
//TODO::默认缓存1小时 (先调整为 2 min)
_, err := cache.SetEx(redisKey, zhios_order_relate_utils.StrToFloat64(amount), 60*0.5)
if err != nil {
return err
}
return nil
}

+ 144
- 0
app/svc/svc_deal_medium_amount.go View File

@@ -0,0 +1,144 @@
package svc

import (
"applet/app/md"
"applet/app/utils/cache"
db "code.fnuoos.com/zhimeng/model.git/src"
"code.fnuoos.com/zhimeng/model.git/src/super/implement"
"code.fnuoos.com/zhimeng/model.git/src/super/model"
zhios_order_relate_utils "code.fnuoos.com/zhimeng/model.git/utils"
zhios_order_relate_logx "code.fnuoos.com/zhimeng/model.git/utils/logx"
"errors"
"fmt"
"github.com/shopspring/decimal"
"strconv"
"time"
"xorm.io/xorm"
)

// DealMediumAmount 处理给媒体余额
func DealMediumAmount(session *xorm.Session, req md.DealMediumAmount) (err error) {
if req.Amount < 0 {
req.Amount = 0
}
//1、分布式锁阻拦
requestIdPrefix := fmt.Sprintf(md.DealMediumAmountRequestIdPrefix, req.Mid, req.MediumId)
cb, err := HandleBalanceDistributedLockForMedium(req.Mid, strconv.Itoa(req.MediumId), requestIdPrefix)
if err != nil {
return err
}
if cb != nil {
defer cb() // 释放锁
}

//2、计算&&组装数据
now := time.Now()
userAmount, err := GetMediumAmount(session, req.Mid, req.MediumId, true)
if err != nil {
return err
}
userAmountValue := decimal.NewFromFloat(zhios_order_relate_utils.StrToFloat64(userAmount))
amountValue := decimal.NewFromFloat(req.Amount).RoundFloor(4)

var finUserFlow = model.FinMediumFlow{
MediumId: req.MediumId,
Type: req.Type,
Amount: amountValue.String(),
BeforeAmount: userAmount,
OrdId: req.OrdId,
Kind: req.Kind,
Memo: req.Memo,
CreateAt: now.Format("2006-01-02 15:04:05"),
UpdateAt: now.Format("2006-01-02 15:04:05"),
}
if req.Type == md.FinMediumFlowDirectionIncome {
finUserFlow.AfterAmount = userAmountValue.Add(amountValue).RoundFloor(4).String()
} else if req.Type == md.FinMediumFlowDirectionExpenditure {
finUserFlow.AfterAmount = userAmountValue.Sub(amountValue).RoundFloor(4).String()
} else {
err = errors.New("错误的kind类型")
return err
}
if zhios_order_relate_utils.StrToFloat64(finUserFlow.AfterAmount) < 0 {
zhios_order_relate_utils.FilePutContents("medium_amount_not", zhios_order_relate_utils.SerializeStr(map[string]interface{}{
"medium_id": finUserFlow.MediumId,
"amount": finUserFlow.Amount,
"before_amount": finUserFlow.BeforeAmount,
"memo": finUserFlow.Memo,
"mid": req.Mid,
}))
return errors.New("媒体余额不足")
}

//3、插入 `fin_user_flow` 记录
affected, err := session.Insert(&finUserFlow)
if affected == 0 || err != nil {
_ = zhios_order_relate_logx.Warn(err)
return err
}

//4、修改 `user_profile`的fin_valid值 && 及缓存
err = SetCacheMediumAmount(session, req.Mid, finUserFlow.AfterAmount, req.MediumId, true)
if err != nil {
return err
}

return nil
}

// GetMediumAmount 获取媒体余额
func GetMediumAmount(session *xorm.Session, masterId string, mediumId int, isForceUpdate bool) (amount string, err error) {
if isForceUpdate {
mediumListDb := implement.NewMediumListDb(db.Db)
medium, err1 := mediumListDb.GetMediumListBySession(session, mediumId)
if err1 != nil {
return amount, err1
}
if medium == nil {
amount = "0"
} else {
amount = medium.Amount
}
//将获取到的余额值缓存至redis
_ = SetCacheMediumAmount(session, masterId, amount, mediumId, false)
} else {
redisKey := fmt.Sprintf(md.MediumAmountRedisKey, masterId, mediumId)
amount, err = cache.GetString(redisKey)
if err != nil && err.Error() != "redigo: nil returned" {
return
}
mediumListDb := implement.NewMediumListDb(db.DBs[masterId])
medium, err1 := mediumListDb.GetMediumListBySession(session, mediumId)
if err1 != nil {
return amount, err1
}
if medium == nil {
amount = "0"
} else {
amount = medium.Amount
}
//将获取到的余额值缓存至redis
_ = SetCacheMediumAmount(session, masterId, amount, mediumId, false)
}
return amount, nil
}

// SetCacheMediumAmount 设置缓存的用户余额
func SetCacheMediumAmount(session *xorm.Session, masterId, amount string, mediumId int, isUpdateDb bool) error {
redisKey := fmt.Sprintf(md.MediumAmountRedisKey, masterId, mediumId)
if isUpdateDb {
_, err := session.Where("medium_id=?", mediumId).Update(model.MediumList{
MediumId: mediumId,
Amount: amount,
})
if err != nil {
return err
}
}
//TODO::默认缓存1小时 (先调整为 2 min)
_, err := cache.SetEx(redisKey, zhios_order_relate_utils.StrToFloat64(amount), 60*0.5)
if err != nil {
return err
}
return nil
}

+ 3
- 2
app/svc/svc_medium_agent.go View File

@@ -134,13 +134,14 @@ func MediumBindAgentSave(c *gin.Context) {
var tmp = model.AgentWithMedium{ var tmp = model.AgentWithMedium{
AgentId: username.AgentId, AgentId: username.AgentId,
MediumId: utils.StrToInt(req.MediumId), MediumId: utils.StrToInt(req.MediumId),
CreateAt: time.Now(),
UpdateAt: time.Now(),
CreateAt: time.Now().Format("2006-01-02 15:04:05"),
UpdateAt: time.Now().Format("2006-01-02 15:04:05"),
} }
db.Db.InsertOne(&tmp) db.Db.InsertOne(&tmp)
e.OutSuc(c, "success", nil) e.OutSuc(c, "success", nil)
return return
} }

func MediumBindAgentDel(c *gin.Context) { func MediumBindAgentDel(c *gin.Context) {
var req md.MediumListDelReq var req md.MediumListDelReq
err := c.ShouldBindJSON(&req) err := c.ShouldBindJSON(&req)


+ 24
- 0
app/svc/svc_redis_mutex_lock_for_agent.go View File

@@ -0,0 +1,24 @@
package svc

import (
"applet/app/md"
"errors"
"fmt"
)

// HandleBalanceDistributedLockForAgent 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁
func HandleBalanceDistributedLockForAgent(masterId, uid, requestIdPrefix string) (cb func(), err error) {
// 获取余额更新锁
balanceLockKey := fmt.Sprintf(md.AgentFinValidUpdateLock, masterId, uid)
requestId := GetDistributedLockRequestId(requestIdPrefix)
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
if !balanceLockOk {
return nil, errors.New("系统繁忙,请稍后再试")
}

cb = func() {
_, _ = ReleaseDistributedLock(balanceLockKey, requestId)
}

return cb, nil
}

app/svc/svc_redis_mutex_lock.go → app/svc/svc_redis_mutex_lock_for_medium.go View File

@@ -67,10 +67,10 @@ func GetDistributedLockRequestId(prefix string) string {
return prefix + utils.IntToStr(rand.Intn(100000000)) return prefix + utils.IntToStr(rand.Intn(100000000))
} }


// HandleBalanceDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁
func HandleBalanceDistributedLock(masterId, uid, requestIdPrefix string) (cb func(), err error) {
// HandleBalanceDistributedLockForMedium 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁
func HandleBalanceDistributedLockForMedium(masterId, uid, requestIdPrefix string) (cb func(), err error) {
// 获取余额更新锁 // 获取余额更新锁
balanceLockKey := fmt.Sprintf(md.UserFinValidUpdateLock, masterId, uid)
balanceLockKey := fmt.Sprintf(md.MediumFinValidUpdateLock, masterId, uid)
requestId := GetDistributedLockRequestId(requestIdPrefix) requestId := GetDistributedLockRequestId(requestIdPrefix)
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
if !balanceLockOk { if !balanceLockOk {

+ 238
- 2
app/svc/svc_wx_data.go View File

@@ -1,6 +1,7 @@
package svc package svc


import ( import (
"applet/app/enum"
"applet/app/md" "applet/app/md"
"applet/app/utils" "applet/app/utils"
db "code.fnuoos.com/zhimeng/model.git/src" db "code.fnuoos.com/zhimeng/model.git/src"
@@ -13,7 +14,7 @@ import (
func GenerateWxAdData(req md.GenerateWxAdData) (err error, generateWxAdData model.GenerateWxAdData) { func GenerateWxAdData(req md.GenerateWxAdData) (err error, generateWxAdData model.GenerateWxAdData) {
//1、查找原始数据记录 //1、查找原始数据记录
originalWxAdDataDb := implement.NewOriginalWxAdDataDb(db.Db) originalWxAdDataDb := implement.NewOriginalWxAdDataDb(db.Db)
originalWxAdData, err := originalWxAdDataDb.GetOriginalWxAdData(req.GenerateDataId)
originalWxAdData, err := originalWxAdDataDb.GetOriginalWxAdData(req.OriginalDataId)
if err != nil { if err != nil {
return return
} }
@@ -92,7 +93,7 @@ func GenerateWxAdData(req md.GenerateWxAdData) (err error, generateWxAdData mode
generateWxAdData = model.GenerateWxAdData{ generateWxAdData = model.GenerateWxAdData{
Uuid: originalWxAdData.Uuid, Uuid: originalWxAdData.Uuid,
AppId: originalWxAdData.AppId, AppId: originalWxAdData.AppId,
GenerateDataId: originalWxAdData.Id,
OriginalDataId: originalWxAdData.Id,
SlotId: originalWxAdData.SlotId, SlotId: originalWxAdData.SlotId,
AdSlot: originalWxAdData.AdSlot, AdSlot: originalWxAdData.AdSlot,
Date: originalWxAdData.Date, Date: originalWxAdData.Date,
@@ -195,3 +196,238 @@ func ClacEcpm(req md.ClacEcpmReq) (err error, ecpm string) {
ecpm = utils.Float64ToStrPrec4(float64(agreementSharingTotal) / (float64(originalWxAdData.ExposureCount) / 1000)) ecpm = utils.Float64ToStrPrec4(float64(agreementSharingTotal) / (float64(originalWxAdData.ExposureCount) / 1000))
return return
} }

func SettlementWxAdData(req md.SettlementWxAdData) (err error) {
//1、查找生成数据记录
generateWxAdDataDb := implement.NewGenerateWxAdDataDb(db.Db)
generateWxAdData, err := generateWxAdDataDb.GetGenerateWxAdData(req.GenerateDataId)
if err != nil {
return
}
if generateWxAdData == nil {
err = errors.New("未查询到生成数据记录")
return
}

originalWxAdDataDb := implement.NewOriginalWxAdDataDb(db.Db)
originalWxAdData, err := originalWxAdDataDb.GetOriginalWxAdData(generateWxAdData.OriginalDataId)
if err != nil {
return
}
if originalWxAdData == nil {
err = errors.New("未查询到原始数据记录")
return
}

//2、查询 生成数据-代理明细 记录
generateWxAdDataWithAgentFlowDb := implement.NewGenerateWxAdDataWithAgentFlowDb(db.Db)
generateWxAdDataWithAgentFlow, err := generateWxAdDataWithAgentFlowDb.FindGenerateWxAdDataWithAgentFlowByStrategyId(generateWxAdData.Id)
if err != nil {
return
}

//3、查询 媒体、代理的结算方式
mediumListDb := implement.NewMediumListDb(db.Db)
medium, err := mediumListDb.GetMediumList(originalWxAdData.MediumId)
if err != nil {
return
}
if medium == nil {
return errors.New("媒体:" + utils.IntToStr(originalWxAdData.MediumId) + "未查询到对应记录")
}
var agentMap = map[int]model.AgentList{}
agentListDb := implement.NewAgentListDb(db.Db)
for _, v := range *generateWxAdDataWithAgentFlow {
agent, err1 := agentListDb.GetAgentList(v.AgentId)
if err1 != nil {
return err1
}
if agent == nil {
return errors.New("代理:" + utils.IntToStr(v.AgentId) + "未查询到对应记录")
}
agentMap[v.AgentId] = *agent
}

//4、生成数据
now := time.Now()
session := db.Db.NewSession()
defer session.Close()
session.Begin()

//4.1 新增/更新 medium_settlement 数据
mediumSettlementDb := implement.NewMediumSettlementDb(db.Db)
mediumSettlement, err := mediumSettlementDb.GetMediumSettlementForAvailable(originalWxAdData.MediumId)
if err != nil {
return
}
var basicIncomeBefore, mediumSettlementState, mediumSettlementPayState int
//判断是否为预付结算类型
if medium.SettlementType == enum.MediumSettlementTypeForPaymentInAdvance {
mediumSettlementState = enum.MediumSettlementStateForCompleteSign
mediumSettlementPayState = enum.MediumSettlementPayStateForPaymentAlready
}

if mediumSettlement == nil {
//新增一条数据
mediumSettlement = &model.MediumSettlement{
Uuid: originalWxAdData.Uuid,
MediumId: originalWxAdData.MediumId,
AppId: originalWxAdData.AppId,
BusinessKind: 1,
Kind: medium.SettlementType,
BasicIncome: generateWxAdData.MediaRevenue,
OtherIncome: 0,
State: mediumSettlementState,
PayState: mediumSettlementPayState,
StartDate: now.Format("2006-01-02"),
EndDate: "",
CreateAt: now.Format("2006-01-02 15:04:05"),
UpdateAt: now.Format("2006-01-02 15:04:05"),
}
_, err = mediumSettlementDb.MediumSettlementInsertBySession(session, mediumSettlement)
if err != nil {
_ = session.Rollback()
return
}
} else {
//更新数据
basicIncomeBefore = mediumSettlement.BasicIncome
mediumSettlement.BasicIncome += generateWxAdData.MediaRevenue
mediumSettlement.State = mediumSettlementState
mediumSettlement.PayState = mediumSettlementPayState
_, err = mediumSettlementDb.UpdateMediumSettlementBySession(session, mediumSettlement, "basic_income", "pay_state", "state")
if err != nil {
_ = session.Rollback()
return
}
}
if medium.SettlementType == enum.MediumSettlementTypeForPaymentInAdvance {
err = DealMediumAmount(session, md.DealMediumAmount{
Mid: utils.IntToStr(originalWxAdData.Uuid),
Type: md.FinMediumFlowDirectionExpenditure,
Kind: md.SettlementSubKindForMediumFlow,
OrdId: utils.IntToStr(mediumSettlement.Id),
MediumId: originalWxAdData.MediumId,
Amount: float64(generateWxAdData.MediaRevenue) / 100,
Memo: md.SettlementSubTitleForMediumFlow,
})
if err != nil {
_ = session.Rollback()
return
}
}

//4.2 新增 medium_settlement_with_flow 数据
mediumSettlementWithFlowDb := implement.NewMediumSettlementWithFlowDb(db.Db)
_, err = mediumSettlementWithFlowDb.MediumSettlementWithFlowInsertBySession(session, &model.MediumSettlementWithFlow{
SettlementId: mediumSettlement.Id,
GenerateDataId: generateWxAdData.Id,
Amount: generateWxAdData.MediaRevenue,
BasicIncomeBefore: basicIncomeBefore,
BasicIncomeAfter: mediumSettlement.BasicIncome,
OtherIncomeBefore: 0,
OtherIncomeAfter: 0,
Kind: enum.MediumSettlementWithFlowKindForBasicIncome,
CreateAt: now.Format("2006-01-02 15:04:05"),
UpdateAt: now.Format("2006-01-02 15:04:05"),
})
if err != nil {
_ = session.Rollback()
return
}
//4.3 新增 agent_settlement、agent_settlement_with_flow 数据
agentSettlementDb := implement.NewAgentSettlementDb(db.Db)
agentSettlementWithFlowDb := implement.NewAgentSettlementWithFlowDb(db.Db)
for _, v := range *generateWxAdDataWithAgentFlow {
agentSettlement, err1 := agentSettlementDb.GetAgentSettlementForAvailable(v.AgentId)
if err1 != nil {
_ = session.Rollback()
return err1
}

var agentBasicIncomeBefore, agentOtherIncomeBefore, agentSettlementState, agentSettlementPayState int
//判断是否为预付结算类型
if agentMap[v.AgentId].SettlementType == enum.AgentSettlementTypeForPaymentInAdvance {
agentSettlementState = enum.AgentSettlementStateForCompleteSign
agentSettlementPayState = enum.AgentSettlementPayStateForPaymentAlready
}
if agentSettlement == nil {
//新增一条数据
agentSettlement = &model.AgentSettlement{
Uuid: v.Uuid,
AgentId: v.AgentId,
MediumId: originalWxAdData.MediumId,
AppId: v.AppId,
BusinessKind: 1,
Kind: agentMap[v.AgentId].SettlementType,
BasicIncome: v.AgentRevenue,
OtherIncome: v.ExtraRevenue,
State: agentSettlementState,
PayState: agentSettlementPayState,
StartDate: now.Format("2006-01-02"),
EndDate: "",
CreateAt: now.Format("2006-01-02 15:04:05"),
UpdateAt: now.Format("2006-01-02 15:04:05"),
}
_, err = agentSettlementDb.AgentSettlementInsertBySession(session, agentSettlement)
if err != nil {
_ = session.Rollback()
return
}
} else {
//更新数据
agentBasicIncomeBefore = agentSettlement.BasicIncome
agentOtherIncomeBefore = agentSettlement.OtherIncome
agentSettlement.BasicIncome += v.AgentRevenue
agentSettlement.OtherIncome += v.ExtraRevenue
agentSettlement.State += agentSettlementState
agentSettlement.PayState += agentSettlementPayState

_, err = agentSettlementDb.UpdateAgentSettlementBySession(session, agentSettlement, "basic_income", "other_income", "pay_state", "state")
if err != nil {
_ = session.Rollback()
return
}
}

if agentMap[v.AgentId].SettlementType == enum.AgentSettlementTypeForPaymentInAdvance {
err = DealAgentAmount(session, md.DealAgentAmount{
Mid: utils.IntToStr(originalWxAdData.Uuid),
Type: md.FinAgentFlowDirectionExpenditure,
Kind: md.SettlementSubKindForAgentFlow,
OrdId: utils.IntToStr(v.Id),
Amount: float64(v.AgentRevenue)/100 + float64(v.ExtraRevenue)/100,
Memo: md.SettlementSubTitleForMediumFlow,
})
if err != nil {
_ = session.Rollback()
return
}
}
_, err = agentSettlementWithFlowDb.AgentSettlementWithFlowInsertBySession(session, &model.AgentSettlementWithFlow{
SettlementId: mediumSettlement.Id,
GenerateDataId: generateWxAdData.Id,
Amount: v.AgentRevenue,
BasicIncomeBefore: agentBasicIncomeBefore,
BasicIncomeAfter: agentSettlement.BasicIncome,
OtherIncomeBefore: agentOtherIncomeBefore,
OtherIncomeAfter: agentSettlement.OtherIncome,
Kind: enum.AgentSettlementWithFlowKindForBasicIncome,
CreateAt: now.Format("2006-01-02 15:04:05"),
UpdateAt: now.Format("2006-01-02 15:04:05"),
})
if err != nil {
_ = session.Rollback()
return
}
}

//4、修改 generate_wx_ad_data 记录中的 is_generate_report (是否已应用(0:未 1:已) )
generateWxAdData.IsGenerateReport = 1
_, err = generateWxAdDataDb.UpdateGenerateWxAdDataBySession(session, generateWxAdData, "is_generate_report")
if err != nil {
_ = session.Rollback()
return
}
return session.Commit()
}

+ 2
- 1
go.mod View File

@@ -5,7 +5,7 @@ go 1.18
//replace code.fnuoos.com/zhimeng/model.git => E:/company/ad/models //replace code.fnuoos.com/zhimeng/model.git => E:/company/ad/models


require ( require (
code.fnuoos.com/zhimeng/model.git v0.0.3-0.20240827055504-c124d0031bf7
code.fnuoos.com/zhimeng/model.git v0.0.3-0.20240828084358-f52add033ca9
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
github.com/boombuler/barcode v1.0.1 github.com/boombuler/barcode v1.0.1
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5
@@ -22,6 +22,7 @@ require (
github.com/makiuchi-d/gozxing v0.0.0-20210324052758-57132e828831 github.com/makiuchi-d/gozxing v0.0.0-20210324052758-57132e828831
github.com/qiniu/api.v7/v7 v7.8.2 github.com/qiniu/api.v7/v7 v7.8.2
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/shopspring/decimal v1.3.1
github.com/sony/sonyflake v1.0.0 github.com/sony/sonyflake v1.0.0
github.com/swaggo/files v1.0.1 github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/gin-swagger v1.6.0


Loading…
Cancel
Save