| @@ -412,3 +412,18 @@ func GetUnTransformedActions() ([]*Action, error) { | |||
| Find(&actions) | |||
| return actions, err | |||
| } | |||
| func GetActionByIds(ids []int64) ([]*Action, error) { | |||
| if len(ids) == 0 { | |||
| return nil, nil | |||
| } | |||
| actions := make([]*Action, 0) | |||
| err := x.In("id", ids).Find(&actions) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if err := ActionList(actions).LoadAttributes(); err != nil { | |||
| return nil, fmt.Errorf("ActionList loadAttributes: %v", err) | |||
| } | |||
| return actions, nil | |||
| } | |||
| @@ -168,6 +168,72 @@ type Cloudbrain struct { | |||
| EndTime timeutil.TimeStamp | |||
| } | |||
| type CloudbrainShow struct { | |||
| JobID string `xorm:"INDEX NOT NULL"` | |||
| JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"` | |||
| JobName string | |||
| DisplayJobName string | |||
| Status string | |||
| UserID int64 `xorm:"INDEX NOT NULL"` | |||
| RepoID int64 `xorm:"INDEX NOT NULL"` | |||
| SubTaskName string | |||
| ContainerID string | |||
| ContainerIp string | |||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX"` | |||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
| Duration int64 `xorm:"DEFAULT 0"` //运行时长 单位秒 | |||
| TrainJobDuration string `xorm:"DEFAULT '00:00:00'"` | |||
| Image string //镜像名称 | |||
| GpuQueue string //GPU类型即GPU队列 | |||
| ResourceSpecId int //GPU规格id | |||
| DeletedAt time.Time `xorm:"deleted"` | |||
| CanDebug bool `xorm:"-"` | |||
| CanDel bool `xorm:"-"` | |||
| CanModify bool `xorm:"-"` | |||
| Type int | |||
| BenchmarkTypeID int | |||
| BenchmarkChildTypeID int | |||
| VersionID int64 //版本id | |||
| VersionName string `xorm:"INDEX"` //当前版本 | |||
| Uuid string //数据集id | |||
| DatasetName string | |||
| VersionCount int //任务的当前版本数量,不包括删除的 | |||
| IsLatestVersion string //是否是最新版本,1是,0否 | |||
| CommitID string //提交的仓库代码id | |||
| PreVersionName string //父版本名称 | |||
| ComputeResource string //计算资源,例如npu | |||
| EngineID int64 //引擎id | |||
| TrainUrl string //输出模型的obs路径 | |||
| BranchName string //分支名称 | |||
| Parameters string //传给modelarts的param参数 | |||
| BootFile string //启动文件 | |||
| DataUrl string //数据集的obs路径 | |||
| LogUrl string //日志输出的obs路径 | |||
| PreVersionId int64 //父版本的版本id | |||
| FlavorCode string //modelarts上的规格id | |||
| Description string `xorm:"varchar(256)"` //描述 | |||
| WorkServerNumber int //节点数 | |||
| FlavorName string //规格名称 | |||
| EngineName string //引擎名称 | |||
| TotalVersionCount int //任务的所有版本数量,包括删除的 | |||
| LabelName string //标签名称 | |||
| ModelName string //模型名称 | |||
| ModelVersion string //模型版本 | |||
| CkptName string //权重文件名称 | |||
| ResultUrl string //推理结果的obs路径 | |||
| User *User `xorm:"-"` | |||
| Repo *Repository `xorm:"-"` | |||
| BenchmarkType string `xorm:"-"` //算法评测,模型评测 | |||
| BenchmarkTypeName string `xorm:"-"` | |||
| BenchmarkTypeRankLink string `xorm:"-"` | |||
| StartTime timeutil.TimeStamp | |||
| EndTime timeutil.TimeStamp | |||
| } | |||
| func (task *Cloudbrain) ComputeAndSetDuration() { | |||
| var d int64 | |||
| if task.StartTime == 0 { | |||
| @@ -1844,9 +1910,51 @@ func CloudbrainAllStatic(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, er | |||
| func GetStartedCloudbrainTaskByUpdatedUnix(startTime, endTime time.Time) ([]Cloudbrain, error) { | |||
| r := make([]Cloudbrain, 0) | |||
| err := x.Where("updated_unix >= ? and updated_unix <= ? and start_time > 0", startTime.Unix(), endTime.Unix()).Find(&r) | |||
| err := x.Where("updated_unix >= ? and updated_unix <= ? and start_time > 0", startTime.Unix(), endTime.Unix()).Unscoped().Find(&r) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return r, nil | |||
| } | |||
| func GetCloudbrainByIds(ids []int64) ([]Cloudbrain, error) { | |||
| if len(ids) == 0 { | |||
| return nil, nil | |||
| } | |||
| cloudbrains := make([]Cloudbrain, 0) | |||
| err := x.In("id", ids).Unscoped().Find(&cloudbrains) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return cloudbrains, nil | |||
| } | |||
| var ( | |||
| DebugResourceSpecs *ResourceSpecs | |||
| TrainResourceSpecs *ResourceSpecs | |||
| ) | |||
| func GetResourceSpec(jobType string, resourceSpecId int) *ResourceSpec { | |||
| if jobType == string(JobTypeTrain) { | |||
| if TrainResourceSpecs == nil { | |||
| json.Unmarshal([]byte(setting.TrainResourceSpecs), &TrainResourceSpecs) | |||
| } | |||
| for _, spec := range TrainResourceSpecs.ResourceSpec { | |||
| if resourceSpecId == spec.Id { | |||
| return spec | |||
| } | |||
| } | |||
| } else { | |||
| if DebugResourceSpecs == nil { | |||
| json.Unmarshal([]byte(setting.ResourceSpecs), &DebugResourceSpecs) | |||
| } | |||
| for _, spec := range DebugResourceSpecs.ResourceSpec { | |||
| if resourceSpecId == spec.Id { | |||
| return spec | |||
| } | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| @@ -2,6 +2,7 @@ package models | |||
| import ( | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| "strings" | |||
| ) | |||
| const ( | |||
| @@ -44,3 +45,17 @@ func UpdateRewardAdminLogStatus(logId string, oldStatus, newStatus int) error { | |||
| } | |||
| return nil | |||
| } | |||
| func GetRewardAdminLogByLogIds(logIds []string) ([]RewardAdminLog, error) { | |||
| if len(logIds) == 0 { | |||
| return nil, nil | |||
| } | |||
| adminLogs := make([]RewardAdminLog, 0) | |||
| err := x.SQL("select rdl.id,rdl.log_id,rdl.amount,rdl.reward_type,rdl.remark,rdl.status,rdl.target_user_id,rdl.creator_id,u.name as creator_name "+ | |||
| "from reward_admin_log rdl left join public.user u on rdl.creator_id = u.id "+ | |||
| "where rdl.log_id in (?)", strings.Join(logIds, ",")).Find(&adminLogs) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return adminLogs, nil | |||
| } | |||
| @@ -2,6 +2,8 @@ package models | |||
| import ( | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| "fmt" | |||
| "strconv" | |||
| "strings" | |||
| "xorm.io/builder" | |||
| ) | |||
| @@ -112,21 +114,120 @@ const ( | |||
| RewardOrderByIDDesc RewardOperateOrderBy = "id desc" | |||
| ) | |||
| type RewardRecordList []*RewardOperateRecord | |||
| type RewardRecordShowList []*RewardOperateRecordShow | |||
| func (l *RewardRecordList) ToShow() (RewardRecordShowList, error) { | |||
| actionMap, err := l.GetRewardRecordAction() | |||
| adminLogMap, err := l.GetRewardRecordAdminLog() | |||
| CloudbrainMap, err := l.GetRewardRecordCloudbrainTask() | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| result := make([]*RewardOperateRecordShow, 0) | |||
| for _, v := range *l { | |||
| temp := v.ToShow() | |||
| switch v.SourceType { | |||
| case SourceTypeAccomplishTask.Name(): | |||
| temp.Action = actionMap[v.SourceId] | |||
| case SourceTypeAdminOperate.Name(): | |||
| temp.AdminLog = adminLogMap[v.SourceId] | |||
| case SourceTypeRunCloudbrainTask.Name(): | |||
| temp.Cloudbrain = CloudbrainMap[v.SourceId] | |||
| } | |||
| result = append(result, &temp) | |||
| } | |||
| return result, nil | |||
| } | |||
| func (l *RewardRecordList) GetRewardRecordAction() (map[string]Action, error) { | |||
| if len(*l) == 0 { | |||
| return nil, nil | |||
| } | |||
| actionIds := make([]int64, 0) | |||
| for _, r := range *l { | |||
| if r.SourceType != SourceTypeAccomplishTask.Name() { | |||
| continue | |||
| } | |||
| i, _ := strconv.ParseInt(r.SourceId, 10, 64) | |||
| actionIds = append(actionIds, i) | |||
| } | |||
| actions, err := GetActionByIds(actionIds) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| result := make(map[string]Action, 0) | |||
| for _, v := range actions { | |||
| result[fmt.Sprint(v.ID)] = *v | |||
| } | |||
| return result, nil | |||
| } | |||
| func (l *RewardRecordList) GetRewardRecordAdminLog() (map[string]RewardAdminLog, error) { | |||
| if len(*l) == 0 { | |||
| return nil, nil | |||
| } | |||
| logIds := make([]string, 0) | |||
| for _, r := range *l { | |||
| if r.SourceType != SourceTypeAdminOperate.Name() { | |||
| continue | |||
| } | |||
| logIds = append(logIds, r.SourceId) | |||
| } | |||
| logs, err := GetRewardAdminLogByLogIds(logIds) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| result := make(map[string]RewardAdminLog, 0) | |||
| for _, v := range logs { | |||
| result[fmt.Sprint(v.LogId)] = v | |||
| } | |||
| return result, nil | |||
| } | |||
| func (l *RewardRecordList) GetRewardRecordCloudbrainTask() (map[string]Cloudbrain, error) { | |||
| if len(*l) == 0 { | |||
| return nil, nil | |||
| } | |||
| cloudbrainIds := make([]int64, 0) | |||
| for _, r := range *l { | |||
| if r.SourceType != SourceTypeRunCloudbrainTask.Name() { | |||
| continue | |||
| } | |||
| i, _ := strconv.ParseInt(r.SourceId, 10, 64) | |||
| cloudbrainIds = append(cloudbrainIds, i) | |||
| } | |||
| cloudbrains, err := GetCloudbrainByIds(cloudbrainIds) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| result := make(map[string]Cloudbrain, 0) | |||
| for _, v := range cloudbrains { | |||
| result[fmt.Sprint(v.ID)] = v | |||
| } | |||
| return result, nil | |||
| } | |||
| type RewardOperateRecord struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| SerialNo string `xorm:"INDEX NOT NULL"` | |||
| UserId int64 `xorm:"INDEX NOT NULL"` | |||
| Amount int64 `xorm:"NOT NULL"` | |||
| Tittle string | |||
| RewardType string `xorm:"NOT NULL"` | |||
| SourceType string `xorm:"NOT NULL"` | |||
| SourceId string `xorm:"INDEX NOT NULL"` | |||
| RequestId string `xorm:"INDEX NOT NULL"` | |||
| OperateType string `xorm:"NOT NULL"` | |||
| Status string `xorm:"NOT NULL"` | |||
| Remark string | |||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
| ID int64 `xorm:"pk autoincr"` | |||
| SerialNo string `xorm:"INDEX NOT NULL"` | |||
| UserId int64 `xorm:"INDEX NOT NULL"` | |||
| Amount int64 `xorm:"NOT NULL"` | |||
| Tittle string | |||
| RewardType string `xorm:"NOT NULL"` | |||
| SourceType string `xorm:"NOT NULL"` | |||
| SourceId string `xorm:"INDEX NOT NULL"` | |||
| RequestId string `xorm:"INDEX NOT NULL"` | |||
| OperateType string `xorm:"NOT NULL"` | |||
| Status string `xorm:"NOT NULL"` | |||
| Remark string | |||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
| FinishedUnix timeutil.TimeStamp `xorm:"INDEX"` | |||
| } | |||
| type AdminRewardOperateReq struct { | |||
| @@ -144,6 +245,8 @@ func (r RewardOperateRecord) ToShow() RewardOperateRecordShow { | |||
| OperateType: r.OperateType, | |||
| Amount: r.Amount, | |||
| Remark: r.Remark, | |||
| Status: r.Status, | |||
| SourceType: r.SourceType, | |||
| } | |||
| } | |||
| @@ -153,10 +256,11 @@ type RewardOperateRecordShow struct { | |||
| Status string | |||
| OperateType string | |||
| Amount int64 | |||
| Remark string | |||
| SourceType string | |||
| Action Action | |||
| Cloudbrain Cloudbrain | |||
| SourceType SourceType | |||
| Remark string | |||
| AdminLog RewardAdminLog | |||
| } | |||
| func getPointOperateRecord(tl *RewardOperateRecord) (*RewardOperateRecord, error) { | |||
| @@ -189,11 +293,12 @@ func InsertRewardOperateRecord(tl *RewardOperateRecord) (int64, error) { | |||
| return x.Insert(tl) | |||
| } | |||
| func UpdateAwardOperateRecordStatus(sourceType, requestId, oldStatus, newStatus string) (int64, error) { | |||
| func UpdateRewardRecordToFinalStatus(sourceType, requestId, newStatus string) (int64, error) { | |||
| r := &RewardOperateRecord{ | |||
| Status: newStatus, | |||
| Status: newStatus, | |||
| FinishedUnix: timeutil.TimeStampNow(), | |||
| } | |||
| return x.Cols("status").Where("source_type=? and request_id=? and status=?", sourceType, requestId, oldStatus).Update(r) | |||
| return x.Cols("status", "finished_unix").Where("source_type=? and request_id=? and status=?", sourceType, requestId, OperateStatusOperating).Update(r) | |||
| } | |||
| func SumRewardAmountInTaskPeriod(rewardType string, sourceType string, userId int64, period *PeriodResult) (int64, error) { | |||
| @@ -252,7 +357,7 @@ type RewardRecordListOpts struct { | |||
| OrderBy RewardOperateOrderBy | |||
| } | |||
| func GetRewardRecordList(opts RewardRecordListOpts) ([]RewardOperateRecord, int64, error) { | |||
| func GetRewardRecordList(opts RewardRecordListOpts) (RewardRecordList, int64, error) { | |||
| if opts.Page <= 0 { | |||
| opts.Page = 1 | |||
| } | |||
| @@ -261,7 +366,7 @@ func GetRewardRecordList(opts RewardRecordListOpts) ([]RewardOperateRecord, int6 | |||
| opts.OrderBy = RewardOrderByIDDesc | |||
| } | |||
| r := make([]RewardOperateRecord, 0) | |||
| r := make([]*RewardOperateRecord, 0) | |||
| cond := builder.NewCond() | |||
| if opts.UserId > 0 { | |||
| cond = cond.And(builder.Eq{"user_id": opts.UserId}) | |||
| @@ -77,7 +77,7 @@ func IncrRewardTaskSuccessCount(t RewardPeriodicTask, count int64, nextTime time | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| _, err = sess.Exec("update reward_operate_record set amount = amount + ? ,updated_unix = ? where serial_no = ?", count*t.Amount, timeutil.TimeStampNow(), t.OperateSerialNo) | |||
| _, err = sess.Exec("update reward_operate_record set amount = amount + ? ,updated_unix = ? where serial_no = ?", t.Amount, timeutil.TimeStampNow(), t.OperateSerialNo) | |||
| if err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| @@ -7,7 +7,6 @@ import ( | |||
| type TaskAccomplishLog struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| LogId string `xorm:"INDEX NOT NULL"` | |||
| ConfigId int64 `xorm:"NOT NULL"` | |||
| TaskCode string `xorm:"NOT NULL"` | |||
| UserId int64 `xorm:"INDEX NOT NULL"` | |||
| @@ -251,6 +251,6 @@ func initBasicTasks() { | |||
| registerSyncCloudbrainStatus() | |||
| registerHandleOrgStatistic() | |||
| registerRewardPeriodTask() | |||
| //registerRewardPeriodTask() | |||
| registerCloudbrainPointDeductTask() | |||
| } | |||
| @@ -2,7 +2,7 @@ package repo | |||
| import ( | |||
| "bufio" | |||
| "code.gitea.io/gitea/services/reward" | |||
| "code.gitea.io/gitea/services/reward/point/account" | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| @@ -230,7 +230,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||
| command = commandTrain | |||
| } | |||
| if !reward.IsPointBalanceEnough(ctx.User.ID, jobType, resourceSpecId) { | |||
| if !account.IsPointBalanceEnough(ctx.User.ID, jobType, resourceSpecId) { | |||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, jobType, resourceSpecId) | |||
| cloudBrainNewDataPrepare(ctx) | |||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tpl, &form) | |||
| @@ -318,7 +318,7 @@ func CloudBrainRestart(ctx *context.Context) { | |||
| var status = string(models.JobWaiting) | |||
| task := ctx.Cloudbrain | |||
| for { | |||
| if !reward.IsPointBalanceEnough(ctx.User.ID, task.JobType, task.ResourceSpecId) { | |||
| if !account.IsPointBalanceEnough(ctx.User.ID, task.JobType, task.ResourceSpecId) { | |||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, task.JobType, task.ResourceSpecId) | |||
| resultCode = "-1" | |||
| errorMsg = models.ErrInsufficientPointsBalance{}.Error() | |||
| @@ -1870,7 +1870,7 @@ func BenchMarkAlgorithmCreate(ctx *context.Context, form auth.CreateCloudBrainFo | |||
| repo := ctx.Repo.Repository | |||
| if !reward.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) { | |||
| if !account.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) { | |||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) | |||
| cloudBrainNewDataPrepare(ctx) | |||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tplCloudBrainBenchmarkNew, &form) | |||
| @@ -2032,7 +2032,7 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm) | |||
| tpl := tplCloudBrainBenchmarkNew | |||
| command := cloudbrain.Command | |||
| if !reward.IsPointBalanceEnough(ctx.User.ID, jobType, resourceSpecId) { | |||
| if !account.IsPointBalanceEnough(ctx.User.ID, jobType, resourceSpecId) { | |||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, jobType, resourceSpecId) | |||
| cloudBrainNewDataPrepare(ctx) | |||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tpl, &form) | |||
| @@ -2,7 +2,7 @@ package repo | |||
| import ( | |||
| "archive/zip" | |||
| "code.gitea.io/gitea/services/reward" | |||
| "code.gitea.io/gitea/services/reward/point/account" | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| @@ -207,7 +207,7 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm | |||
| repo := ctx.Repo.Repository | |||
| resourceSpecId := form.ResourceSpecId | |||
| if !reward.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeDebug), resourceSpecId) { | |||
| if !account.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeDebug), resourceSpecId) { | |||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) | |||
| cloudBrainNewDataPrepare(ctx) | |||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tplModelArtsNotebookNew, &form) | |||
| @@ -426,7 +426,7 @@ func NotebookManage(ctx *context.Context) { | |||
| errorMsg = "you have no right to restart the job" | |||
| break | |||
| } | |||
| if !reward.IsPointBalanceEnough(ctx.User.ID, task.JobType, task.ResourceSpecId) { | |||
| if !account.IsPointBalanceEnough(ctx.User.ID, task.JobType, task.ResourceSpecId) { | |||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, task.JobType, task.ResourceSpecId) | |||
| resultCode = "-1" | |||
| errorMsg = models.ErrInsufficientPointsBalance{}.Error() | |||
| @@ -1002,7 +1002,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) | |||
| EngineName := form.EngineName | |||
| resourceSpecId := form.ResourceSpecId | |||
| if !reward.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeTrain), resourceSpecId) { | |||
| if !account.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeTrain), resourceSpecId) { | |||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) | |||
| cloudBrainNewDataPrepare(ctx) | |||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tplModelArtsTrainJobNew, &form) | |||
| @@ -1851,7 +1851,7 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference | |||
| ckptUrl := form.TrainUrl + form.CkptName | |||
| if !reward.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeInference), resourceSpecId) { | |||
| if !account.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeInference), resourceSpecId) { | |||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) | |||
| inferenceJobErrorNewDataPrepare(ctx, form) | |||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tplModelArtsInferenceJobNew, &form) | |||
| @@ -13,7 +13,6 @@ import ( | |||
| const tplPoint base.TplName = "/reward/point" | |||
| type AccountResponse struct { | |||
| AccountCode string | |||
| Balance int64 | |||
| TotalEarned int64 | |||
| TotalConsumed int64 | |||
| @@ -4,8 +4,6 @@ import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "code.gitea.io/gitea/services/reward/point/account" | |||
| "encoding/json" | |||
| "fmt" | |||
| "time" | |||
| ) | |||
| @@ -17,34 +15,17 @@ var ( | |||
| const RUN_CLOUDBRAIN_TASK_TITTLE = "运行云脑任务" | |||
| //IsPointBalanceEnough check whether the user's point balance is bigger than task unit price | |||
| func IsPointBalanceEnough(targetUserId int64, jobType string, resourceSpecId int) bool { | |||
| func StartAndGetCloudBrainPointDeductTask(task models.Cloudbrain) (*models.RewardPeriodicTask, error) { | |||
| if !setting.CloudBrainPaySwitch { | |||
| return true | |||
| } | |||
| spec := getResourceSpec(jobType, resourceSpecId) | |||
| if spec == nil { | |||
| return true | |||
| } | |||
| a, error := account.GetAccount(targetUserId) | |||
| if error != nil { | |||
| return false | |||
| } | |||
| return a.Balance >= spec.UnitPrice | |||
| } | |||
| func StartCloudBrainPointDeductTask(task models.Cloudbrain) { | |||
| if !setting.CloudBrainPaySwitch { | |||
| return | |||
| return nil, nil | |||
| } | |||
| spec := getResourceSpec(task.JobType, task.ResourceSpecId) | |||
| spec := models.GetResourceSpec(task.JobType, task.ResourceSpecId) | |||
| if spec == nil || spec.UnitPrice == 0 { | |||
| return | |||
| return nil, nil | |||
| } | |||
| StartPeriodicTask(&models.StartPeriodicTaskOpts{ | |||
| return StartAndGetPeriodicTask(&models.StartPeriodicTaskOpts{ | |||
| SourceType: models.SourceTypeRunCloudbrainTask, | |||
| SourceId: getCloudBrainPointTaskSourceId(task), | |||
| TargetUserId: task.UserID, | |||
| @@ -67,31 +48,6 @@ func getCloudBrainPointTaskSourceId(task models.Cloudbrain) string { | |||
| return fmt.Sprint(task.ID) | |||
| } | |||
| func getResourceSpec(jobType string, resourceSpecId int) *models.ResourceSpec { | |||
| if jobType == string(models.JobTypeTrain) { | |||
| if TrainResourceSpecs == nil { | |||
| json.Unmarshal([]byte(setting.TrainResourceSpecs), &TrainResourceSpecs) | |||
| } | |||
| for _, spec := range TrainResourceSpecs.ResourceSpec { | |||
| if resourceSpecId == spec.Id { | |||
| return spec | |||
| } | |||
| } | |||
| } else { | |||
| if ResourceSpecs == nil { | |||
| json.Unmarshal([]byte(setting.ResourceSpecs), &ResourceSpecs) | |||
| } | |||
| for _, spec := range ResourceSpecs.ResourceSpec { | |||
| if resourceSpecId == spec.Id { | |||
| return spec | |||
| } | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| var firstTimeFlag = true | |||
| func StartCloudbrainPointDeductTask() { | |||
| @@ -107,10 +63,9 @@ func StartCloudbrainPointDeductTask() { | |||
| if firstTimeFlag { | |||
| //When it is executed for the first time, it needs to process the tasks of the last 1 hours. | |||
| //This is done to prevent the application from hanging for a long time | |||
| start = end.Add(-1 * time.Hour) | |||
| start = end.Add(-3 * time.Hour) | |||
| firstTimeFlag = false | |||
| } | |||
| taskList, err := models.GetStartedCloudbrainTaskByUpdatedUnix(start, end) | |||
| if err != nil { | |||
| log.Error("GetStartedCloudbrainTaskByUpdatedUnix error. %v", err) | |||
| @@ -121,11 +76,30 @@ func StartCloudbrainPointDeductTask() { | |||
| return | |||
| } | |||
| for _, t := range taskList { | |||
| if int64(t.StartTime) <= end.Unix() && int64(t.StartTime) >= start.Unix() { | |||
| StartCloudBrainPointDeductTask(t) | |||
| //初始化 period_task 和 operate_record | |||
| if int64(t.StartTime) > end.Unix() || int64(t.StartTime) < start.Unix() { | |||
| continue | |||
| } | |||
| task, err := StartAndGetCloudBrainPointDeductTask(t) | |||
| if err != nil { | |||
| log.Error("run cloubrain point deduct task error,err=%v", err) | |||
| continue | |||
| } | |||
| if task == nil { | |||
| continue | |||
| } | |||
| if task.Status == models.PeriodicTaskStatusFinished { | |||
| log.Info("Periodic task is finished") | |||
| continue | |||
| } | |||
| if int64(t.EndTime) <= end.Unix() && int64(t.EndTime) >= start.Unix() { | |||
| StopCloudBrainPointDeductTask(t) | |||
| endTime := time.Unix(int64(t.EndTime), 0) | |||
| RunRewardTask(*task, endTime) | |||
| models.StopPeriodicTask(task.ID, task.OperateSerialNo, endTime) | |||
| } else { | |||
| RunRewardTask(*task, end) | |||
| } | |||
| } | |||
| } | |||
| @@ -78,11 +78,11 @@ func Operate(ctx *models.RewardOperateContext) error { | |||
| //operate | |||
| if err := operator.Operate(ctx); err != nil { | |||
| updateAwardOperateRecordStatus(ctx.SourceType.Name(), ctx.RequestId, models.OperateStatusOperating, models.OperateStatusFailed) | |||
| UpdateRewardRecordToFinalStatus(ctx.SourceType.Name(), ctx.RequestId, models.OperateStatusFailed) | |||
| return err | |||
| } | |||
| updateAwardOperateRecordStatus(ctx.SourceType.Name(), ctx.RequestId, models.OperateStatusOperating, models.OperateStatusSucceeded) | |||
| UpdateRewardRecordToFinalStatus(ctx.SourceType.Name(), ctx.RequestId, models.OperateStatusSucceeded) | |||
| NotifyRewardOperation(ctx.TargetUserId, ctx.Reward.Amount, ctx.Reward.Type, ctx.OperateType) | |||
| return nil | |||
| } | |||
| @@ -160,8 +160,8 @@ func createPeriodicRewardOperateRecord(ctx *models.StartPeriodicTaskOpts) (strin | |||
| return record.SerialNo, nil | |||
| } | |||
| func updateAwardOperateRecordStatus(sourceType, requestId, oldStatus, newStatus string) error { | |||
| _, err := models.UpdateAwardOperateRecordStatus(sourceType, requestId, oldStatus, newStatus) | |||
| func UpdateRewardRecordToFinalStatus(sourceType, requestId, newStatus string) error { | |||
| _, err := models.UpdateRewardRecordToFinalStatus(sourceType, requestId, newStatus) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| @@ -169,10 +169,10 @@ func updateAwardOperateRecordStatus(sourceType, requestId, oldStatus, newStatus | |||
| } | |||
| func StartPeriodicTaskAsyn(opts *models.StartPeriodicTaskOpts) { | |||
| go StartPeriodicTask(opts) | |||
| go StartAndGetPeriodicTask(opts) | |||
| } | |||
| func StartPeriodicTask(opts *models.StartPeriodicTaskOpts) error { | |||
| func StartAndGetPeriodicTask(opts *models.StartPeriodicTaskOpts) (*models.RewardPeriodicTask, error) { | |||
| defer func() { | |||
| if err := recover(); err != nil { | |||
| combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2)) | |||
| @@ -183,35 +183,46 @@ func StartPeriodicTask(opts *models.StartPeriodicTaskOpts) error { | |||
| var rewardLock = redis_lock.NewDistributeLock(redis_key.RewardOperateLock(opts.RequestId, opts.SourceType.Name(), opts.OperateType.Name())) | |||
| isOk, err := rewardLock.Lock(3 * time.Second) | |||
| if err != nil { | |||
| return err | |||
| return nil, err | |||
| } | |||
| if !isOk { | |||
| log.Info("duplicated operate request,targetUserId=%d requestId=%s", opts.TargetUserId, opts.RequestId) | |||
| return nil | |||
| return nil, nil | |||
| } | |||
| defer rewardLock.UnLock() | |||
| //is handled before? | |||
| isHandled, err := isHandled(opts.SourceType.Name(), opts.RequestId, opts.OperateType.Name()) | |||
| if err != nil { | |||
| log.Error("operate is handled error,%v", err) | |||
| return err | |||
| _, err = models.GetPointOperateRecordBySourceTypeAndRequestId(opts.SourceType.Name(), opts.RequestId, opts.OperateType.Name()) | |||
| if err == nil { | |||
| task, err := models.GetPeriodicTaskBySourceIdAndType(opts.SourceType, opts.SourceId, opts.OperateType) | |||
| if err != nil { | |||
| log.Error("GetPeriodicTaskBySourceIdAndType error,%v", err) | |||
| return nil, err | |||
| } | |||
| return task, nil | |||
| } | |||
| if isHandled { | |||
| log.Info("operate has been handled,opts=%+v", opts) | |||
| return nil | |||
| if err != nil && !models.IsErrRecordNotExist(err) { | |||
| log.Error("operate is handled error,%v", err) | |||
| return nil, err | |||
| } | |||
| //new reward operate record | |||
| recordId, err := createPeriodicRewardOperateRecord(opts) | |||
| if err != nil { | |||
| return err | |||
| return nil, err | |||
| } | |||
| if err = NewRewardPeriodicTask(recordId, opts); err != nil { | |||
| updateAwardOperateRecordStatus(opts.SourceType.Name(), opts.RequestId, models.OperateStatusOperating, models.OperateStatusFailed) | |||
| return err | |||
| UpdateRewardRecordToFinalStatus(opts.SourceType.Name(), opts.RequestId, models.OperateStatusFailed) | |||
| return nil, err | |||
| } | |||
| return nil | |||
| task, err := models.GetPeriodicTaskBySourceIdAndType(opts.SourceType, opts.SourceId, opts.OperateType) | |||
| if err != nil { | |||
| log.Error("GetPeriodicTaskBySourceIdAndType error,%v", err) | |||
| return nil, err | |||
| } | |||
| return task, nil | |||
| } | |||
| func StopPeriodicTaskAsyn(sourceType models.SourceType, sourceId string, operateType models.RewardOperateType) { | |||
| @@ -6,6 +6,8 @@ import ( | |||
| "code.gitea.io/gitea/modules/redis/redis_key" | |||
| "code.gitea.io/gitea/modules/redis/redis_lock" | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| "code.gitea.io/gitea/routers/repo" | |||
| "errors" | |||
| "fmt" | |||
| "time" | |||
| ) | |||
| @@ -46,33 +48,33 @@ func StartRewardTask() { | |||
| } | |||
| } | |||
| func RunRewardTask(t models.RewardPeriodicTask, now time.Time) { | |||
| func RunRewardTask(t models.RewardPeriodicTask, now time.Time) error { | |||
| lock := redis_lock.NewDistributeLock(redis_key.RewardTaskRunningLock(t.ID)) | |||
| isOk, _ := lock.LockWithWait(3*time.Second, 3*time.Second) | |||
| if !isOk { | |||
| log.Error("get RewardTaskRunningLock failed,t=%+v", t) | |||
| return | |||
| return errors.New("get RewardTaskRunningLock failed") | |||
| } | |||
| defer lock.UnLock() | |||
| record, err := models.GetPointOperateRecordBySerialNo(t.OperateSerialNo) | |||
| if err != nil { | |||
| log.Error("RunRewardTask. GetPointOperateRecordBySerialNo error. %v", err) | |||
| return | |||
| return errors.New("GetPointOperateRecordBySerialNo error") | |||
| } | |||
| if record.Status != models.OperateStatusOperating { | |||
| log.Info("RunRewardTask. operate record is finished,record=%+v", record) | |||
| return | |||
| return nil | |||
| } | |||
| n, _ := countExecuteTimes(t, now) | |||
| if n == 0 { | |||
| return | |||
| return nil | |||
| } | |||
| //get operator | |||
| operator := GetOperator(models.GetRewardTypeInstance(record.RewardType)) | |||
| if operator == nil { | |||
| log.Error("RunRewardTask. operator of reward type is not exist") | |||
| return | |||
| return errors.New("operator of reward type is not exist") | |||
| } | |||
| nextTime := t.NextExecuteTime | |||
| for i := 0; int64(i) <= n; i++ { | |||
| @@ -89,14 +91,20 @@ func RunRewardTask(t models.RewardPeriodicTask, now time.Time) { | |||
| if err != nil { | |||
| log.Error("RunRewardTask.operator operate error.%v", err) | |||
| if models.IsErrInsufficientPointsBalance(err) { | |||
| StopCloudbrainTask(record) | |||
| return | |||
| task, err := models.GetCloudbrainByID(record.SourceId) | |||
| if err != nil { | |||
| log.Error("RunRewardTask GetCloudbrainByID error. %v", err) | |||
| return err | |||
| } | |||
| repo.StopJobs([]*models.Cloudbrain{task}) | |||
| return nil | |||
| } | |||
| return | |||
| return nil | |||
| } | |||
| models.IncrRewardTaskSuccessCount(t, n, nextTime) | |||
| models.IncrRewardTaskSuccessCount(t, 1, nextTime) | |||
| nextTime = timeutil.TimeStamp(int64(nextTime) + t.IntervalSeconds) | |||
| } | |||
| return nil | |||
| } | |||
| @@ -111,8 +119,3 @@ func countExecuteTimes(t models.RewardPeriodicTask, now time.Time) (int64, timeu | |||
| newNextTime := timeutil.TimeStamp(nextTime + n*interval) | |||
| return n, newNextTime | |||
| } | |||
| func StopCloudbrainTask(r *models.RewardOperateRecord) { | |||
| //todo | |||
| } | |||
| @@ -5,6 +5,7 @@ import ( | |||
| "code.gitea.io/gitea/modules/redis/redis_client" | |||
| "code.gitea.io/gitea/modules/redis/redis_key" | |||
| "code.gitea.io/gitea/modules/redis/redis_lock" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "code.gitea.io/gitea/modules/util" | |||
| "encoding/json" | |||
| "time" | |||
| @@ -60,3 +61,20 @@ func InitAccount(userId int64) (*models.PointAccount, error) { | |||
| return nil, nil | |||
| } | |||
| //IsPointBalanceEnough check whether the user's point balance is bigger than task unit price | |||
| func IsPointBalanceEnough(targetUserId int64, jobType string, resourceSpecId int) bool { | |||
| if !setting.CloudBrainPaySwitch { | |||
| return true | |||
| } | |||
| spec := models.GetResourceSpec(jobType, resourceSpecId) | |||
| if spec == nil { | |||
| return true | |||
| } | |||
| a, error := GetAccount(targetUserId) | |||
| if error != nil { | |||
| return false | |||
| } | |||
| return a.Balance >= spec.UnitPrice | |||
| } | |||
| @@ -1,9 +1,11 @@ | |||
| package reward | |||
| import "code.gitea.io/gitea/models" | |||
| import ( | |||
| "code.gitea.io/gitea/models" | |||
| ) | |||
| type RecordResponse struct { | |||
| Records []models.RewardOperateRecordShow | |||
| Records []*models.RewardOperateRecordShow | |||
| Total int64 | |||
| PageSize int | |||
| Page int | |||
| @@ -14,9 +16,13 @@ func GetRewardRecordList(opts models.RewardRecordListOpts) (*RecordResponse, err | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| r := make([]models.RewardOperateRecordShow, 0) | |||
| for _, v := range l { | |||
| r = append(r, v.ToShow()) | |||
| if len(l) == 0 { | |||
| return &RecordResponse{Records: make([]*models.RewardOperateRecordShow, 0), Total: n, Page: opts.Page, PageSize: opts.PageSize}, nil | |||
| } | |||
| return &RecordResponse{Records: r, Total: n, Page: opts.Page, PageSize: opts.PageSize}, nil | |||
| result, err := l.ToShow() | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return &RecordResponse{Records: result, Total: n, Page: opts.Page, PageSize: opts.PageSize}, nil | |||
| } | |||
| @@ -3,7 +3,6 @@ package task | |||
| import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/util" | |||
| "code.gitea.io/gitea/services/reward" | |||
| "code.gitea.io/gitea/services/reward/limiter" | |||
| "fmt" | |||
| @@ -55,9 +54,7 @@ func accomplish(action models.Action) error { | |||
| } | |||
| //add log | |||
| logId := util.UUID() | |||
| _, err = models.InsertTaskAccomplishLog(&models.TaskAccomplishLog{ | |||
| LogId: logId, | |||
| ConfigId: config.ID, | |||
| TaskCode: config.TaskCode, | |||
| UserId: userId, | |||
| @@ -70,14 +67,14 @@ func accomplish(action models.Action) error { | |||
| //reward | |||
| reward.Operate(&models.RewardOperateContext{ | |||
| SourceType: models.SourceTypeAccomplishTask, | |||
| SourceId: logId, | |||
| SourceId: fmt.Sprint(action.ID), | |||
| Tittle: config.Tittle, | |||
| Reward: models.Reward{ | |||
| Amount: config.AwardAmount, | |||
| Type: models.GetRewardTypeInstance(config.AwardType), | |||
| }, | |||
| TargetUserId: userId, | |||
| RequestId: logId, | |||
| RequestId: fmt.Sprint(action.ID), | |||
| OperateType: models.OperateTypeIncrease, | |||
| RejectPolicy: models.FillUp, | |||
| }) | |||