实现网费奖励下发和回调接口

This commit is contained in:
2025-07-05 14:45:02 +08:00
parent 357ada8455
commit 3933d98c4d
17 changed files with 271 additions and 17 deletions

View File

@ -12,7 +12,7 @@ import (
type IRewardV1 interface {
List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error)
ListInternetCharge(ctx context.Context, req *v1.ListInternetChargeReq) (res *v1.ListRes, err error)
ListInternetCharge(ctx context.Context, req *v1.ListInternetChargeReq) (res *v1.ListInternetChargeRes, err error)
Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error)
Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error)
Delete(ctx context.Context, req *v1.DeleteReq) (res *v1.DeleteRes, err error)
@ -23,4 +23,5 @@ type IRewardV1 interface {
GetGoodsDetails(ctx context.Context, req *v1.GetGoodsDetailsReq) (res *v1.GetGoodsDetailsRes, err error)
OperateTaskReward(ctx context.Context, req *v1.OperateTaskRewardReq) (res *v1.OperateTaskRewardRes, err error)
GetUserRewardsCanClaimList(ctx context.Context, req *v1.GetUserRewardsCanClaimListReq) (res *v1.GetUserRewardsCanClaimListRes, err error)
NetfeeCallback(ctx context.Context, req *v1.NetfeeCallbackReq) (res *v1.NetfeeCallbackRes, err error)
}

View File

@ -314,3 +314,11 @@ type GetUserRewardsCanClaimListRes struct {
List interface{} `json:"list"`
Total int `json:"total"`
}
type NetfeeCallbackReq struct {
g.Meta `path:"/reward/netfeeCallback" method:"post" tags:"Reward" summary:"(8圈)回调"`
OrderId string `json:"order_id"`
}
type NetfeeCallbackRes struct {
Success bool `json:"success" dc:"是否成功"`
}

View File

@ -13,3 +13,8 @@ const (
CmdDesktopSetting = 10001 // 桌面组件显示设置
CmdUserFee = 10002 // 用户网费下发
)
const (
UPDataTopic = "/+/up"
DOWNDataTopic = "/%d/down"
)

View File

@ -0,0 +1,5 @@
package consts
const (
NetfeeCode = "internet_fee"
)

View File

@ -0,0 +1,17 @@
package reward
import (
"context"
"server/internal/model"
"server/internal/service"
"server/api/reward/v1"
)
func (c *ControllerV1) NetfeeCallback(ctx context.Context, req *v1.NetfeeCallbackReq) (res *v1.NetfeeCallbackRes, err error) {
out, err := service.Reward().NetfeeCallback(ctx, &model.NetfeeCallbackIn{OrderId: req.OrderId})
if err != nil {
return
}
return &v1.NetfeeCallbackRes{Success: out.Success}, nil
}

View File

@ -34,6 +34,7 @@ type StoreClientSessionsColumns struct {
UpdatedAt string // 更新时间
DeletedAt string // 软删除时间戳
AreaName string //
UserId string // 用户 id
}
// storeClientSessionsColumns holds the columns for the table store_client_sessions.
@ -52,6 +53,7 @@ var storeClientSessionsColumns = StoreClientSessionsColumns{
UpdatedAt: "updated_at",
DeletedAt: "deleted_at",
AreaName: "area_name",
UserId: "user_id",
}
// NewStoreClientSessionsDao creates and returns a new DAO object for table data access.

View File

@ -37,6 +37,7 @@ type UsersColumns struct {
RoleId string // 角色ID
LastLoginStoreId string // 上次登录门店ID
Quan8Uuid string // 8圈使用的 uuid
XyUserId string // 系统唯一用户ID
}
// usersColumns holds the columns for the table users.
@ -58,6 +59,7 @@ var usersColumns = UsersColumns{
RoleId: "role_id",
LastLoginStoreId: "last_login_store_id",
Quan8Uuid: "quan8_uuid",
XyUserId: "xy_user_id",
}
// NewUsersDao creates and returns a new DAO object for table data access.

View File

