Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
package taskser
import (
"time"
"91porn-server/models/v/dailytaskmod"
"91porn-server/models/v/oncetaskmod"
"91porn-server/models/v/prizemod"
"91porn-server/models/v/taskmod"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type TaskStatus int
const (
UnDone TaskStatus = iota + 1 // 未完成
Finish // 已完成
Receive // 已领取
ReDo // 重做
)
// 签到信息返回
type SignDetailsResponse struct {
List []Config `json:"list"` // 列表
Value int64 `json:"value"` // 累计签到天数
}
type Config struct {
ID primitive.ObjectID `json:"id"` // 文档ID
Title string `json:"title"` // 任务标题
Desc string `json:"desc"` // 任务描述
Prizes []prizemod.Prize `json:"prizes"` // 奖品列表
FinishCondition int64 `json:"finishCondition"` // 达成条件
FinishValue int64 `json:"finishValue"` // 完成数量
Status TaskStatus `json:"status"` // 任务状态 1:未完成 2:已完成 3:已领取
BoonType taskmod.ConfigType `json:"boonType"` // 福利类型
IsToday bool `json:"isToday"` // 是否是今日任务
}
type TaskResponse struct {
JewelBoxDetails struct {
List []Config `json:"list"` // 列表
Value int64 `json:"value"` // 累计活跃值
TotalValue int64 `json:"totalValue"` // 总活跃值
} `json:"jewelBoxDetails"` // 宝箱详情
TaskList []Config `json:"taskList"` // 任务列表
}
type TaskDetailsResponse struct {
TaskList []Config `json:"taskList"` // 任务列表
Value int64 `json:"value"` // 累计金币/充值金额
}
type DailyTaskResponse struct {
ID primitive.ObjectID `json:"id"` // 任务id
Title string `json:"title"` // 任务标题
Img string `json:"img"` // 任务图标
Type dailytaskmod.DailyTaskTypeEnum `json:"type"` // 0 每日广告点击; 1 每日邀请
Desc string `json:"desc"` // 任务说明
Link string `json:"link"` // 跳转地址
Status int `json:"status"` // 0 未完成; 1 已完成未领取; 2 已领取
StartAt time.Time `json:"startAt"` // 场次开始时间;CountdownType=1 时为红包雨场次时间,否则零值
EndAt time.Time `json:"endAt"` // 场次结束时间;CountdownType=1 时为红包雨场次时间,否则零值
CountdownType int `json:"countdownType"` // 倒计时类型 0-无 1-红包雨倒计时
}
type DailyTaskResponseDetail struct {
Prizes []*prizemod.Prize `json:"prizes"` // 奖品列表, 与签到相同
FinishCondition uint64 `json:"finishCondition"` // 达成条件. 比如点击广告任务中, 表示3次点击才可以领取奖励
FinishCount uint64 `json:"finishCount"` // 已完成数量. 比如比如点击广告任务中 2表示已经点击2次广告
Status int `json:"status"` // 0 未完成; 1 已完成未领取; 2 已领取
}
type OnceTaskResponse struct {
ID primitive.ObjectID `json:"id"` // 任务id
Title string `json:"title"` // 任务标题
Img string `json:"img"` // 任务图标
Type oncetaskmod.OnceTaskTypeEnum `json:"type"` // 0 每日广告点击; 1 每日邀请
Prizes []*prizemod.Prize `json:"prizes"` // 奖品列表, 与签到相同
Desc string `json:"desc"` // 任务说明
Link string `json:"link"` // 跳转地址
Status int `json:"status"` // 0 未完成; 1 已完成未领取; 2 已领取
StartAt time.Time `json:"startAt"` // 场次开始时间;CountdownType=1 时为红包雨场次时间,否则零值
EndAt time.Time `json:"endAt"` // 场次结束时间;CountdownType=1 时为红包雨场次时间,否则零值
CountdownType int `json:"countdownType"` // 倒计时类型 0-无 1-红包雨倒计时
}
type NewTaskResponse struct {
DailyTasks []DailyTaskResponse `json:"dailyTask"`
OnceTasks []OnceTaskResponse `json:"onceTask"`
GrowthTasks []*GrowthTaskResponse `json:"growthTasks"`
}
type GrowthTaskResponse struct {
ID primitive.ObjectID `json:"id"` // 任务id
Title string `json:"title"` // 任务标题
Img string `json:"img"` // 任务图标
Type int `json:"type" bson:"type"` // 配置类型 1-购买金币
SubTitle string `json:"subTitle"` // 子标题
Status int `json:"status"` // 0 未完成; 1 已完成未领取; 2 已领取
}
+433
View File
@@ -0,0 +1,433 @@
package taskser
import (
"91porn-server/app/appg"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/stderr"
"91porn-server/common/timeutil"
"91porn-server/models/v/prizemod"
"91porn-server/models/v/signtaskmod"
"91porn-server/models/v/taskrecordmod"
"91porn-server/models/v/txnmod"
"91porn-server/models/v/usermod"
"encoding/json"
"sort"
"strconv"
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// 签到
func Sign(uid uint64, configID string) (code stderr.Code) {
now := time.Now()
id, err := primitive.ObjectIDFromHex(configID)
if err != nil {
return stderr.ErrParamError
}
// 获取用户信息
u, err := usermod.FindUserByUID(uid)
if err != nil || u == nil {
code = stderr.UserIsNotExists
return
}
// 获取配置列表
signConfig, err := signtaskmod.GetConfigList()
if err != nil {
return stderr.ErrDbQueryError
}
// 查询当前签到配置
configInfo, err := signtaskmod.GetConfigByID(id)
if err != nil {
return stderr.ErrDbQueryError
}
// 异常判定 未配置签到福利/签到福利类型不正确
if configInfo == nil || signConfig == nil || len(signConfig) == 0 {
log.Info("未配置签到福利")
return stderr.ErrInvalidRequest
}
// 查询签到信息
signRecordMap := make(map[string]bool, 0)
taskInfo, err := taskrecordmod.GetSignRecord(uid)
if err != nil {
return stderr.ErrDbQueryError
}
if taskInfo == nil {
log.Info("未查询到签到任务")
return stderr.ErrDbQueryError
}
for _, taskId := range taskInfo.SignRecord {
signRecordMap[taskId.Hex()] = true
}
// 判断是否重复签到
if _, ok := signRecordMap[configID]; ok {
log.Info("已签到")
return stderr.HasSigned
}
// 计算相差多少天
duration := timeutil.BeginningOfDay(now).Sub(taskInfo.SignTime)
diffDays := int64(duration.Hours() / 24)
// 判断此签到是否是当天签到
if configInfo.FinishCondition-1 != diffDays {
log.Info("条件不匹配")
return stderr.ErrInvalidRequest
}
// 获取奖品列表
prizes, err := prizemod.GetPrizeListByIDs(configInfo.Prizes)
if err != nil {
return stderr.ErrDbQueryError
}
// 获取额外奖品列表
signCount := len(signRecordMap)
for _, item := range signConfig {
if item.FinishCondition == int64(signCount)+1 {
extraPrizeIds, e := prizemod.GetPrizeListByIDs(item.ExtraPrizes)
if e != nil {
continue
}
prizes = append(prizes, extraPrizeIds...)
}
}
// 更新签到记录(事务)
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
if err = taskrecordmod.TaskSignUpdate(t, uid, id); err != nil {
return err
}
return nil
}); err != nil {
return stderr.ErrDbTransError
}
// 发放奖励
for _, v := range prizes {
for i := 0; i < int(v.Count); i++ {
handler, err := prizemod.Run(uid, *v, txnmod.SignBoon)
if err != nil {
log.Error("签到发放奖励handler失败:", log.Any("err", err))
continue
}
err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
err = handler.Run(t)
if err != nil {
return err
}
err = txnmod.InsertManyTransactionLog(t, handler.GetTransactionLog())
if err != nil {
return err
}
return nil
})
if err != nil {
log.Error("签到发放奖励事务失败:", log.Any("err", err))
continue
}
}
}
return stderr.Success
}
// 补签
func ReSign(uid uint64, configID string) (code stderr.Code) {
now := time.Now()
redisKey := "task::resign::" + strconv.FormatUint(uid, 10)
if appg.Redis.Exists(redisKey) {
return stderr.ExceededTimes
}
id, err := primitive.ObjectIDFromHex(configID)
if err != nil {
return stderr.ErrParamError
}
// 获取用户信息
u, err := usermod.FindUserByUID(uid)
if err != nil || u == nil {
code = stderr.UserIsNotExists
return
}
// 获取配置列表
signConfig, err := signtaskmod.GetConfigList()
if err != nil {
return stderr.ErrDbQueryError
}
// 查询当前签到配置
configInfo, err := signtaskmod.GetConfigByID(id)
if err != nil {
return stderr.ErrDbQueryError
}
// 异常判定 未配置签到福利/签到福利类型不正确
if configInfo == nil || signConfig == nil || len(signConfig) == 0 {
log.Info("未配置签到福利")
return stderr.ErrInvalidRequest
}
//查询签到信息
signRecordMap := make(map[string]bool, 0)
taskInfo, err := taskrecordmod.GetSignRecord(uid)
if err != nil {
return stderr.ErrDbQueryError
}
if taskInfo == nil {
log.Info("未查询到签到任务")
return stderr.ErrDbQueryError
}
for _, taskId := range taskInfo.SignRecord {
signRecordMap[taskId.Hex()] = true
}
// 判断是否重复签到
if _, ok := signRecordMap[configID]; ok {
log.Info("已签到")
return stderr.HasSigned
}
// 计算相差多少天
duration := timeutil.BeginningOfDay(now).Sub(taskInfo.SignTime)
diffDays := int64(duration.Hours() / 24)
// 判断是否在补签范围
if configInfo.FinishCondition > diffDays {
log.Info("条件不匹配")
return stderr.ErrInvalidRequest
}
// redis作为倒计时(一天只能补签一次)
seconds := int64(timeutil.BeginningOfTomorrow(now).Sub(now).Seconds())
err = appg.Redis.Set(redisKey, configID, time.Duration(seconds)*time.Second)
if err != nil {
log.Error(err.Error())
}
return stderr.Success
}
// 补签回调
func ReSignCallBack(uid uint64) (code stderr.Code) {
now := time.Now()
redisKey := "task::resign::" + strconv.FormatUint(uid, 10)
if !appg.Redis.Exists(redisKey) {
return stderr.ErrParamError
}
configID, err := appg.Redis.Get(redisKey)
if err != nil {
log.Error("redis缓存不存在!")
return stderr.ErrParamError
}
if configID == nil {
log.Error("签到配置不存在!")
return stderr.ErrParamError
}
id, err := primitive.ObjectIDFromHex(*configID)
if err != nil {
return stderr.ErrParamError
}
// 获取用户信息
u, err := usermod.FindUserByUID(uid)
if err != nil || u == nil {
code = stderr.UserIsNotExists
return
}
// 获取配置列表
signConfig, err := signtaskmod.GetConfigList()
if err != nil {
return stderr.ErrDbQueryError
}
// 查询当前签到配置
configInfo, err := signtaskmod.GetConfigByID(id)
if err != nil {
return stderr.ErrDbQueryError
}
// 异常判定 未配置签到福利/签到福利类型不正确
if configInfo == nil || signConfig == nil || len(signConfig) == 0 {
log.Info("未配置签到福利")
return stderr.ErrInvalidRequest
}
//查询签到信息
signRecordMap := make(map[string]bool, 0)
taskInfo, err := taskrecordmod.GetSignRecord(uid)
if err != nil {
return stderr.ErrDbQueryError
}
if taskInfo == nil {
log.Info("未查询到签到任务")
return stderr.ErrDbQueryError
}
for _, taskId := range taskInfo.SignRecord {
signRecordMap[taskId.Hex()] = true
}
// 判断是否重复签到
if _, ok := signRecordMap[*configID]; ok {
log.Info("已签到")
return stderr.HasSigned
}
// 计算相差多少天
duration := timeutil.BeginningOfDay(now).Sub(taskInfo.SignTime)
diffDays := int64(duration.Hours() / 24)
// 判断是否在补签范围
if configInfo.FinishCondition > diffDays {
log.Info("条件不匹配")
return stderr.ErrInvalidRequest
}
// 获取奖品列表
prizes, err := prizemod.GetPrizeListByIDs(configInfo.Prizes)
if err != nil {
return stderr.ErrDbQueryError
}
// 获取额外奖品列表
signCount := len(signRecordMap)
for _, item := range signConfig {
if item.FinishCondition == int64(signCount) {
extraPrizeIds, e := prizemod.GetPrizeListByIDs(item.ExtraPrizes)
if e != nil {
continue
}
prizes = append(prizes, extraPrizeIds...)
}
}
// 更新签到记录(事务)
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
if err = taskrecordmod.TaskSignUpdate(t, uid, id); err != nil {
return err
}
return nil
}); err != nil {
return stderr.ErrDbTransError
}
// 发放奖励
for _, v := range prizes {
for i := 0; i < int(v.Count); i++ {
handler, err := prizemod.Run(uid, *v, txnmod.SignBoon)
if err != nil {
log.Error("签到发放奖励handler失败:", log.Any("err", err))
continue
}
err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
err = handler.Run(t)
if err != nil {
return err
}
err = txnmod.InsertManyTransactionLog(t, handler.GetTransactionLog())
if err != nil {
return err
}
return nil
})
if err != nil {
log.Error("签到发放奖励事务失败:", log.Any("err", err))
continue
}
}
}
return stderr.Success
}
// 获取签到额外奖励
func SignExtraPrizes(uid uint64) (out []map[string]interface{}, code stderr.Code) {
out = make([]map[string]interface{}, 0)
taskInfo, err := taskrecordmod.GetSignRecord(uid)
if err != nil {
return out, stderr.ErrDbQueryError
}
signRecords := make([]primitive.ObjectID, 0)
if taskInfo != nil {
signRecords = taskInfo.SignRecord
}
signConfig, err := signtaskmod.GetConfigList()
if err != nil {
return out, stderr.ErrDbQueryError
}
// 获取奖品列表
prizes, err := prizemod.GetPrizeList()
for _, item := range signConfig {
if len(item.ExtraPrizes) == 0 {
continue
}
extraPrizes := getPrizeList(item.ExtraPrizes, prizes)
for _, prize := range extraPrizes {
b, _ := json.Marshal(&prize)
var extraPrizeMap map[string]interface{}
_ = json.Unmarshal(b, &extraPrizeMap)
days := item.FinishCondition - int64(len(signRecords))
if days < 0 {
days = 0
}
extraPrizeMap["finishCondition"] = days
// 设置额外奖励领取状态
if len(signRecords) >= int(item.FinishCondition) {
extraPrizeMap["extraPrizeStatus"] = true
} else {
extraPrizeMap["extraPrizeStatus"] = false
}
out = append(out, extraPrizeMap)
}
}
sort.Slice(out, func(i, j int) bool {
valI := out[i]["extraPrizeStatus"].(bool)
valJ := out[j]["extraPrizeStatus"].(bool)
return !valI && valJ
})
return out, stderr.Success
}
// 获取签到详情
func GetSignDetails(uid uint64) (out SignDetailsResponse, code stderr.Code) {
now := time.Now()
// 获取配置列表
signConfig, err := signtaskmod.GetConfigList()
if err != nil {
return out, stderr.ErrDbQueryError
}
// 查询用户签到信息
signRecordMap := make(map[string]bool, 0)
taskInfo, err := taskrecordmod.GetSignRecord(uid)
if err != nil {
return out, stderr.ErrDbQueryError
}
// 新增用户任务记录
if taskInfo == nil {
taskInfo, err = taskrecordmod.TaskSignInit(uid)
if err != nil {
return out, stderr.ErrDbQueryError
}
}
// 计算相差多少天
diffDays := int(timeutil.BeginningOfDay(now).Sub(taskInfo.SignTime).Hours() / 24)
// 判断是否重置数据
if diffDays >= len(signConfig) {
taskInfo, err = taskrecordmod.TaskSignReset(uid)
if err != nil {
return out, stderr.ErrDbQueryError
}
}
// 构建签到记录map
for _, item := range taskInfo.SignRecord {
signRecordMap[item.String()] = true
}
// 获取奖品列表
prizes, err := prizemod.GetPrizeList()
if err != nil {
return out, stderr.ErrDbQueryError
}
out.Value = int64(len(taskInfo.SignRecord))
out.List = make([]Config, len(signConfig))
for i, v := range signConfig {
taskConfig := Config{
ID: v.ID,
Prizes: getPrizeList(v.Prizes, prizes),
FinishCondition: v.FinishCondition,
Title: v.Title,
Desc: v.Desc,
Status: UnDone,
}
// 存在表示已经签到,不存在则需要判断是否是补签
if _, ok := signRecordMap[v.ID.String()]; ok {
// 如果存在,标记为已完成
taskConfig.Status = Finish
} else {
diffDays = int(timeutil.BeginningOfDay(now).Sub(taskInfo.SignTime).Hours() / 24)
if i < diffDays {
taskConfig.Status = ReDo
}
}
out.List[i] = taskConfig
}
// 当日任务标识
if diffDays >= 0 && diffDays < len(out.List) {
out.List[diffDays].IsToday = true
}
return out, stderr.Success
}
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
package taskser
import (
"91porn-server/models/v/prizemod"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// 获取奖品详情列表 通过奖品ID列表
func getPrizeList(ids []primitive.ObjectID, mPrize map[primitive.ObjectID]prizemod.Prize) []prizemod.Prize {
out := make([]prizemod.Prize, 0, len(ids))
for _, v := range ids {
item, ok := mPrize[v]
if !ok {
continue
}
out = append(out, item)
}
return out
}