@@ -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的第一个字母 | 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" | ||||
@@ -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 | 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"` | |||||
} |
@@ -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{ | 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) | ||||
@@ -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)) | 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 { |
@@ -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() | |||||
} |
@@ -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 | ||||