@@ -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 "未知" | |||
} | |||
} |
@@ -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 "未知" | |||
} | |||
} |
@@ -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 "未知" | |||
} | |||
} |
@@ -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 "未知" | |||
} | |||
} |
@@ -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 "未知" | |||
} | |||
} |
@@ -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 "未知" | |||
} | |||
} |
@@ -8,8 +8,6 @@ const ( | |||
AppCfgCacheKey = "%s:cfg_cache:%s" // 占位符: masterId, key的第一个字母 | |||
UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额) | |||
AdminRolePermissionCacheTime = 3600 * 24 * 0.5 | |||
KEY_SYS_CFG_CACHE = "sys_cfg_cache" | |||
@@ -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"` | |||
} |
@@ -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"` | |||
} |
@@ -1,7 +1,7 @@ | |||
package md | |||
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:"原-曝光量"` | |||
OriginalEcpm string `json:"original_ecpm" example:"原-广告千次曝光收益(分)"` | |||
NowExposureCount int `json:"now_exposure_count" example:"现-曝光量"` | |||
@@ -13,3 +13,7 @@ type ClacEcpmReq struct { | |||
OriginalEcpm string `json:"original_ecpm" example:"原-广告千次曝光收益(分)"` | |||
GenerateDataId int `json:"generate_data_id" example:"原始数据id"` | |||
} | |||
type SettlementWxAdData struct { | |||
GenerateDataId int `json:"generate_data_id" example:"生成数据id"` | |||
} |
@@ -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 | |||
} |
@@ -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 | |||
} |
@@ -134,13 +134,14 @@ func MediumBindAgentSave(c *gin.Context) { | |||
var tmp = model.AgentWithMedium{ | |||
AgentId: username.AgentId, | |||
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) | |||
e.OutSuc(c, "success", nil) | |||
return | |||
} | |||
func MediumBindAgentDel(c *gin.Context) { | |||
var req md.MediumListDelReq | |||
err := c.ShouldBindJSON(&req) | |||
@@ -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 | |||
} |
@@ -67,10 +67,10 @@ func GetDistributedLockRequestId(prefix string) string { | |||
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) | |||
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true) | |||
if !balanceLockOk { |
@@ -1,6 +1,7 @@ | |||
package svc | |||
import ( | |||
"applet/app/enum" | |||
"applet/app/md" | |||
"applet/app/utils" | |||
db "code.fnuoos.com/zhimeng/model.git/src" | |||
@@ -13,7 +14,7 @@ import ( | |||
func GenerateWxAdData(req md.GenerateWxAdData) (err error, generateWxAdData model.GenerateWxAdData) { | |||
//1、查找原始数据记录 | |||
originalWxAdDataDb := implement.NewOriginalWxAdDataDb(db.Db) | |||
originalWxAdData, err := originalWxAdDataDb.GetOriginalWxAdData(req.GenerateDataId) | |||
originalWxAdData, err := originalWxAdDataDb.GetOriginalWxAdData(req.OriginalDataId) | |||
if err != nil { | |||
return | |||
} | |||
@@ -92,7 +93,7 @@ func GenerateWxAdData(req md.GenerateWxAdData) (err error, generateWxAdData mode | |||
generateWxAdData = model.GenerateWxAdData{ | |||
Uuid: originalWxAdData.Uuid, | |||
AppId: originalWxAdData.AppId, | |||
GenerateDataId: originalWxAdData.Id, | |||
OriginalDataId: originalWxAdData.Id, | |||
SlotId: originalWxAdData.SlotId, | |||
AdSlot: originalWxAdData.AdSlot, | |||
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)) | |||
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() | |||
} |
@@ -5,7 +5,7 @@ go 1.18 | |||
//replace code.fnuoos.com/zhimeng/model.git => E:/company/ad/models | |||
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/boombuler/barcode v1.0.1 | |||
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/qiniu/api.v7/v7 v7.8.2 | |||
github.com/robfig/cron/v3 v3.0.1 | |||
github.com/shopspring/decimal v1.3.1 | |||
github.com/sony/sonyflake v1.0.0 | |||
github.com/swaggo/files v1.0.1 | |||
github.com/swaggo/gin-swagger v1.6.0 | |||