@ -7,6 +7,7 @@ import (
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/guid"
"server/internal/consts"
"server/internal/dao"
@ -15,6 +16,8 @@ import (
"server/internal/service"
"server/utility/ecode"
"server/utility/gamelife"
"server/utility/mqtt"
"server/utility/mqtt/emqx"
"strconv"
"time"
)
@ -657,7 +660,7 @@ func (s *sReward) GetLift(ctx context.Context, in *model.GetRewardIn) (out *mode
}
}
if in.Source == 1 && in.RewradTypeId == 37 || in.Source == 2 {
if in.Source == 1 && in.RewradTypeId == 37 {
// 增加奖励已领取数量
_, err = dao.Rewards.Ctx(ctx).Where(do.Rewards{Id: in.RewardId}).Increment(dao.Rewards.Columns().ReceivedNum, 1)
if err != nil {
@ -704,6 +707,63 @@ func (s *sReward) GetLift(ctx context.Context, in *model.GetRewardIn) (out *mode
}
} else {
// 门店奖励处理
value, err := dao.RewardTypes.Ctx(ctx).WherePri(in.RewradTypeId).Value()
if err != nil {
return nil, ecode.Fail.Sub("获取奖励类型失败")
}
if value.IsEmpty() {
return nil, ecode.Params.Sub("奖励类型不存在")
}
switch value.String() {
case consts.NetfeeCode:
dao.UserTaskRewards.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
xyUserId, err := dao.Users.Ctx(ctx).WherePri(in.UserId).Fields(dao.Users.Columns().XyUserId).Value()
if err != nil {
return err
}
if xyUserId.IsEmpty() {
return ecode.Params.Sub("该用户暂未绑定8圈账号无法发放网费奖励")
}
// 增加奖励已领取数量
_, err = dao.Rewards.Ctx(ctx).Where(do.Rewards{Id: in.RewardId}).Increment(dao.Rewards.Columns().ReceivedNum, 1)
if err != nil {
return ecode.Fail.Sub("获取奖励领取记录异常")
}
// 修改用户任务奖励记录状态
_, err = dao.UserTaskRewards.Ctx(ctx).Where(do.UserTaskRewards{Id: in.Id}).Data(do.UserTaskRewards{
Status: consts.RewardExchangeStatus,
}).Update()
if err != nil {
return ecode.Fail.Sub("修改用户任务奖励记录状态异常")
}
client, b := mqtt.GetClient("emqx")
if !b {
return ecode.Fail.Sub("获取mqtt客户端异常")
}
downData := emqx.DownData{CMD: consts.CmdUserFee, Data: struct {
XyUserId string `json:"xy_user_id"`
Money int `json:"money"`
Note string `json:"note"`
OrderId string `json:"order_id"`
}{
XyUserId: xyUserId.String(),
Money: in.GrantQuantity,
Note: fmt.Sprintf("用户领取 id 为 %d下发记录 id 为 %d 的网费", in.RewardId, in.Id),
OrderId: gconv.String(in.Id),
}}
marshal, err := json.Marshal(downData)
if err != nil {
return ecode.Fail.Sub("json.Marshal异常")
}
if err = client.Publish(fmt.Sprintf(consts.DOWNDataTopic, in.StoreId), marshal); err != nil {
return ecode.Fail.Sub("Publish异常")
}
return nil
})
}
}
return out, err
}
@ -1115,3 +1175,41 @@ func (s *sReward) GetUserClaimList(ctx context.Context, in *model.GetUserClaimLi
Total: total,
}, nil
}
func (s *sReward) NetfeeCallback(ctx context.Context, in *model.NetfeeCallbackIn) (out *model.NetfeeCallbackOut, err error) {
if err = dao.UserTaskRewards.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
value, err := dao.UserTaskRewards.Ctx(ctx).WherePri(in.OrderId).Fields(dao.UserTaskRewards.Columns().UserTaskId).Value()
if err != nil {
return ecode.Fail.Sub("查询用户任务奖励失败")
}
if value.IsEmpty() {
return ecode.Fail.Sub("查询用户任务奖励失败")
}
count, err := dao.UserTaskRewards.Ctx(ctx).Where(do.UserTaskRewards{UserTaskId: value.Int64()}).WhereIn(dao.UserTaskRewards.Columns().Status, []int{2, 3, 5}).Count()
if err != nil {
return ecode.Fail.Sub("查询用户任务奖励失败")
}
if count == 1 {
// 修改任务记录状态2
_, err = dao.UserTasks.Ctx(ctx).Where(do.UserTasks{Id: value.Int64()}).Data(do.UserTasks{
Status: 2,
}).Update()
if err != nil {
return ecode.Fail.Sub("修改用户任务状态失败")
}
}
if _, err := dao.UserTaskRewards.Ctx(ctx).Data(do.UserTaskRewards{
Status: consts.RewardSuccessStatus,
}).Update(); err != nil {
return ecode.Fail.Sub("修改用户任务奖励状态失败")
}
return nil
}); err != nil {
return nil, err
}
return &model.NetfeeCallbackOut{Success: true}, nil
}

