diff --git a/app/enum/enum_agent.go b/app/enum/enum_agent.go new file mode 100644 index 0000000..6167dea --- /dev/null +++ b/app/enum/enum_agent.go @@ -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 "未知" + } +} diff --git a/app/enum/enum_agent_settlement.go b/app/enum/enum_agent_settlement.go new file mode 100644 index 0000000..e992be6 --- /dev/null +++ b/app/enum/enum_agent_settlement.go @@ -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 "未知" + } +} diff --git a/app/enum/enum_agent_settlement_with_flow.go b/app/enum/enum_agent_settlement_with_flow.go new file mode 100644 index 0000000..1c70b14 --- /dev/null +++ b/app/enum/enum_agent_settlement_with_flow.go @@ -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 "未知" + } +} diff --git a/app/enum/enum_medium.go b/app/enum/enum_medium.go new file mode 100644 index 0000000..c46f858 --- /dev/null +++ b/app/enum/enum_medium.go @@ -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 "未知" + } +} diff --git a/app/enum/enum_medium_settlement.go b/app/enum/enum_medium_settlement.go new file mode 100644 index 0000000..a9334b7 --- /dev/null +++ b/app/enum/enum_medium_settlement.go @@ -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 "未知" + } +} diff --git a/app/enum/enum_medium_settlement_with_flow.go b/app/enum/enum_medium_settlement_with_flow.go new file mode 100644 index 0000000..a93b030 --- /dev/null +++ b/app/enum/enum_medium_settlement_with_flow.go @@ -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 "未知" + } +} diff --git a/app/md/app_redis_key.go b/app/md/app_redis_key.go index ef19b51..2532d5d 100644 --- a/app/md/app_redis_key.go +++ b/app/md/app_redis_key.go @@ -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" diff --git a/app/md/fin_agent_flow.go b/app/md/fin_agent_flow.go new file mode 100644 index 0000000..e9f13d3 --- /dev/null +++ b/app/md/fin_agent_flow.go @@ -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"` +} diff --git a/app/md/fin_medium_flow.go b/app/md/fin_medium_flow.go new file mode 100644 index 0000000..36e1efb --- /dev/null +++ b/app/md/fin_medium_flow.go @@ -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"` +} diff --git a/app/md/md_generate_wx_ad_data.go b/app/md/md_generate_wx_ad_data.go index fc56bdd..370ba31 100644 --- a/app/md/md_generate_wx_ad_data.go +++ b/app/md/md_generate_wx_ad_data.go @@ -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"` +} diff --git a/app/svc/svc_deal_agent_amount.go b/app/svc/svc_deal_agent_amount.go new file mode 100644 index 0000000..227e670 --- /dev/null +++ b/app/svc/svc_deal_agent_amount.go @@ -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 +} diff --git a/app/svc/svc_deal_medium_amount.go b/app/svc/svc_deal_medium_amount.go new file mode 100644 index 0000000..7b0902f --- /dev/null +++ b/app/svc/svc_deal_medium_amount.go @@ -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 +} diff --git a/app/svc/svc_medium_agent.go b/app/svc/svc_medium_agent.go index e85d0de..949e8d2 100644 --- a/app/svc/svc_medium_agent.go +++ b/app/svc/svc_medium_agent.go @@ -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) diff --git a/app/svc/svc_redis_mutex_lock_for_agent.go b/app/svc/svc_redis_mutex_lock_for_agent.go new file mode 100644 index 0000000..b36785a --- /dev/null +++ b/app/svc/svc_redis_mutex_lock_for_agent.go @@ -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 +} diff --git a/app/svc/svc_redis_mutex_lock.go b/app/svc/svc_redis_mutex_lock_for_medium.go similarity index 85% rename from app/svc/svc_redis_mutex_lock.go rename to app/svc/svc_redis_mutex_lock_for_medium.go index 396e15c..e7c1731 100644 --- a/app/svc/svc_redis_mutex_lock.go +++ b/app/svc/svc_redis_mutex_lock_for_medium.go @@ -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 { diff --git a/app/svc/svc_wx_data.go b/app/svc/svc_wx_data.go index 2dea3ad..3da6063 100644 --- a/app/svc/svc_wx_data.go +++ b/app/svc/svc_wx_data.go @@ -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() +} diff --git a/go.mod b/go.mod index 34be2df..dfea3cf 100644 --- a/go.mod +++ b/go.mod @@ -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