View File

@ -678,6 +678,7 @@ func (s *sTask) GetTaskList(ctx context.Context, in *model.GetTaskListV2In) (out
}
if int(v.UserTimes) >= v.TargetTimes {
completedTime := gtime.Now()
// 判断当前用户完成情况,已完成根据任务、用户,任务类型检查是否存在用户任务记录
orm := dao.UserTasks.Ctx(ctx).Where(do.UserTasks{UserId: in.UserId, TaskId: v.TaskID})
if v.GameTaskConfig.TimeType == 1 {
@ -740,12 +741,17 @@ func (s *sTask) GetTaskList(ctx context.Context, in *model.GetTaskListV2In) (out
// 拼装门店奖励数据
if len(result.TaskList[i].Rewards) > 0 {
for _, reward := range result.TaskList[i].Rewards {
var quantity uint64
quantity, err = CalculateNetfeeRewardQuantity(ctx, in.UserId, in.StoreId, &reward, completedTime)
if err != nil {
quantity = reward.GrantQuantity
}
in := do.UserTaskRewards{
RewardId: reward.Id,
UserTaskId: id,
RewardName: reward.Name,
Status: consts.RewardPendingStatus,
IssueQuantity: reward.GrantQuantity,
IssueQuantity: quantity,
Source: 2,
RewardTypeId: reward.RewardTypeId,
}
@ -935,3 +941,95 @@ func (s *sTask) SyncTaskFromGamelife(ctx context.Context) (out *model.SyncTaskOu
wg.Wait()
return
}
func CalculateNetfeeRewardQuantity(ctx context.Context, userId int64, storeId int64, reward *model.SimpleReward, completedTime *gtime.Time) (uint64, error) {
const rewardTypeCode = consts.NetfeeCode
// 判断是否是门店网费奖励
exist, err := dao.RewardTypes.Ctx(ctx).
WherePri(reward.RewardTypeId).
Where(do.RewardTypes{Code: rewardTypeCode}).
Exist()
if err != nil {
return 0, ecode.Fail.Sub("获取奖励类型失败")
}
if !exist {
// 不是网费奖励,返回当前奖励默认值
return reward.GrantQuantity, nil
}
// 获取当前小时 & 星期几0=周日)
hour := completedTime.Hour()
weekday := int(completedTime.Weekday())
// 获取上机记录
areaLevel, err := dao.StoreClientSessions.Ctx(ctx).
Where(do.StoreClientSessions{UserId: userId}).
WhereLTE(dao.StoreClientSessions.Columns().StartTime, completedTime).
Where("end_time IS NULL OR end_time >= ?", completedTime).
Fields("area_name,level_id").
OrderDesc(dao.StoreClientSessions.Columns().StartTime).
One()
if err != nil {
return 0, ecode.Fail.Sub("获取用户上机记录失败")
}
// 获取会员等级ID内部ID
levelId, err := dao.StoreMemberLevels.Ctx(ctx).
Where(do.StoreMemberLevels{LevelId: areaLevel["level_id"].Int64()}).
Fields("id").
Value()
if err != nil {
return 0, ecode.Fail.Sub("获取会员等级失败")
}
if levelId.IsEmpty() {
return reward.GrantQuantity, nil
}
// 获取区域ID
areaId, err := dao.StoreAreas.Ctx(ctx).
Where(do.StoreAreas{AreaName: areaLevel["area_name"].String()}).
Fields("id").
Value()
if err != nil {
return 0, ecode.Fail.Sub("获取区域失败")
}
if areaId.IsEmpty() {
return reward.GrantQuantity, nil
}
// 获取门店该区域、等级、奖励配置
priceDataStr, err := dao.StoreNetfeeAreaLevel.Ctx(ctx).
Where(do.StoreNetfeeAreaLevel{
StoreId: storeId,
AreaId: areaId.Int(),
MemberLevelId: levelId.Int(),
RewardId: reward.Id,
}).
Fields(dao.StoreNetfeeAreaLevel.Columns().PriceData).
Value()
if err != nil {
return 0, ecode.Fail.Sub("获取网费奖励价格配置失败")
}
// 若配置为空,返回默认值
if priceDataStr.IsEmpty() {
return reward.GrantQuantity, nil
}
// 解析 priceData
var priceData [][]int
if err := json.Unmarshal([]byte(priceDataStr.String()), &priceData); err != nil {
return 0, ecode.Fail.Sub("网费价格配置解析失败")
}
// 防止越界
if weekday >= len(priceData) || hour >= len(priceData[weekday]) {
return 0, ecode.Fail.Sub("网费奖励价格配置不完整")
}
grant := uint64(priceData[weekday][hour])
glog.Infof(ctx, "网费奖励金额为 %d来源于门店配置", grant)
return grant, nil
}

View File

@ -26,4 +26,5 @@ type StoreClientSessions struct {
UpdatedAt *gtime.Time // 更新时间
DeletedAt *gtime.Time // 软删除时间戳
AreaName interface{} //
UserId interface{} // 用户 id
}

View File

@ -29,4 +29,5 @@ type Users struct {
RoleId interface{} // 角色ID
LastLoginStoreId interface{} // 上次登录门店ID
Quan8Uuid interface{} // 8圈使用的 uuid
XyUserId interface{} // 系统唯一用户ID
}

View File

@ -24,4 +24,5 @@ type StoreClientSessions struct {
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳
AreaName string `json:"areaName" orm:"area_name" description:""` //
UserId int `json:"userId" orm:"user_id" description:"用户 id"` // 用户 id
}

View File

@ -27,4 +27,5 @@ type Users struct {
RoleId int64 `json:"roleId" orm:"role_id" description:"角色ID"` // 角色ID
LastLoginStoreId int64 `json:"lastLoginStoreId" orm:"last_login_store_id" description:"上次登录门店ID"` // 上次登录门店ID
Quan8Uuid string `json:"quan8Uuid" orm:"quan8_uuid" description:"8圈使用的 uuid"` // 8圈使用的 uuid
XyUserId string `json:"xyUserId" orm:"xy_user_id" description:"系统唯一用户ID"` // 系统唯一用户ID
}

View File

@ -209,6 +209,8 @@ type GetRewardIn struct {
BindType int
UserTaskId int
UserId int
GrantQuantity int
StoreId int
}
type GetRewardOut struct {
//List []GetRewardNewOut `json:"list"`

View File

@ -44,3 +44,9 @@ type UserClaimReward struct {
RewardName string `json:"rewardName" orm:"reward_name"`
SimpleReward SimpleReward `json:"reward" orm:"with:id=reward_id"`
}
type NetfeeCallbackIn struct {
OrderId string
}
type NetfeeCallbackOut struct {
Success bool
}

View File

@ -337,6 +337,7 @@ type (
// }
CallBack(ctx context.Context, in *model.RewardCallbackIn) (out *model.RewardCallbackOut, err error)
GetUserClaimList(ctx context.Context, in *model.GetUserClaimListIn) (out *model.GetUserClaimListOut, err error)
NetfeeCallback(ctx context.Context, in *model.NetfeeCallbackIn) (out *model.NetfeeCallbackOut, err error)
}
)

View File

@ -189,11 +189,16 @@ func saveOrUpdateClientSession(ctx context.Context, storeId int, session ClientS
}
clientId := client["id"].Int64()
areaId := client["area_id"].Int64()
value, err := dao.Users.Ctx(ctx).Where(do.Users{XyUserId: session.XyUserId}).Fields("id").Value()
if err != nil || value.IsEmpty() {
glog.Errorf(ctx, "未找到用户信息xy_user_id=%s", session.XyUserId)
return
}
// 上机逻辑(没有 end_time
if session.EndTime == "" {
// 插入上机记录
_, err := dao.StoreClientSessions.Ctx(ctx).Data(do.StoreClientSessions{
UserId: value.Int64(),
StoreId: int64(storeId),
ClientId: clientId,
AreaId: areaId,