| @@ -58,6 +58,16 @@ const ( | |||||
| ActionCreateBenchMarkTask //29 | ActionCreateBenchMarkTask //29 | ||||
| ActionCreateNewModelTask //30 | ActionCreateNewModelTask //30 | ||||
| ActionCreateGPUTrainTask //31 | ActionCreateGPUTrainTask //31 | ||||
| ActionBindWechat //32issue_assignees | |||||
| ActionCreateCloudbrainTask //33 | |||||
| ActionDatasetRecommended //34 | |||||
| ActionCreateImage //35 | |||||
| ActionImageRecommend //36 | |||||
| ActionChangeUserAvatar //37 | |||||
| ActionPushCommits //38 | |||||
| ActionForkRepo //39 | |||||
| ) | ) | ||||
| // Action represents user operation type and other information to | // Action represents user operation type and other information to | ||||
| @@ -654,9 +654,9 @@ func Attachments(opts *AttachmentsOptions) ([]*AttachmentInfo, int64, error) { | |||||
| return attachments, count, nil | return attachments, count, nil | ||||
| } | } | ||||
| func GetAllUserIdByDatasetId(datasetId int64) ([]int64, error) { | |||||
| r := make([]int64, 0) | |||||
| if err := x.Table("attachment").Where("dataset_id = ?", datasetId).Distinct("uploader_id").Find(&r); err != nil { | |||||
| func GetAllDatasetContributorByDatasetId(datasetId int64) ([]*User, error) { | |||||
| r := make([]*User, 0) | |||||
| if err := x.Select("distinct(user.*)").Table("attachment").Join("LEFT", "user", "user.ID = attachment.uploader_id").Where("attachment.dataset_id = ?", datasetId).Find(&r); err != nil { | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| return r, nil | return r, nil | ||||
| @@ -2024,3 +2024,15 @@ func IsErrRecordNotExist(err error) bool { | |||||
| func (err ErrRecordNotExist) Error() string { | func (err ErrRecordNotExist) Error() string { | ||||
| return fmt.Sprintf("record not exist in database") | return fmt.Sprintf("record not exist in database") | ||||
| } | } | ||||
| type ErrInsufficientPointsBalance struct { | |||||
| } | |||||
| func IsErrInsufficientPointsBalance(err error) bool { | |||||
| _, ok := err.(ErrInsufficientPointsBalance) | |||||
| return ok | |||||
| } | |||||
| func (err ErrInsufficientPointsBalance) Error() string { | |||||
| return fmt.Sprintf("Insufficient points balance") | |||||
| } | |||||
| @@ -41,6 +41,14 @@ func (l LimitScope) Name() string { | |||||
| } | } | ||||
| } | } | ||||
| type LimiterRejectPolicy string | |||||
| const ( | |||||
| JustReject LimiterRejectPolicy = "JUST_REJECT" | |||||
| PermittedOnce LimiterRejectPolicy = "PERMITTED_ONCE" | |||||
| FillUp LimiterRejectPolicy = "FillUp" | |||||
| ) | |||||
| type LimitConfig struct { | type LimitConfig struct { | ||||
| ID int64 `xorm:"pk autoincr"` | ID int64 `xorm:"pk autoincr"` | ||||
| Tittle string | Tittle string | ||||
| @@ -151,6 +151,7 @@ func init() { | |||||
| new(RewardPeriodicTask), | new(RewardPeriodicTask), | ||||
| new(PointAccountLog), | new(PointAccountLog), | ||||
| new(PointAccount), | new(PointAccount), | ||||
| new(RewardAdminLog), | |||||
| ) | ) | ||||
| tablesStatistic = append(tablesStatistic, | tablesStatistic = append(tablesStatistic, | ||||
| @@ -25,6 +25,7 @@ const ( | |||||
| ) | ) | ||||
| var ActionChan = make(chan *Action, 200) | var ActionChan = make(chan *Action, 200) | ||||
| var ActionChan4Task = make(chan Action, 200) | |||||
| // Watch is connection request for receiving repository notification. | // Watch is connection request for receiving repository notification. | ||||
| type Watch struct { | type Watch struct { | ||||
| @@ -199,6 +200,9 @@ func notifyWatchers(e Engine, actions ...*Action) error { | |||||
| if _, err = e.InsertOne(act); err != nil { | if _, err = e.InsertOne(act); err != nil { | ||||
| return fmt.Errorf("insert new actioner: %v", err) | return fmt.Errorf("insert new actioner: %v", err) | ||||
| } | } | ||||
| // After InsertOne(act),the act has ID | |||||
| // Send the act to task chan | |||||
| ActionChan4Task <- *act | |||||
| if repoChanged { | if repoChanged { | ||||
| act.loadRepo() | act.loadRepo() | ||||
| @@ -279,7 +283,6 @@ func notifyWatchers(e Engine, actions ...*Action) error { | |||||
| // NotifyWatchers creates batch of actions for every watcher. | // NotifyWatchers creates batch of actions for every watcher. | ||||
| func NotifyWatchers(actions ...*Action) error { | func NotifyWatchers(actions ...*Action) error { | ||||
| error := notifyWatchers(x, actions...) | error := notifyWatchers(x, actions...) | ||||
| producer(actions...) | producer(actions...) | ||||
| return error | return error | ||||
| @@ -0,0 +1,46 @@ | |||||
| package models | |||||
| import ( | |||||
| "code.gitea.io/gitea/modules/timeutil" | |||||
| ) | |||||
| const ( | |||||
| RewardAdminLogProcessing = 1 | |||||
| RewardAdminLogSuccess = 2 | |||||
| RewardAdminLogFailed = 3 | |||||
| ) | |||||
| type RewardAdminLog struct { | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| LogId string `xorm:"INDEX NOT NULL"` | |||||
| Amount int64 `xorm:"NOT NULL"` | |||||
| RewardType string | |||||
| Remark string | |||||
| Status int | |||||
| TargetUserId int64 `xorm:"INDEX NOT NULL"` | |||||
| CreatorId int64 `xorm:"NOT NULL"` | |||||
| CreatorName string | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| } | |||||
| func getRewardAdminLog(ra *RewardAdminLog) (*RewardAdminLog, error) { | |||||
| has, err := x.Get(ra) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } else if !has { | |||||
| return nil, ErrRecordNotExist{} | |||||
| } | |||||
| return ra, nil | |||||
| } | |||||
| func InsertRewardAdminLog(ra *RewardAdminLog) (int64, error) { | |||||
| return x.Insert(ra) | |||||
| } | |||||
| func UpdateRewardAdminLogStatus(logId string, oldStatus, newStatus int) error { | |||||
| _, err := x.Where("log_id = ? and status = ?", logId, oldStatus).Update(&RewardAdminLog{Status: newStatus}) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @@ -95,6 +95,7 @@ func GetRewardOperateTypeInstance(s string) RewardOperateType { | |||||
| const ( | const ( | ||||
| OperateTypeIncrease RewardOperateType = "INCREASE" | OperateTypeIncrease RewardOperateType = "INCREASE" | ||||
| OperateTypeDecrease RewardOperateType = "DECREASE" | OperateTypeDecrease RewardOperateType = "DECREASE" | ||||
| OperateTypeNull RewardOperateType = "NIL" | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| @@ -105,11 +106,18 @@ const ( | |||||
| const Semicolon = ";" | const Semicolon = ";" | ||||
| type RewardOperateOrderBy string | |||||
| const ( | |||||
| RewardOrderByID RewardOperateOrderBy = "id" | |||||
| ) | |||||
| type RewardOperateRecord struct { | type RewardOperateRecord struct { | ||||
| ID int64 `xorm:"pk autoincr"` | ID int64 `xorm:"pk autoincr"` | ||||
| RecordId string `xorm:"INDEX NOT NULL"` | |||||
| SerialNo string `xorm:"INDEX NOT NULL"` | |||||
| UserId int64 `xorm:"INDEX NOT NULL"` | UserId int64 `xorm:"INDEX NOT NULL"` | ||||
| Amount int64 `xorm:"NOT NULL"` | Amount int64 `xorm:"NOT NULL"` | ||||
| Tittle string | |||||
| RewardType string `xorm:"NOT NULL"` | RewardType string `xorm:"NOT NULL"` | ||||
| SourceType string `xorm:"NOT NULL"` | SourceType string `xorm:"NOT NULL"` | ||||
| SourceId string `xorm:"INDEX NOT NULL"` | SourceId string `xorm:"INDEX NOT NULL"` | ||||
| @@ -121,6 +129,32 @@ type RewardOperateRecord struct { | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | ||||
| } | } | ||||
| type AdminRewardOperateReq struct { | |||||
| TargetUserId int64 `binding:"Required"` | |||||
| OperateType RewardOperateType `binding:"Required"` | |||||
| Amount int64 `binding:"Required;Range(1,100000)"` | |||||
| Remark string | |||||
| RewardType RewardType | |||||
| } | |||||
| func (r RewardOperateRecord) ToShow() RewardOperateRecordShow { | |||||
| return RewardOperateRecordShow{ | |||||
| SerialNo: r.SerialNo, | |||||
| Date: r.CreatedUnix, | |||||
| Tittle: r.Tittle, | |||||
| OperateType: r.OperateType, | |||||
| Amount: r.Amount, | |||||
| } | |||||
| } | |||||
| type RewardOperateRecordShow struct { | |||||
| SerialNo string | |||||
| Date timeutil.TimeStamp | |||||
| Tittle string | |||||
| OperateType string | |||||
| Amount int64 | |||||
| } | |||||
| func getPointOperateRecord(tl *RewardOperateRecord) (*RewardOperateRecord, error) { | func getPointOperateRecord(tl *RewardOperateRecord) (*RewardOperateRecord, error) { | ||||
| has, err := x.Get(tl) | has, err := x.Get(tl) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -140,14 +174,14 @@ func GetPointOperateRecordBySourceTypeAndRequestId(sourceType, requestId, operat | |||||
| return getPointOperateRecord(t) | return getPointOperateRecord(t) | ||||
| } | } | ||||
| func GetPointOperateRecordByRecordId(recordId string) (*RewardOperateRecord, error) { | |||||
| func GetPointOperateRecordBySerialNo(serialNo string) (*RewardOperateRecord, error) { | |||||
| t := &RewardOperateRecord{ | t := &RewardOperateRecord{ | ||||
| RecordId: recordId, | |||||
| SerialNo: serialNo, | |||||
| } | } | ||||
| return getPointOperateRecord(t) | return getPointOperateRecord(t) | ||||
| } | } | ||||
| func InsertAwardOperateRecord(tl *RewardOperateRecord) (int64, error) { | |||||
| func InsertRewardOperateRecord(tl *RewardOperateRecord) (int64, error) { | |||||
| return x.Insert(tl) | return x.Insert(tl) | ||||
| } | } | ||||
| @@ -175,11 +209,13 @@ func SumRewardAmountInTaskPeriod(rewardType string, sourceType string, userId in | |||||
| type RewardOperateContext struct { | type RewardOperateContext struct { | ||||
| SourceType SourceType | SourceType SourceType | ||||
| SourceId string | SourceId string | ||||
| Tittle string | |||||
| Remark string | Remark string | ||||
| Reward Reward | Reward Reward | ||||
| TargetUserId int64 | TargetUserId int64 | ||||
| RequestId string | RequestId string | ||||
| OperateType RewardOperateType | OperateType RewardOperateType | ||||
| RejectPolicy LimiterRejectPolicy | |||||
| } | } | ||||
| type Reward struct { | type Reward struct { | ||||
| @@ -202,3 +238,43 @@ type UserRewardOperation struct { | |||||
| func AppendRemark(remark, appendStr string) string { | func AppendRemark(remark, appendStr string) string { | ||||
| return strings.TrimPrefix(remark+Semicolon+appendStr, Semicolon) | return strings.TrimPrefix(remark+Semicolon+appendStr, Semicolon) | ||||
| } | } | ||||
| type RewardRecordListOpts struct { | |||||
| ListOptions | |||||
| UserId int64 | |||||
| OperateType RewardOperateType | |||||
| RewardType RewardType | |||||
| OrderBy RewardOperateOrderBy | |||||
| } | |||||
| func GetRewardRecordList(opts RewardRecordListOpts) ([]RewardOperateRecord, int64, error) { | |||||
| if opts.Page <= 0 { | |||||
| opts.Page = 1 | |||||
| } | |||||
| if len(opts.OrderBy) == 0 { | |||||
| opts.OrderBy = RewardOrderByID | |||||
| } | |||||
| r := make([]RewardOperateRecord, 0) | |||||
| cond := builder.NewCond() | |||||
| if opts.UserId > 0 { | |||||
| cond = cond.And(builder.Eq{"user_id": opts.UserId}) | |||||
| } | |||||
| if opts.OperateType != OperateTypeNull { | |||||
| cond = cond.And(builder.Eq{"operate_type": opts.OperateType.Name()}) | |||||
| } | |||||
| cond = cond.And(builder.Eq{"reward_type": opts.RewardType.Name()}) | |||||
| cond = cond.And(builder.Gt{"amount": 0}) | |||||
| count, err := x.Where(cond).Count(&RewardOperateRecord{}) | |||||
| if err != nil { | |||||
| return nil, 0, err | |||||
| } | |||||
| err = x.Where(cond).Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).OrderBy(string(opts.OrderBy)).Find(&r) | |||||
| if err != nil { | |||||
| return nil, 0, err | |||||
| } | |||||
| return r, count, nil | |||||
| } | |||||
| @@ -29,7 +29,7 @@ func (r PeriodType) Name() string { | |||||
| type RewardPeriodicTask struct { | type RewardPeriodicTask struct { | ||||
| ID int64 `xorm:"pk autoincr"` | ID int64 `xorm:"pk autoincr"` | ||||
| OperateRecordId string `xorm:"INDEX NOT NULL"` | |||||
| OperateSerialNo string `xorm:"INDEX NOT NULL"` | |||||
| DelaySeconds int64 | DelaySeconds int64 | ||||
| IntervalSeconds int64 | IntervalSeconds int64 | ||||
| Amount int64 `xorm:"NOT NULL"` | Amount int64 `xorm:"NOT NULL"` | ||||
| @@ -45,6 +45,7 @@ type StartPeriodicTaskOpts struct { | |||||
| SourceType SourceType | SourceType SourceType | ||||
| SourceId string | SourceId string | ||||
| Remark string | Remark string | ||||
| Tittle string | |||||
| TargetUserId int64 | TargetUserId int64 | ||||
| RequestId string | RequestId string | ||||
| OperateType RewardOperateType | OperateType RewardOperateType | ||||
| @@ -76,7 +77,7 @@ func IncrRewardTaskSuccessCount(t RewardPeriodicTask, count int64, nextTime time | |||||
| sess.Rollback() | sess.Rollback() | ||||
| return err | return err | ||||
| } | } | ||||
| _, err = sess.Exec("update reward_operate_record set amount = amount + ? ,updated_unix = ? where record_id = ?", count*t.Amount, timeutil.TimeStampNow(), t.OperateRecordId) | |||||
| _, err = sess.Exec("update reward_operate_record set amount = amount + ? ,updated_unix = ? where serial_no = ?", count*t.Amount, timeutil.TimeStampNow(), t.OperateSerialNo) | |||||
| if err != nil { | if err != nil { | ||||
| sess.Rollback() | sess.Rollback() | ||||
| return err | return err | ||||
| @@ -88,7 +89,7 @@ func IncrRewardTaskSuccessCount(t RewardPeriodicTask, count int64, nextTime time | |||||
| func GetPeriodicTaskBySourceIdAndType(sourceType SourceType, sourceId string, operateType RewardOperateType) (*RewardPeriodicTask, error) { | func GetPeriodicTaskBySourceIdAndType(sourceType SourceType, sourceId string, operateType RewardOperateType) (*RewardPeriodicTask, error) { | ||||
| r := RewardPeriodicTask{} | r := RewardPeriodicTask{} | ||||
| _, err := x.SQL("select rpt.* from reward_periodic_task rpt "+ | _, err := x.SQL("select rpt.* from reward_periodic_task rpt "+ | ||||
| "inner join reward_operate_record ror on rpt.operate_record_id = ror.record_id"+ | |||||
| "inner join reward_operate_record ror on rpt.operate_serial_no = ror.serial_no"+ | |||||
| " where ror.source_type = ? and source_id = ? and operate_type = ? ", sourceType.Name(), sourceId, operateType.Name()).Get(&r) | " where ror.source_type = ? and source_id = ? and operate_type = ? ", sourceType.Name(), sourceId, operateType.Name()).Get(&r) | ||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| @@ -96,7 +97,7 @@ func GetPeriodicTaskBySourceIdAndType(sourceType SourceType, sourceId string, op | |||||
| return &r, nil | return &r, nil | ||||
| } | } | ||||
| func StopPeriodicTask(taskId int64, operateRecordId string, stopTime time.Time) error { | |||||
| func StopPeriodicTask(taskId int64, operateSerialNo string, stopTime time.Time) error { | |||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| _, err := sess.Where("id = ? and status = ?", taskId, PeriodicTaskStatusRunning).Update(&RewardPeriodicTask{Status: PeriodicTaskStatusFinished, FinishedUnix: timeutil.TimeStamp(stopTime.Unix())}) | _, err := sess.Where("id = ? and status = ?", taskId, PeriodicTaskStatusRunning).Update(&RewardPeriodicTask{Status: PeriodicTaskStatusFinished, FinishedUnix: timeutil.TimeStamp(stopTime.Unix())}) | ||||
| @@ -104,7 +105,7 @@ func StopPeriodicTask(taskId int64, operateRecordId string, stopTime time.Time) | |||||
| sess.Rollback() | sess.Rollback() | ||||
| return err | return err | ||||
| } | } | ||||
| _, err = sess.Where("record_id = ? and status = ?", operateRecordId, OperateStatusOperating).Update(&RewardOperateRecord{Status: OperateStatusSucceeded}) | |||||
| _, err = sess.Where("serial_no = ? and status = ?", operateSerialNo, OperateStatusOperating).Update(&RewardOperateRecord{Status: OperateStatusSucceeded}) | |||||
| if err != nil { | if err != nil { | ||||
| sess.Rollback() | sess.Rollback() | ||||
| return err | return err | ||||
| @@ -6,11 +6,12 @@ import ( | |||||
| ) | ) | ||||
| type TaskAccomplishLog struct { | 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"` | |||||
| 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"` | |||||
| ActionId int64 | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | ||||
| } | } | ||||
| @@ -4,36 +4,6 @@ import ( | |||||
| "code.gitea.io/gitea/modules/timeutil" | "code.gitea.io/gitea/modules/timeutil" | ||||
| ) | ) | ||||
| const ( | |||||
| TaskTypeNewIssue = "NEW_ISSUE" | |||||
| TaskTypeIssueChangeStatus = "ISSUE_CHANGE_STATUS" | |||||
| TaskTypeCreateIssueComment = "CREATE_ISSUE_COMMENT" | |||||
| TaskTypeNewPullRequest = "NEW_PULL_REQUEST" | |||||
| TaskTypeRenameRepository = "RENAME_REPOSITORY" | |||||
| TaskTypeAliasRepository = "ALIAS_REPOSITORY" | |||||
| TaskTypeTransferRepository = "TRANSFER_REPOSITORY" | |||||
| TaskTypeCreateRepository = "CREATE_REPOSITORY" | |||||
| TaskTypeCreatePublicRepository = "CREATE_PUBLIC_REPOSITORY" | |||||
| TaskTypeForkRepository = "FORK_REPOSITORY" | |||||
| TaskTypePullRequestReview = "PULL_REQUEST_REVIEW" | |||||
| TaskTypeCommentPull = "COMMENT_PULL" | |||||
| TaskTypeApprovePullRequest = "APPROVE_PULL_REQUEST" | |||||
| TaskTypeRejectPullRequest = "REJECT_PULL_REQUEST" | |||||
| TaskTypeMergePullRequest = "MERGE_PULL_REQUEST" | |||||
| TaskTypeSyncPushCommits = "SYNC_PUSH_COMMITS" | |||||
| TaskTypeSyncCreateRef = "SYNC_CREATE_REF" | |||||
| TaskTypeSyncDeleteRef = "SYNC_DELETE_REF" | |||||
| TaskTypeBindWechat = "BIND_WECHAT" | |||||
| TaskTypeUploadAttachment = "UPLOAD_ATTACHMENT" | |||||
| TaskTypeCreateCloudbrainTask = "CREATE_CLOUDBRAIN_TASK" | |||||
| TaskTypeDatasetRecommended = "DATASET_RECOMMENDED" | |||||
| TaskTypeCreateModel = "CREATE_MODEL" | |||||
| TaskTypeCreatePublicImage = "CREATE_PUBLIC_IMAGE" | |||||
| TaskTypeImageRecommend = "IMAGE_RECOMMEND" | |||||
| TaskTypeChangeUserAvatar = "CHANGE_USER_AVATAR" | |||||
| TaskTypePushCommits = "PUSH_COMMITS" | |||||
| ) | |||||
| const ( | const ( | ||||
| PeriodNotCycle = "NOT_CYCLE" | PeriodNotCycle = "NOT_CYCLE" | ||||
| PeriodDaily = "DAILY" | PeriodDaily = "DAILY" | ||||
| @@ -1,6 +1,7 @@ | |||||
| package wechat | package wechat | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/notification" | "code.gitea.io/gitea/modules/notification" | ||||
| "code.gitea.io/gitea/modules/redis/redis_client" | "code.gitea.io/gitea/modules/redis/redis_client" | ||||
| "code.gitea.io/gitea/modules/redis/redis_key" | "code.gitea.io/gitea/modules/redis/redis_key" | ||||
| @@ -72,6 +73,10 @@ func HandleSubscribeEvent(we WechatEvent) string { | |||||
| jsonStr, _ := json.Marshal(qrCache) | jsonStr, _ := json.Marshal(qrCache) | ||||
| redis_client.Setex(redis_key.WechatBindingUserIdKey(sceneStr), string(jsonStr), 60*time.Second) | redis_client.Setex(redis_key.WechatBindingUserIdKey(sceneStr), string(jsonStr), 60*time.Second) | ||||
| } | } | ||||
| notification.NotifyWechatBind(qrCache.UserId, we.FromUserName) | |||||
| u, err := models.GetUserByID(qrCache.UserId) | |||||
| if err == nil { | |||||
| notification.NotifyWechatBind(u, we.FromUserName) | |||||
| } | |||||
| return BIND_REPLY_SUCCESS | return BIND_REPLY_SUCCESS | ||||
| } | } | ||||
| @@ -212,7 +212,7 @@ func getQueryString(page int, size int, name string) string { | |||||
| return fmt.Sprintf("pageIndex=%d&pageSize=%d&name=%s", page, size, name) | return fmt.Sprintf("pageIndex=%d&pageSize=%d&name=%s", page, size, name) | ||||
| } | } | ||||
| func CommitImage(jobID string, params models.CommitImageParams) error { | |||||
| func CommitImage(jobID string, params models.CommitImageParams, doer *models.User) error { | |||||
| imageTag := strings.TrimSpace(params.ImageTag) | imageTag := strings.TrimSpace(params.ImageTag) | ||||
| dbImage, err := models.GetImageByTag(imageTag) | dbImage, err := models.GetImageByTag(imageTag) | ||||
| @@ -314,12 +314,12 @@ sendjob: | |||||
| }) | }) | ||||
| if err == nil { | if err == nil { | ||||
| go updateImageStatus(image, isSetCreatedUnix, createTime) | go updateImageStatus(image, isSetCreatedUnix, createTime) | ||||
| notification.NotifyCreateImage(params.UID, image) | |||||
| notification.NotifyCreateImage(doer, image) | |||||
| } | } | ||||
| return err | return err | ||||
| } | } | ||||
| func CommitAdminImage(params models.CommitImageParams) error { | |||||
| func CommitAdminImage(params models.CommitImageParams, doer *models.User) error { | |||||
| imageTag := strings.TrimSpace(params.ImageTag) | imageTag := strings.TrimSpace(params.ImageTag) | ||||
| exist, err := models.IsImageExist(imageTag) | exist, err := models.IsImageExist(imageTag) | ||||
| @@ -357,7 +357,7 @@ func CommitAdminImage(params models.CommitImageParams) error { | |||||
| return nil | return nil | ||||
| }) | }) | ||||
| if err == nil { | if err == nil { | ||||
| notification.NotifyCreateImage(params.UID, image) | |||||
| notification.NotifyCreateImage(doer, image) | |||||
| } | } | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -212,7 +212,7 @@ func registerRewardPeriodTask() { | |||||
| RegisterTaskFatal("reward_period_task", &BaseConfig{ | RegisterTaskFatal("reward_period_task", &BaseConfig{ | ||||
| Enabled: true, | Enabled: true, | ||||
| RunAtStart: true, | RunAtStart: true, | ||||
| Schedule: "@every 5m", | |||||
| Schedule: "@every 1m", | |||||
| }, func(ctx context.Context, _ *models.User, _ Config) error { | }, func(ctx context.Context, _ *models.User, _ Config) error { | ||||
| reward.StartRewardTask() | reward.StartRewardTask() | ||||
| return nil | return nil | ||||
| @@ -5,6 +5,7 @@ | |||||
| package action | package action | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/auth" | |||||
| "encoding/json" | "encoding/json" | ||||
| "fmt" | "fmt" | ||||
| "path" | "path" | ||||
| @@ -345,3 +346,105 @@ func (a *actionNotifier) NotifyOtherTask(doer *models.User, repo *models.Reposit | |||||
| log.Error("notifyWatchers: %v", err) | log.Error("notifyWatchers: %v", err) | ||||
| } | } | ||||
| } | } | ||||
| func (t *actionNotifier) NotifyWechatBind(user *models.User, wechatOpenId string) { | |||||
| act := &models.Action{ | |||||
| ActUserID: user.ID, | |||||
| ActUser: user, | |||||
| OpType: models.ActionBindWechat, | |||||
| IsPrivate: true, | |||||
| Content: wechatOpenId, | |||||
| } | |||||
| if err := models.NotifyWatchers(act); err != nil { | |||||
| log.Error("notifyWatchers: %v", err) | |||||
| } | |||||
| } | |||||
| func (t *actionNotifier) NotifyDatasetRecommend(optUser *models.User, dataset *models.Dataset, action string) { | |||||
| switch action { | |||||
| case "recommend": | |||||
| users, err := models.GetAllDatasetContributorByDatasetId(dataset.ID) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| var actions = make([]*models.Action, 0) | |||||
| for _, user := range users { | |||||
| actions = append(actions, &models.Action{ | |||||
| OpType: models.ActionDatasetRecommended, | |||||
| ActUserID: user.ID, | |||||
| ActUser: user, | |||||
| RepoID: dataset.RepoID, | |||||
| Repo: dataset.Repo, | |||||
| Content: fmt.Sprint(dataset.ID), | |||||
| }) | |||||
| } | |||||
| if err := models.NotifyWatchers(actions...); err != nil { | |||||
| log.Error("notifyWatchers: %v", err) | |||||
| } | |||||
| } | |||||
| } | |||||
| func (t *actionNotifier) NotifyCreateImage(doer *models.User, image models.Image) { | |||||
| act := &models.Action{ | |||||
| ActUserID: doer.ID, | |||||
| ActUser: doer, | |||||
| OpType: models.ActionCreateImage, | |||||
| IsPrivate: image.IsPrivate, | |||||
| Content: fmt.Sprint(image.ID), | |||||
| } | |||||
| if err := models.NotifyWatchers(act); err != nil { | |||||
| log.Error("notifyWatchers: %v", err) | |||||
| } | |||||
| } | |||||
| func (t *actionNotifier) NotifyImageRecommend(optUser *models.User, imageId int64, action string) { | |||||
| image, err := models.GetImageByID(imageId) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| u, err := models.GetUserByID(image.UID) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| switch action { | |||||
| case "recommend": | |||||
| act := &models.Action{ | |||||
| ActUserID: u.ID, | |||||
| ActUser: u, | |||||
| OpType: models.ActionImageRecommend, | |||||
| IsPrivate: false, | |||||
| Content: fmt.Sprint(imageId), | |||||
| } | |||||
| if err := models.NotifyWatchers(act); err != nil { | |||||
| log.Error("notifyWatchers: %v", err) | |||||
| } | |||||
| } | |||||
| } | |||||
| func (t *actionNotifier) NotifyChangeUserAvatar(user *models.User, form auth.AvatarForm) { | |||||
| act := &models.Action{ | |||||
| ActUserID: user.ID, | |||||
| ActUser: user, | |||||
| OpType: models.ActionChangeUserAvatar, | |||||
| IsPrivate: true, | |||||
| } | |||||
| if err := models.NotifyWatchers(act); err != nil { | |||||
| log.Error("notifyWatchers: %v", err) | |||||
| } | |||||
| } | |||||
| func (t *actionNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) { | |||||
| act := &models.Action{ | |||||
| ActUserID: pusher.ID, | |||||
| ActUser: pusher, | |||||
| OpType: models.ActionPushCommits, | |||||
| RepoID: repo.ID, | |||||
| Repo: repo, | |||||
| RefName: refName, | |||||
| IsPrivate: repo.IsPrivate, | |||||
| Content: fmt.Sprintf("%s|%s", oldCommitID, newCommitID), | |||||
| } | |||||
| if err := models.NotifyWatchers(act); err != nil { | |||||
| log.Error("notifyWatchers: %v", err) | |||||
| } | |||||
| } | |||||
| @@ -6,6 +6,7 @@ package base | |||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/auth" | |||||
| "code.gitea.io/gitea/modules/repository" | "code.gitea.io/gitea/modules/repository" | ||||
| ) | ) | ||||
| @@ -56,9 +57,9 @@ type Notifier interface { | |||||
| NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) | NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) | ||||
| NotifyOtherTask(doer *models.User, repo *models.Repository, id string, name string, optype models.ActionType) | NotifyOtherTask(doer *models.User, repo *models.Repository, id string, name string, optype models.ActionType) | ||||
| NotifyWechatBind(userId int64, wechatOpenId string) | |||||
| NotifyWechatBind(user *models.User, wechatOpenId string) | |||||
| NotifyDatasetRecommend(optUser *models.User, dataset *models.Dataset, action string) | NotifyDatasetRecommend(optUser *models.User, dataset *models.Dataset, action string) | ||||
| NotifyCreateImage(optUserId int64, image models.Image) | |||||
| NotifyCreateImage(doer *models.User, image models.Image) | |||||
| NotifyImageRecommend(optUser *models.User, imageId int64, action string) | NotifyImageRecommend(optUser *models.User, imageId int64, action string) | ||||
| NotifyChangeUserAvatar(user *models.User) | |||||
| NotifyChangeUserAvatar(user *models.User, form auth.AvatarForm) | |||||
| } | } | ||||
| @@ -6,6 +6,7 @@ package base | |||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/auth" | |||||
| "code.gitea.io/gitea/modules/repository" | "code.gitea.io/gitea/modules/repository" | ||||
| ) | ) | ||||
| @@ -159,18 +160,19 @@ func (*NullNotifier) NotifyOtherTask(doer *models.User, repo *models.Repository, | |||||
| } | } | ||||
| func (*NullNotifier) NotifyWechatBind(userId int64, wechatOpenId string) { | |||||
| func (*NullNotifier) NotifyWechatBind(user *models.User, wechatOpenId string) { | |||||
| } | } | ||||
| func (*NullNotifier) NotifyDatasetRecommend(optUser *models.User, dataset *models.Dataset, action string) { | func (*NullNotifier) NotifyDatasetRecommend(optUser *models.User, dataset *models.Dataset, action string) { | ||||
| } | } | ||||
| func (*NullNotifier) NotifyCreateImage(optUserId int64, image models.Image) { | |||||
| func (*NullNotifier) NotifyCreateImage(doer *models.User, image models.Image) { | |||||
| } | } | ||||
| func (*NullNotifier) NotifyImageRecommend(optUser *models.User, imageId int64, action string) { | func (*NullNotifier) NotifyImageRecommend(optUser *models.User, imageId int64, action string) { | ||||
| } | } | ||||
| func (*NullNotifier) NotifyChangeUserAvatar(user *models.User) { | |||||
| func (*NullNotifier) NotifyChangeUserAvatar(user *models.User, form auth.AvatarForm) { | |||||
| } | } | ||||
| @@ -6,11 +6,11 @@ package notification | |||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/auth" | |||||
| "code.gitea.io/gitea/modules/notification/action" | "code.gitea.io/gitea/modules/notification/action" | ||||
| "code.gitea.io/gitea/modules/notification/base" | "code.gitea.io/gitea/modules/notification/base" | ||||
| "code.gitea.io/gitea/modules/notification/indexer" | "code.gitea.io/gitea/modules/notification/indexer" | ||||
| "code.gitea.io/gitea/modules/notification/mail" | "code.gitea.io/gitea/modules/notification/mail" | ||||
| "code.gitea.io/gitea/modules/notification/task" | |||||
| "code.gitea.io/gitea/modules/notification/ui" | "code.gitea.io/gitea/modules/notification/ui" | ||||
| "code.gitea.io/gitea/modules/notification/webhook" | "code.gitea.io/gitea/modules/notification/webhook" | ||||
| "code.gitea.io/gitea/modules/repository" | "code.gitea.io/gitea/modules/repository" | ||||
| @@ -36,7 +36,6 @@ func NewContext() { | |||||
| RegisterNotifier(indexer.NewNotifier()) | RegisterNotifier(indexer.NewNotifier()) | ||||
| RegisterNotifier(webhook.NewNotifier()) | RegisterNotifier(webhook.NewNotifier()) | ||||
| RegisterNotifier(action.NewNotifier()) | RegisterNotifier(action.NewNotifier()) | ||||
| RegisterNotifier(task.NewNotifier()) | |||||
| } | } | ||||
| // NotifyUploadAttachment notifies attachment upload message to notifiers | // NotifyUploadAttachment notifies attachment upload message to notifiers | ||||
| @@ -273,9 +272,9 @@ func NotifySyncDeleteRef(pusher *models.User, repo *models.Repository, refType, | |||||
| } | } | ||||
| // NotifyWechatBind notifies wechat bind | // NotifyWechatBind notifies wechat bind | ||||
| func NotifyWechatBind(userId int64, wechatOpenId string) { | |||||
| func NotifyWechatBind(user *models.User, wechatOpenId string) { | |||||
| for _, notifier := range notifiers { | for _, notifier := range notifiers { | ||||
| notifier.NotifyWechatBind(userId, wechatOpenId) | |||||
| notifier.NotifyWechatBind(user, wechatOpenId) | |||||
| } | } | ||||
| } | } | ||||
| @@ -287,9 +286,9 @@ func NotifyDatasetRecommend(optUser *models.User, dataset *models.Dataset, actio | |||||
| } | } | ||||
| // NotifyDatasetRecommend | // NotifyDatasetRecommend | ||||
| func NotifyCreateImage(optUserId int64, image models.Image) { | |||||
| func NotifyCreateImage(doer *models.User, image models.Image) { | |||||
| for _, notifier := range notifiers { | for _, notifier := range notifiers { | ||||
| notifier.NotifyCreateImage(optUserId, image) | |||||
| notifier.NotifyCreateImage(doer, image) | |||||
| } | } | ||||
| } | } | ||||
| @@ -301,8 +300,8 @@ func NotifyImageRecommend(optUser *models.User, imageId int64, action string) { | |||||
| } | } | ||||
| // NotifyDatasetRecommend | // NotifyDatasetRecommend | ||||
| func NotifyChangeUserAvatar(user *models.User) { | |||||
| func NotifyChangeUserAvatar(user *models.User, form auth.AvatarForm) { | |||||
| for _, notifier := range notifiers { | for _, notifier := range notifiers { | ||||
| notifier.NotifyChangeUserAvatar(user) | |||||
| notifier.NotifyChangeUserAvatar(user, form) | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,157 +0,0 @@ | |||||
| package task | |||||
| import ( | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/notification/base" | |||||
| "code.gitea.io/gitea/modules/repository" | |||||
| "code.gitea.io/gitea/services/task" | |||||
| "strings" | |||||
| ) | |||||
| type taskNotifier struct { | |||||
| base.NullNotifier | |||||
| } | |||||
| var ( | |||||
| _ base.Notifier = &taskNotifier{} | |||||
| ) | |||||
| // NewNotifier create a new actionNotifier notifier | |||||
| func NewNotifier() base.Notifier { | |||||
| return &taskNotifier{} | |||||
| } | |||||
| func (t *taskNotifier) NotifyNewIssue(issue *models.Issue) { | |||||
| task.Accomplish(issue.Poster.ID, models.TaskTypeNewIssue) | |||||
| } | |||||
| // NotifyIssueChangeStatus notifies close or reopen issue to notifiers | |||||
| func (t *taskNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) { | |||||
| task.Accomplish(doer.ID, models.TaskTypeIssueChangeStatus) | |||||
| } | |||||
| // NotifyCreateIssueComment notifies comment on an issue to notifiers | |||||
| func (t *taskNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository, | |||||
| issue *models.Issue, comment *models.Comment) { | |||||
| task.Accomplish(doer.ID, models.TaskTypeCreateIssueComment) | |||||
| } | |||||
| func (t *taskNotifier) NotifyNewPullRequest(pull *models.PullRequest) { | |||||
| task.Accomplish(pull.Issue.Poster.ID, models.TaskTypeNewPullRequest) | |||||
| } | |||||
| func (t *taskNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) { | |||||
| task.Accomplish(doer.ID, models.TaskTypeRenameRepository) | |||||
| } | |||||
| func (t *taskNotifier) NotifyAliasRepository(doer *models.User, repo *models.Repository, oldAlias string) { | |||||
| task.Accomplish(doer.ID, models.TaskTypeAliasRepository) | |||||
| } | |||||
| func (t *taskNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) { | |||||
| task.Accomplish(doer.ID, models.TaskTypeTransferRepository) | |||||
| } | |||||
| func (t *taskNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) { | |||||
| if !repo.IsPrivate { | |||||
| task.Accomplish(doer.ID, models.TaskTypeCreatePublicRepository) | |||||
| } | |||||
| } | |||||
| func (t *taskNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) { | |||||
| task.Accomplish(doer.ID, models.TaskTypeForkRepository) | |||||
| } | |||||
| func (t *taskNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) { | |||||
| for _, lines := range review.CodeComments { | |||||
| for _, comments := range lines { | |||||
| for _, _ = range comments { | |||||
| task.Accomplish(review.Reviewer.ID, models.TaskTypePullRequestReview) | |||||
| } | |||||
| } | |||||
| } | |||||
| if review.Type != models.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" { | |||||
| switch review.Type { | |||||
| case models.ReviewTypeApprove: | |||||
| task.Accomplish(review.Reviewer.ID, models.TaskTypeApprovePullRequest) | |||||
| case models.ReviewTypeReject: | |||||
| task.Accomplish(review.Reviewer.ID, models.TaskTypeRejectPullRequest) | |||||
| default: | |||||
| task.Accomplish(review.Reviewer.ID, models.TaskTypeCommentPull) | |||||
| } | |||||
| } | |||||
| } | |||||
| func (t *taskNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) { | |||||
| task.Accomplish(doer.ID, models.TaskTypeMergePullRequest) | |||||
| } | |||||
| func (t *taskNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) { | |||||
| task.Accomplish(repo.OwnerID, models.TaskTypeSyncPushCommits) | |||||
| } | |||||
| func (t *taskNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) { | |||||
| task.Accomplish(repo.OwnerID, models.TaskTypeSyncCreateRef) | |||||
| } | |||||
| func (t *taskNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) { | |||||
| task.Accomplish(repo.OwnerID, models.TaskTypeSyncDeleteRef) | |||||
| } | |||||
| func (t *taskNotifier) NotifyOtherTask(doer *models.User, repo *models.Repository, id string, name string, optype models.ActionType) { | |||||
| switch optype { | |||||
| case models.ActionUploadAttachment: | |||||
| task.Accomplish(doer.ID, models.TaskTypeUploadAttachment) | |||||
| case models.ActionCreateDebugGPUTask, | |||||
| models.ActionCreateDebugNPUTask, | |||||
| models.ActionCreateTrainTask, | |||||
| models.ActionCreateInferenceTask, | |||||
| models.ActionCreateBenchMarkTask, | |||||
| models.ActionCreateGPUTrainTask: | |||||
| task.Accomplish(doer.ID, models.TaskTypeCreateCloudbrainTask) | |||||
| case models.ActionCreateNewModelTask: | |||||
| task.Accomplish(doer.ID, models.TaskTypeCreateModel) | |||||
| } | |||||
| return | |||||
| } | |||||
| func (t *taskNotifier) NotifyWechatBind(userId int64, wechatOpenId string) { | |||||
| task.Accomplish(userId, models.TaskTypeBindWechat) | |||||
| } | |||||
| func (t *taskNotifier) NotifyDatasetRecommend(optUser *models.User, dataset *models.Dataset, action string) { | |||||
| switch action { | |||||
| case "recommend": | |||||
| userIds, err := models.GetAllUserIdByDatasetId(dataset.ID) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| for _, userId := range userIds { | |||||
| task.Accomplish(userId, models.TaskTypeDatasetRecommended) | |||||
| } | |||||
| } | |||||
| } | |||||
| func (t *taskNotifier) NotifyCreateImage(optUserId int64, image models.Image) { | |||||
| if !image.IsPrivate { | |||||
| task.Accomplish(optUserId, models.TaskTypeCreatePublicImage) | |||||
| } | |||||
| } | |||||
| func (t *taskNotifier) NotifyImageRecommend(optUser *models.User, imageId int64, action string) { | |||||
| switch action { | |||||
| case "recommend": | |||||
| task.Accomplish(optUser.ID, models.TaskTypeImageRecommend) | |||||
| } | |||||
| } | |||||
| func (t *taskNotifier) NotifyChangeUserAvatar(user *models.User) { | |||||
| task.Accomplish(user.ID, models.TaskTypeChangeUserAvatar) | |||||
| } | |||||
| func (t *taskNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) { | |||||
| task.Accomplish(pusher.ID, models.TaskTypePushCommits) | |||||
| } | |||||
| @@ -99,11 +99,11 @@ func IncrBy(key string, n int64) (int64, error) { | |||||
| } | } | ||||
| func Expire(key string, expireSeconds int64) error { | |||||
| func Expire(key string, expireTime time.Duration) error { | |||||
| redisClient := labelmsg.Get() | redisClient := labelmsg.Get() | ||||
| defer redisClient.Close() | defer redisClient.Close() | ||||
| _, err := redisClient.Do("EXPIRE", key, expireSeconds) | |||||
| _, err := redisClient.Do("EXPIRE", key, int64(expireTime.Seconds())) | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -1,6 +1,8 @@ | |||||
| package redis_key | package redis_key | ||||
| import "fmt" | |||||
| import ( | |||||
| "fmt" | |||||
| ) | |||||
| const REWARD_REDIS_PREFIX = "reward" | const REWARD_REDIS_PREFIX = "reward" | ||||
| @@ -11,6 +13,7 @@ func RewardOperateLock(requestId string, sourceType string, operateType string) | |||||
| func RewardOperateNotification() string { | func RewardOperateNotification() string { | ||||
| return KeyJoin(REWARD_REDIS_PREFIX, "operate", "notification") | return KeyJoin(REWARD_REDIS_PREFIX, "operate", "notification") | ||||
| } | } | ||||
| func RewardTaskRunningLock(taskId int64) string { | func RewardTaskRunningLock(taskId int64) string { | ||||
| return KeyJoin(REWARD_REDIS_PREFIX, "periodic_task", fmt.Sprint(taskId), "lock") | return KeyJoin(REWARD_REDIS_PREFIX, "periodic_task", fmt.Sprint(taskId), "lock") | ||||
| } | } | ||||
| @@ -0,0 +1,10 @@ | |||||
| package redis_key | |||||
| import "time" | |||||
| const SERIAL_REDIS_PREFIX = "serial" | |||||
| func RewardSerialCounter(now time.Time) string { | |||||
| h := now.Format("200601021504") | |||||
| return KeyJoin(SERIAL_REDIS_PREFIX, "reward_operate", h, "counter") | |||||
| } | |||||
| @@ -232,7 +232,7 @@ var ( | |||||
| TimeoutStep: 10 * time.Second, | TimeoutStep: 10 * time.Second, | ||||
| MaxTimeout: 60 * time.Second, | MaxTimeout: 60 * time.Second, | ||||
| EventSourceUpdateTime: 10 * time.Second, | EventSourceUpdateTime: 10 * time.Second, | ||||
| RewardNotifyUpdateTime: 3 * time.Second, | |||||
| RewardNotifyUpdateTime: 2 * time.Second, | |||||
| }, | }, | ||||
| Admin: struct { | Admin: struct { | ||||
| UserPagingNum int | UserPagingNum int | ||||
| @@ -549,7 +549,9 @@ var ( | |||||
| WechatAuthSwitch bool | WechatAuthSwitch bool | ||||
| //point config | //point config | ||||
| CloudBrainTaskPointPaySwitch bool | |||||
| CloudBrainPaySwitch bool | |||||
| CloudBrainPayDelay time.Duration | |||||
| CloudBrainPayInterval time.Duration | |||||
| //nginx proxy | //nginx proxy | ||||
| PROXYURL string | PROXYURL string | ||||
| @@ -1380,7 +1382,9 @@ func NewContext() { | |||||
| WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(false) | WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(false) | ||||
| sec = Cfg.Section("point") | sec = Cfg.Section("point") | ||||
| CloudBrainTaskPointPaySwitch = sec.Key("CLOUDBRAIN_PAY_SWITCH").MustBool(false) | |||||
| CloudBrainPaySwitch = sec.Key("CLOUDBRAIN_PAY_SWITCH").MustBool(false) | |||||
| CloudBrainPayDelay = sec.Key("CLOUDBRAIN_PAY_DELAY").MustDuration(30 * time.Minute) | |||||
| CloudBrainPayInterval = sec.Key("CLOUDBRAIN_PAY_INTERVAL").MustDuration(60 * time.Minute) | |||||
| SetRadarMapConfig() | SetRadarMapConfig() | ||||
| @@ -233,7 +233,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| if !reward.IsPointBalanceEnough(ctx.User.ID, jobType, resourceSpecId) { | if !reward.IsPointBalanceEnough(ctx.User.ID, jobType, resourceSpecId) { | ||||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", 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) | cloudBrainNewDataPrepare(ctx) | ||||
| ctx.RenderWithErr("point balance not enough", tpl, &form) | |||||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tpl, &form) | |||||
| return | return | ||||
| } | } | ||||
| @@ -319,7 +319,7 @@ func CloudBrainRestart(ctx *context.Context) { | |||||
| if !reward.IsPointBalanceEnough(ctx.User.ID, task.JobType, task.ResourceSpecId) { | if !reward.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) | log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, task.JobType, task.ResourceSpecId) | ||||
| resultCode = "-1" | resultCode = "-1" | ||||
| errorMsg = "insufficient points balance" | |||||
| errorMsg = models.ErrInsufficientPointsBalance{}.Error() | |||||
| break | break | ||||
| } | } | ||||
| @@ -737,7 +737,7 @@ func CloudBrainAdminCommitImage(ctx *context.Context, form auth.CommitAdminImage | |||||
| UID: ctx.User.ID, | UID: ctx.User.ID, | ||||
| Type: models.GetRecommondType(form.IsRecommend), | Type: models.GetRecommondType(form.IsRecommend), | ||||
| Place: form.Place, | Place: form.Place, | ||||
| }) | |||||
| }, ctx.User) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("CommitImagefailed") | log.Error("CommitImagefailed") | ||||
| if models.IsErrImageTagExist(err) { | if models.IsErrImageTagExist(err) { | ||||
| @@ -784,7 +784,7 @@ func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrain | |||||
| CloudBrainType: form.Type, | CloudBrainType: form.Type, | ||||
| Topics: validTopics, | Topics: validTopics, | ||||
| UID: ctx.User.ID, | UID: ctx.User.ID, | ||||
| }) | |||||
| }, ctx.User) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("CommitImage(%s) failed:%v", ctx.Cloudbrain.JobName, err.Error(), ctx.Data["msgID"]) | log.Error("CommitImage(%s) failed:%v", ctx.Cloudbrain.JobName, err.Error(), ctx.Data["msgID"]) | ||||
| if models.IsErrImageTagExist(err) { | if models.IsErrImageTagExist(err) { | ||||
| @@ -1862,7 +1862,7 @@ func BenchMarkAlgorithmCreate(ctx *context.Context, form auth.CreateCloudBrainFo | |||||
| if !reward.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) { | if !reward.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) | log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) | ||||
| cloudBrainNewDataPrepare(ctx) | cloudBrainNewDataPrepare(ctx) | ||||
| ctx.RenderWithErr("point balance not enough", tplCloudBrainBenchmarkNew, &form) | |||||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tplCloudBrainBenchmarkNew, &form) | |||||
| return | return | ||||
| } | } | ||||
| @@ -2024,7 +2024,7 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm) | |||||
| if !reward.IsPointBalanceEnough(ctx.User.ID, jobType, resourceSpecId) { | if !reward.IsPointBalanceEnough(ctx.User.ID, jobType, resourceSpecId) { | ||||
| log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", 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) | cloudBrainNewDataPrepare(ctx) | ||||
| ctx.RenderWithErr("point balance not enough", tpl, &form) | |||||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tpl, &form) | |||||
| return | return | ||||
| } | } | ||||
| @@ -210,7 +210,7 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm | |||||
| if !reward.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeDebug), resourceSpecId) { | if !reward.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) | log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) | ||||
| cloudBrainNewDataPrepare(ctx) | cloudBrainNewDataPrepare(ctx) | ||||
| ctx.RenderWithErr("point balance not enough", tplModelArtsNotebookNew, &form) | |||||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tplModelArtsNotebookNew, &form) | |||||
| return | return | ||||
| } | } | ||||
| count, err := models.GetCloudbrainNotebookCountByUserID(ctx.User.ID) | count, err := models.GetCloudbrainNotebookCountByUserID(ctx.User.ID) | ||||
| @@ -429,7 +429,7 @@ func NotebookManage(ctx *context.Context) { | |||||
| if !reward.IsPointBalanceEnough(ctx.User.ID, task.JobType, task.ResourceSpecId) { | if !reward.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) | log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, task.JobType, task.ResourceSpecId) | ||||
| resultCode = "-1" | resultCode = "-1" | ||||
| errorMsg = "point balance not enough" | |||||
| errorMsg = models.ErrInsufficientPointsBalance{}.Error() | |||||
| break | break | ||||
| return | return | ||||
| } | } | ||||
| @@ -1005,7 +1005,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) | |||||
| if !reward.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeTrain), resourceSpecId) { | if !reward.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) | log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) | ||||
| cloudBrainNewDataPrepare(ctx) | cloudBrainNewDataPrepare(ctx) | ||||
| ctx.RenderWithErr("point balance not enough", tplModelArtsTrainJobNew, &form) | |||||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tplModelArtsTrainJobNew, &form) | |||||
| return | return | ||||
| } | } | ||||
| count, err := models.GetCloudbrainTrainJobCountByUserID(ctx.User.ID) | count, err := models.GetCloudbrainTrainJobCountByUserID(ctx.User.ID) | ||||
| @@ -1854,7 +1854,7 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference | |||||
| if !reward.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeInference), resourceSpecId) { | if !reward.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) | log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, string(models.JobTypeBenchmark), resourceSpecId) | ||||
| inferenceJobErrorNewDataPrepare(ctx, form) | inferenceJobErrorNewDataPrepare(ctx, form) | ||||
| ctx.RenderWithErr("point balance not enough", tplModelArtsInferenceJobNew, &form) | |||||
| ctx.RenderWithErr(models.ErrInsufficientPointsBalance{}.Error(), tplModelArtsInferenceJobNew, &form) | |||||
| return | return | ||||
| } | } | ||||
| count, err := models.GetCloudbrainInferenceJobCountByUserID(ctx.User.ID) | count, err := models.GetCloudbrainInferenceJobCountByUserID(ctx.User.ID) | ||||
| @@ -1,8 +1,10 @@ | |||||
| package point | package point | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/routers/response" | "code.gitea.io/gitea/routers/response" | ||||
| "code.gitea.io/gitea/services/reward" | |||||
| "code.gitea.io/gitea/services/reward/point/account" | "code.gitea.io/gitea/services/reward/point/account" | ||||
| "net/http" | "net/http" | ||||
| ) | ) | ||||
| @@ -29,3 +31,47 @@ func GetPointAccount(ctx *context.Context) { | |||||
| } | } | ||||
| ctx.JSON(http.StatusOK, response.SuccessWithData(res)) | ctx.JSON(http.StatusOK, response.SuccessWithData(res)) | ||||
| } | } | ||||
| func GetPointRecordList(ctx *context.Context) { | |||||
| operateType := ctx.Query("operate") | |||||
| page := ctx.QueryInt("page") | |||||
| var orderBy models.RewardOperateOrderBy | |||||
| switch ctx.Query("sort") { | |||||
| default: | |||||
| orderBy = models.RewardOrderByID | |||||
| } | |||||
| t := models.GetRewardOperateTypeInstance(operateType) | |||||
| if t == "" { | |||||
| ctx.JSON(http.StatusOK, response.ServerError("param error")) | |||||
| return | |||||
| } | |||||
| r, err := reward.GetRewardRecordList(models.RewardRecordListOpts{ | |||||
| ListOptions: models.ListOptions{PageSize: 20, Page: page}, | |||||
| UserId: ctx.User.ID, | |||||
| OperateType: t, | |||||
| RewardType: models.RewardTypePoint, | |||||
| OrderBy: orderBy, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, response.SuccessWithData(r)) | |||||
| return | |||||
| } | |||||
| func OperatePointAccountBalance(ctx *context.Context, req models.AdminRewardOperateReq) { | |||||
| req.RewardType = models.RewardTypePoint | |||||
| if req.OperateType.Name() == "" { | |||||
| ctx.JSON(http.StatusOK, "param error") | |||||
| return | |||||
| } | |||||
| err := reward.AdminBalanceOperate(req, ctx.User) | |||||
| if err != nil { | |||||
| ctx.JSON(http.StatusOK, response.ServerError(err.Error())) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, response.Success()) | |||||
| } | |||||
| @@ -324,6 +324,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("/", routers.Home) | m.Get("/", routers.Home) | ||||
| m.Get("/dashboard", routers.Dashboard) | m.Get("/dashboard", routers.Dashboard) | ||||
| go routers.SocketManager.Run() | go routers.SocketManager.Run() | ||||
| go task.RunTask() | |||||
| m.Get("/action/notification", routers.ActionNotification) | m.Get("/action/notification", routers.ActionNotification) | ||||
| m.Get("/reward/notification", routers.ActionNotification) | m.Get("/reward/notification", routers.ActionNotification) | ||||
| m.Get("/recommend/org", routers.RecommendOrgFromPromote) | m.Get("/recommend/org", routers.RecommendOrgFromPromote) | ||||
| @@ -594,6 +595,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Group("/reward/point", func() { | m.Group("/reward/point", func() { | ||||
| m.Get("/limiter/list", point.GetPointLimitConfigList) | m.Get("/limiter/list", point.GetPointLimitConfigList) | ||||
| m.Post("/limiter/add", bindIgnErr(models.LimitConfigVO{}), point.AddPointLimitConfig) | m.Post("/limiter/add", bindIgnErr(models.LimitConfigVO{}), point.AddPointLimitConfig) | ||||
| m.Post("/operate", binding.Bind(models.AdminRewardOperateReq{}), point.OperatePointAccountBalance) | |||||
| }) | }) | ||||
| m.Group("/task/config", func() { | m.Group("/task/config", func() { | ||||
| @@ -601,6 +603,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Post("/add", bindIgnErr(models.TaskConfigWithLimit{}), task.AddTaskConfig) | m.Post("/add", bindIgnErr(models.TaskConfigWithLimit{}), task.AddTaskConfig) | ||||
| m.Post("/add/batch", bindIgnErr(models.BatchLimitConfigVO{}), task.BatchAddTaskConfig) | m.Post("/add/batch", bindIgnErr(models.BatchLimitConfigVO{}), task.BatchAddTaskConfig) | ||||
| }) | }) | ||||
| }, adminReq) | }, adminReq) | ||||
| // ***** END: Admin ***** | // ***** END: Admin ***** | ||||
| @@ -1330,6 +1333,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Group("/reward/point", func() { | m.Group("/reward/point", func() { | ||||
| m.Get("/account", point.GetPointAccount) | m.Get("/account", point.GetPointAccount) | ||||
| m.Get("/record/list", point.GetPointRecordList) | |||||
| }, reqSignIn) | }, reqSignIn) | ||||
| if setting.API.EnableSwagger { | if setting.API.EnableSwagger { | ||||
| @@ -0,0 +1,15 @@ | |||||
| package task | |||||
| import ( | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/services/task" | |||||
| ) | |||||
| func RunTask() { | |||||
| for { | |||||
| select { | |||||
| case action := <-models.ActionChan4Task: | |||||
| task.Accomplish(action) | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -166,7 +166,7 @@ func AvatarPost(ctx *context.Context, form auth.AvatarForm) { | |||||
| if err := UpdateAvatarSetting(ctx, form, ctx.User); err != nil { | if err := UpdateAvatarSetting(ctx, form, ctx.User); err != nil { | ||||
| ctx.Flash.Error(err.Error()) | ctx.Flash.Error(err.Error()) | ||||
| } else { | } else { | ||||
| notification.NotifyChangeUserAvatar(ctx.User) | |||||
| notification.NotifyChangeUserAvatar(ctx.User, form) | |||||
| ctx.Flash.Success(ctx.Tr("settings.update_avatar_success")) | ctx.Flash.Success(ctx.Tr("settings.update_avatar_success")) | ||||
| } | } | ||||
| @@ -0,0 +1,50 @@ | |||||
| package reward | |||||
| import ( | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/util" | |||||
| "code.gitea.io/gitea/services/reward/limiter" | |||||
| ) | |||||
| func AdminBalanceOperate(req models.AdminRewardOperateReq, doer *models.User) error { | |||||
| logId := util.UUID() | |||||
| _, err := models.InsertRewardAdminLog(&models.RewardAdminLog{ | |||||
| LogId: logId, | |||||
| Amount: req.Amount, | |||||
| RewardType: req.RewardType.Name(), | |||||
| TargetUserId: req.TargetUserId, | |||||
| CreatorId: doer.ID, | |||||
| CreatorName: doer.Name, | |||||
| Remark: req.Remark, | |||||
| Status: models.RewardAdminLogProcessing, | |||||
| }) | |||||
| if err != nil { | |||||
| log.Error("AdminBalanceOperate InsertRewardAdminLog error.%v", err) | |||||
| return err | |||||
| } | |||||
| //reward | |||||
| err = Operate(&models.RewardOperateContext{ | |||||
| SourceType: models.SourceTypeAdminOperate, | |||||
| SourceId: logId, | |||||
| Tittle: "管理员操作", | |||||
| Reward: models.Reward{ | |||||
| Amount: req.Amount, | |||||
| Type: req.RewardType, | |||||
| }, | |||||
| TargetUserId: req.TargetUserId, | |||||
| RequestId: logId, | |||||
| OperateType: req.OperateType, | |||||
| Remark: req.Remark, | |||||
| RejectPolicy: limiter.JustReject, | |||||
| }) | |||||
| if err != nil { | |||||
| log.Error("AdminBalanceOperate operate error.%v", err) | |||||
| models.UpdateRewardAdminLogStatus(logId, models.RewardAdminLogProcessing, models.RewardAdminLogFailed) | |||||
| return err | |||||
| } | |||||
| models.UpdateRewardAdminLogStatus(logId, models.RewardAdminLogProcessing, models.RewardAdminLogSuccess) | |||||
| return nil | |||||
| } | |||||
| @@ -15,9 +15,11 @@ var ( | |||||
| TrainResourceSpecs *models.ResourceSpecs | TrainResourceSpecs *models.ResourceSpecs | ||||
| ) | ) | ||||
| const RUN_CLOUDBRAIN_TASK_TITTLE = "运行云脑任务" | |||||
| //IsPointBalanceEnough check whether the user's point balance is bigger than task unit price | //IsPointBalanceEnough check whether the user's point balance is bigger than task unit price | ||||
| func IsPointBalanceEnough(targetUserId int64, jobType string, resourceSpecId int) bool { | func IsPointBalanceEnough(targetUserId int64, jobType string, resourceSpecId int) bool { | ||||
| if !setting.CloudBrainTaskPointPaySwitch { | |||||
| if !setting.CloudBrainPaySwitch { | |||||
| return true | return true | ||||
| } | } | ||||
| spec := getResourceSpec(jobType, resourceSpecId) | spec := getResourceSpec(jobType, resourceSpecId) | ||||
| @@ -33,7 +35,7 @@ func IsPointBalanceEnough(targetUserId int64, jobType string, resourceSpecId int | |||||
| } | } | ||||
| func StartCloudBrainPointDeductTask(task models.Cloudbrain) { | func StartCloudBrainPointDeductTask(task models.Cloudbrain) { | ||||
| if !setting.CloudBrainTaskPointPaySwitch { | |||||
| if !setting.CloudBrainPaySwitch { | |||||
| return | return | ||||
| } | } | ||||
| @@ -48,11 +50,12 @@ func StartCloudBrainPointDeductTask(task models.Cloudbrain) { | |||||
| TargetUserId: task.UserID, | TargetUserId: task.UserID, | ||||
| RequestId: getCloudBrainPointTaskSourceId(task), | RequestId: getCloudBrainPointTaskSourceId(task), | ||||
| OperateType: models.OperateTypeDecrease, | OperateType: models.OperateTypeDecrease, | ||||
| Delay: 30 * time.Minute, | |||||
| Interval: 60 * time.Minute, | |||||
| Delay: setting.CloudBrainPayDelay, | |||||
| Interval: setting.CloudBrainPayInterval, | |||||
| UnitAmount: spec.UnitPrice, | UnitAmount: spec.UnitPrice, | ||||
| RewardType: models.RewardTypePoint, | RewardType: models.RewardTypePoint, | ||||
| StartTime: time.Unix(int64(task.StartTime), 0), | StartTime: time.Unix(int64(task.StartTime), 0), | ||||
| Tittle: RUN_CLOUDBRAIN_TASK_TITTLE, | |||||
| }) | }) | ||||
| } | } | ||||
| @@ -61,7 +64,7 @@ func StopCloudBrainPointDeductTask(task models.Cloudbrain) { | |||||
| } | } | ||||
| func getCloudBrainPointTaskSourceId(task models.Cloudbrain) string { | func getCloudBrainPointTaskSourceId(task models.Cloudbrain) string { | ||||
| return models.SourceTypeRunCloudbrainTask.Name() + "_" + task.JobType + "_" + fmt.Sprint(task.Type) + "_" + fmt.Sprint(task.ID) | |||||
| return fmt.Sprint(task.ID) | |||||
| } | } | ||||
| func getResourceSpec(jobType string, resourceSpecId int) *models.ResourceSpec { | func getResourceSpec(jobType string, resourceSpecId int) *models.ResourceSpec { | ||||
| @@ -100,11 +103,11 @@ func StartCloudbrainPointDeductTask() { | |||||
| }() | }() | ||||
| log.Debug("try to run CloudbrainPointDeductTask") | log.Debug("try to run CloudbrainPointDeductTask") | ||||
| end := time.Now() | end := time.Now() | ||||
| start := end.Add(5 * time.Minute) | |||||
| start := end.Add(-5 * time.Minute) | |||||
| if firstTimeFlag { | if firstTimeFlag { | ||||
| //When it is executed for the first time, it needs to process the tasks of the last 1 hours. | //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 | //This is done to prevent the application from hanging for a long time | ||||
| start = end.Add(1 * time.Hour) | |||||
| start = end.Add(-1 * time.Hour) | |||||
| firstTimeFlag = false | firstTimeFlag = false | ||||
| } | } | ||||
| @@ -12,14 +12,6 @@ import ( | |||||
| "time" | "time" | ||||
| ) | ) | ||||
| type limiterRejectPolicy string | |||||
| const ( | |||||
| JustReject limiterRejectPolicy = "JUST_REJECT" | |||||
| PermittedOnce limiterRejectPolicy = "PERMITTED_ONCE" | |||||
| FillUp limiterRejectPolicy = "FillUp" | |||||
| ) | |||||
| type limiterRunner struct { | type limiterRunner struct { | ||||
| limiters []models.LimitConfig | limiters []models.LimitConfig | ||||
| index int | index int | ||||
| @@ -27,7 +19,7 @@ type limiterRunner struct { | |||||
| amount int64 | amount int64 | ||||
| limitCode string | limitCode string | ||||
| limitType models.LimitType | limitType models.LimitType | ||||
| rejectPolicy limiterRejectPolicy | |||||
| rejectPolicy models.LimiterRejectPolicy | |||||
| resultMap map[int]limitResult | resultMap map[int]limitResult | ||||
| minRealAmount int64 | minRealAmount int64 | ||||
| } | } | ||||
| @@ -46,7 +38,7 @@ func newLimitResult(isLoss bool, planAmount int64, realAmount int64) limitResult | |||||
| } | } | ||||
| } | } | ||||
| func newLimiterRunner(limitCode string, limitType models.LimitType, userId, amount int64, policy limiterRejectPolicy) *limiterRunner { | |||||
| func newLimiterRunner(limitCode string, limitType models.LimitType, userId, amount int64, policy models.LimiterRejectPolicy) *limiterRunner { | |||||
| return &limiterRunner{ | return &limiterRunner{ | ||||
| userId: userId, | userId: userId, | ||||
| amount: amount, | amount: amount, | ||||
| @@ -149,7 +141,7 @@ func (l *limiterRunner) limit(r models.LimitConfig) error { | |||||
| usedNum, err = redis_client.IncrBy(redisKey, n) | usedNum, err = redis_client.IncrBy(redisKey, n) | ||||
| } | } | ||||
| if p != nil { | if p != nil { | ||||
| redis_client.Expire(redisKey, int64(p.LeftTime.Seconds())) | |||||
| redis_client.Expire(redisKey, p.LeftTime) | |||||
| } | } | ||||
| } | } | ||||
| if usedNum > r.LimitNum { | if usedNum > r.LimitNum { | ||||
| @@ -158,16 +150,16 @@ func (l *limiterRunner) limit(r models.LimitConfig) error { | |||||
| return errors.New(fmt.Sprintf("%s:over limit", r.Tittle)) | return errors.New(fmt.Sprintf("%s:over limit", r.Tittle)) | ||||
| } | } | ||||
| switch l.rejectPolicy { | switch l.rejectPolicy { | ||||
| case FillUp: | |||||
| case models.FillUp: | |||||
| exceed := usedNum - r.LimitNum | exceed := usedNum - r.LimitNum | ||||
| realAmount := l.amount - exceed | realAmount := l.amount - exceed | ||||
| redis_client.IncrBy(redisKey, -1*exceed) | redis_client.IncrBy(redisKey, -1*exceed) | ||||
| l.resultMap[l.index] = newLimitResult(true, l.amount, realAmount) | l.resultMap[l.index] = newLimitResult(true, l.amount, realAmount) | ||||
| return nil | return nil | ||||
| case JustReject: | |||||
| case models.JustReject: | |||||
| redis_client.IncrBy(redisKey, -1*l.amount) | redis_client.IncrBy(redisKey, -1*l.amount) | ||||
| return errors.New(fmt.Sprintf("%s:over limit", r.Tittle)) | return errors.New(fmt.Sprintf("%s:over limit", r.Tittle)) | ||||
| case PermittedOnce: | |||||
| case models.PermittedOnce: | |||||
| l.resultMap[l.index] = newLimitResult(false, l.amount, l.amount) | l.resultMap[l.index] = newLimitResult(false, l.amount, l.amount) | ||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -200,8 +192,11 @@ func (l *limiterRunner) countInPeriod(r models.LimitConfig, p *models.PeriodResu | |||||
| } | } | ||||
| } | } | ||||
| func CheckLimitWithFillUp(limitCode string, limitType models.LimitType, userId, amount int64) (int64, error) { | |||||
| r := newLimiterRunner(limitCode, limitType, userId, amount, FillUp) | |||||
| func CheckLimit(limitCode string, limitType models.LimitType, userId, amount int64, rejectPolicy models.LimiterRejectPolicy) (int64, error) { | |||||
| if rejectPolicy == "" { | |||||
| rejectPolicy = models.JustReject | |||||
| } | |||||
| r := newLimiterRunner(limitCode, limitType, userId, amount, rejectPolicy) | |||||
| err := r.Run() | err := r.Run() | ||||
| if err != nil { | if err != nil { | ||||
| return 0, err | return 0, err | ||||
| @@ -209,18 +204,6 @@ func CheckLimitWithFillUp(limitCode string, limitType models.LimitType, userId, | |||||
| return r.minRealAmount, nil | return r.minRealAmount, nil | ||||
| } | } | ||||
| func CheckLimitWithPermittedOnce(limitCode string, limitType models.LimitType, userId, amount int64) error { | |||||
| r := newLimiterRunner(limitCode, limitType, userId, amount, PermittedOnce) | |||||
| err := r.Run() | |||||
| return err | |||||
| } | |||||
| func CheckLimit(limitCode string, limitType models.LimitType, userId, amount int64) error { | |||||
| r := newLimiterRunner(limitCode, limitType, userId, amount, JustReject) | |||||
| err := r.Run() | |||||
| return err | |||||
| } | |||||
| func GetLimiters(limitCode string, limitType models.LimitType) ([]models.LimitConfig, error) { | func GetLimiters(limitCode string, limitType models.LimitType) ([]models.LimitConfig, error) { | ||||
| limiters, err := GetLimitersByLimitType(limitType) | limiters, err := GetLimitersByLimitType(limitType) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -5,7 +5,6 @@ import ( | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/redis/redis_key" | "code.gitea.io/gitea/modules/redis/redis_key" | ||||
| "code.gitea.io/gitea/modules/redis/redis_lock" | "code.gitea.io/gitea/modules/redis/redis_lock" | ||||
| "code.gitea.io/gitea/modules/util" | |||||
| "code.gitea.io/gitea/services/reward/point" | "code.gitea.io/gitea/services/reward/point" | ||||
| "errors" | "errors" | ||||
| "fmt" | "fmt" | ||||
| @@ -69,7 +68,7 @@ func Operate(ctx *models.RewardOperateContext) error { | |||||
| } | } | ||||
| //new reward operate record | //new reward operate record | ||||
| recordId, err := initAwardOperateRecord(ctx) | |||||
| recordId, err := initRewardOperateRecord(ctx) | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -110,9 +109,12 @@ func isHandled(sourceType string, requestId string, operateType string) (bool, e | |||||
| } | } | ||||
| func initAwardOperateRecord(ctx *models.RewardOperateContext) (string, error) { | |||||
| func initRewardOperateRecord(ctx *models.RewardOperateContext) (string, error) { | |||||
| sn, err := generateOperateSerialNo(ctx.OperateType, ctx.Reward.Type) | |||||
| if err != nil { | |||||
| return "", err | |||||
| } | |||||
| record := &models.RewardOperateRecord{ | record := &models.RewardOperateRecord{ | ||||
| RecordId: util.UUID(), | |||||
| UserId: ctx.TargetUserId, | UserId: ctx.TargetUserId, | ||||
| Amount: ctx.Reward.Amount, | Amount: ctx.Reward.Amount, | ||||
| RewardType: ctx.Reward.Type.Name(), | RewardType: ctx.Reward.Type.Name(), | ||||
| @@ -122,17 +124,22 @@ func initAwardOperateRecord(ctx *models.RewardOperateContext) (string, error) { | |||||
| OperateType: ctx.OperateType.Name(), | OperateType: ctx.OperateType.Name(), | ||||
| Status: models.OperateStatusOperating, | Status: models.OperateStatusOperating, | ||||
| Remark: ctx.Remark, | Remark: ctx.Remark, | ||||
| Tittle: ctx.Tittle, | |||||
| SerialNo: sn, | |||||
| } | } | ||||
| _, err := models.InsertAwardOperateRecord(record) | |||||
| _, err = models.InsertRewardOperateRecord(record) | |||||
| if err != nil { | if err != nil { | ||||
| return "", err | return "", err | ||||
| } | } | ||||
| return record.RecordId, nil | |||||
| return record.SerialNo, nil | |||||
| } | } | ||||
| func createPeriodicRewardOperateRecord(ctx *models.StartPeriodicTaskOpts) (string, error) { | func createPeriodicRewardOperateRecord(ctx *models.StartPeriodicTaskOpts) (string, error) { | ||||
| sn, err := generateOperateSerialNo(ctx.OperateType, ctx.RewardType) | |||||
| if err != nil { | |||||
| return "", err | |||||
| } | |||||
| record := &models.RewardOperateRecord{ | record := &models.RewardOperateRecord{ | ||||
| RecordId: util.UUID(), | |||||
| UserId: ctx.TargetUserId, | UserId: ctx.TargetUserId, | ||||
| Amount: 0, | Amount: 0, | ||||
| RewardType: ctx.RewardType.Name(), | RewardType: ctx.RewardType.Name(), | ||||
| @@ -142,12 +149,14 @@ func createPeriodicRewardOperateRecord(ctx *models.StartPeriodicTaskOpts) (strin | |||||
| OperateType: ctx.OperateType.Name(), | OperateType: ctx.OperateType.Name(), | ||||
| Status: models.OperateStatusOperating, | Status: models.OperateStatusOperating, | ||||
| Remark: ctx.Remark, | Remark: ctx.Remark, | ||||
| Tittle: ctx.Tittle, | |||||
| SerialNo: sn, | |||||
| } | } | ||||
| _, err := models.InsertAwardOperateRecord(record) | |||||
| _, err = models.InsertRewardOperateRecord(record) | |||||
| if err != nil { | if err != nil { | ||||
| return "", err | return "", err | ||||
| } | } | ||||
| return record.RecordId, nil | |||||
| return record.SerialNo, nil | |||||
| } | } | ||||
| func updateAwardOperateRecordStatus(sourceType, requestId, oldStatus, newStatus string) error { | func updateAwardOperateRecordStatus(sourceType, requestId, oldStatus, newStatus string) error { | ||||
| @@ -230,5 +239,30 @@ func StopPeriodicTask(sourceType models.SourceType, sourceId string, operateType | |||||
| } | } | ||||
| now := time.Now() | now := time.Now() | ||||
| RunRewardTask(*task, now) | RunRewardTask(*task, now) | ||||
| return models.StopPeriodicTask(task.ID, task.OperateRecordId, now) | |||||
| return models.StopPeriodicTask(task.ID, task.OperateSerialNo, now) | |||||
| } | |||||
| func generateOperateSerialNo(operateType models.RewardOperateType, rewardType models.RewardType) (string, error) { | |||||
| s, err := GetSerialNoByRedis() | |||||
| if err != nil { | |||||
| return "", err | |||||
| } | |||||
| switch operateType { | |||||
| case models.OperateTypeIncrease: | |||||
| s += "1" | |||||
| case models.OperateTypeDecrease: | |||||
| s += "2" | |||||
| default: | |||||
| s += "9" | |||||
| } | |||||
| switch rewardType { | |||||
| case models.RewardTypePoint: | |||||
| s += "1" | |||||
| default: | |||||
| s += "9" | |||||
| } | |||||
| return s, nil | |||||
| } | } | ||||
| @@ -15,7 +15,7 @@ func NewRewardPeriodicTask(operateRecordId string, opts *models.StartPeriodicTas | |||||
| task.DelaySeconds = int64(opts.Delay.Seconds()) | task.DelaySeconds = int64(opts.Delay.Seconds()) | ||||
| task.IntervalSeconds = int64(opts.Interval.Seconds()) | task.IntervalSeconds = int64(opts.Interval.Seconds()) | ||||
| task.Amount = opts.UnitAmount | task.Amount = opts.UnitAmount | ||||
| task.OperateRecordId = operateRecordId | |||||
| task.OperateSerialNo = operateRecordId | |||||
| task.Status = models.PeriodicTaskStatusRunning | task.Status = models.PeriodicTaskStatusRunning | ||||
| task.NextExecuteTime = timeutil.TimeStamp(opts.StartTime.Add(opts.Delay).Unix()) | task.NextExecuteTime = timeutil.TimeStamp(opts.StartTime.Add(opts.Delay).Unix()) | ||||
| @@ -54,9 +54,9 @@ func RunRewardTask(t models.RewardPeriodicTask, now time.Time) { | |||||
| return | return | ||||
| } | } | ||||
| defer lock.UnLock() | defer lock.UnLock() | ||||
| record, err := models.GetPointOperateRecordByRecordId(t.OperateRecordId) | |||||
| record, err := models.GetPointOperateRecordBySerialNo(t.OperateSerialNo) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("RunRewardTask. GetPointOperateRecordByRecordId error. %v", err) | |||||
| log.Error("RunRewardTask. GetPointOperateRecordBySerialNo error. %v", err) | |||||
| return | return | ||||
| } | } | ||||
| if record.Status != models.OperateStatusOperating { | if record.Status != models.OperateStatusOperating { | ||||
| @@ -75,7 +75,7 @@ func RunRewardTask(t models.RewardPeriodicTask, now time.Time) { | |||||
| } | } | ||||
| err = operator.Operate(&models.RewardOperateContext{ | err = operator.Operate(&models.RewardOperateContext{ | ||||
| SourceType: models.SourceTypeRunCloudbrainTask, | SourceType: models.SourceTypeRunCloudbrainTask, | ||||
| SourceId: t.OperateRecordId, | |||||
| SourceId: t.OperateSerialNo, | |||||
| Reward: models.Reward{ | Reward: models.Reward{ | ||||
| Amount: n * t.Amount, | Amount: n * t.Amount, | ||||
| Type: models.GetRewardTypeInstance(record.RewardType), | Type: models.GetRewardTypeInstance(record.RewardType), | ||||
| @@ -18,7 +18,7 @@ type PointOperator struct { | |||||
| } | } | ||||
| func (operator *PointOperator) IsLimited(ctx *models.RewardOperateContext) bool { | func (operator *PointOperator) IsLimited(ctx *models.RewardOperateContext) bool { | ||||
| realAmount, err := limiter.CheckLimitWithFillUp(ctx.SourceType.Name(), models.LimitTypeRewardPoint, ctx.TargetUserId, ctx.Reward.Amount) | |||||
| realAmount, err := limiter.CheckLimit(ctx.SourceType.Name(), models.LimitTypeRewardPoint, ctx.TargetUserId, ctx.Reward.Amount, ctx.RejectPolicy) | |||||
| if err != nil { | if err != nil { | ||||
| return true | return true | ||||
| } | } | ||||
| @@ -0,0 +1,20 @@ | |||||
| package reward | |||||
| import "code.gitea.io/gitea/models" | |||||
| type RecordResponse struct { | |||||
| Records []models.RewardOperateRecordShow | |||||
| Total int64 | |||||
| } | |||||
| func GetRewardRecordList(opts models.RewardRecordListOpts) (*RecordResponse, error) { | |||||
| l, n, err := models.GetRewardRecordList(opts) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| r := make([]models.RewardOperateRecordShow, 0) | |||||
| for _, v := range l { | |||||
| r = append(r, v.ToShow()) | |||||
| } | |||||
| return &RecordResponse{Records: r, Total: n}, nil | |||||
| } | |||||
| @@ -0,0 +1,21 @@ | |||||
| package reward | |||||
| import ( | |||||
| "code.gitea.io/gitea/modules/redis/redis_client" | |||||
| "code.gitea.io/gitea/modules/redis/redis_key" | |||||
| "fmt" | |||||
| "math/rand" | |||||
| "time" | |||||
| ) | |||||
| func GetSerialNoByRedis() (string, error) { | |||||
| now := time.Now() | |||||
| n, err := redis_client.IncrBy(redis_key.RewardSerialCounter(now), 1) | |||||
| if err != nil { | |||||
| return "", err | |||||
| } | |||||
| if n == 1 { | |||||
| redis_client.Expire(redis_key.RewardSerialCounter(now), 5*time.Minute) | |||||
| } | |||||
| return now.Format("200601021504") + fmt.Sprint(rand.Intn(10)) + fmt.Sprintf("%02d", n), nil | |||||
| } | |||||
| @@ -9,17 +9,33 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| ) | ) | ||||
| func Accomplish(userId int64, taskType string) { | |||||
| go accomplish(userId, taskType) | |||||
| func Accomplish(action models.Action) { | |||||
| switch action.OpType { | |||||
| case models.ActionCreateRepo, | |||||
| models.ActionCreateImage: | |||||
| if action.Repo.IsPrivate { | |||||
| return | |||||
| } | |||||
| case models.ActionCreateDebugGPUTask, | |||||
| models.ActionCreateDebugNPUTask, | |||||
| models.ActionCreateTrainTask, | |||||
| models.ActionCreateInferenceTask, | |||||
| models.ActionCreateBenchMarkTask, | |||||
| models.ActionCreateGPUTrainTask: | |||||
| action.OpType = models.ActionCreateCloudbrainTask | |||||
| } | |||||
| go accomplish(action) | |||||
| } | } | ||||
| func accomplish(userId int64, taskType string) error { | |||||
| func accomplish(action models.Action) error { | |||||
| defer func() { | defer func() { | ||||
| if err := recover(); err != nil { | if err := recover(); err != nil { | ||||
| combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2)) | combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2)) | ||||
| log.Error("PANIC:%v", combinedErr) | log.Error("PANIC:%v", combinedErr) | ||||
| } | } | ||||
| }() | }() | ||||
| userId := action.ActUserID | |||||
| taskType := fmt.Sprint(action.OpType) | |||||
| //get task config | //get task config | ||||
| config, err := GetTaskConfig(taskType) | config, err := GetTaskConfig(taskType) | ||||
| @@ -33,7 +49,7 @@ func accomplish(userId int64, taskType string) error { | |||||
| } | } | ||||
| //is limited? | //is limited? | ||||
| if isLimited(userId, config) { | |||||
| if isLimited(userId, config, limiter.JustReject) { | |||||
| log.Info("task accomplish maximum times are reached,userId=%d taskType=%s", userId, taskType) | log.Info("task accomplish maximum times are reached,userId=%d taskType=%s", userId, taskType) | ||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -45,6 +61,7 @@ func accomplish(userId int64, taskType string) error { | |||||
| ConfigId: config.ID, | ConfigId: config.ID, | ||||
| TaskCode: config.TaskCode, | TaskCode: config.TaskCode, | ||||
| UserId: userId, | UserId: userId, | ||||
| ActionId: action.ID, | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| @@ -54,6 +71,7 @@ func accomplish(userId int64, taskType string) error { | |||||
| reward.Operate(&models.RewardOperateContext{ | reward.Operate(&models.RewardOperateContext{ | ||||
| SourceType: models.SourceTypeAccomplishTask, | SourceType: models.SourceTypeAccomplishTask, | ||||
| SourceId: logId, | SourceId: logId, | ||||
| Tittle: config.Tittle, | |||||
| Reward: models.Reward{ | Reward: models.Reward{ | ||||
| Amount: config.AwardAmount, | Amount: config.AwardAmount, | ||||
| Type: models.GetRewardTypeInstance(config.AwardType), | Type: models.GetRewardTypeInstance(config.AwardType), | ||||
| @@ -61,13 +79,14 @@ func accomplish(userId int64, taskType string) error { | |||||
| TargetUserId: userId, | TargetUserId: userId, | ||||
| RequestId: logId, | RequestId: logId, | ||||
| OperateType: models.OperateTypeIncrease, | OperateType: models.OperateTypeIncrease, | ||||
| RejectPolicy: limiter.FillUp, | |||||
| }) | }) | ||||
| return nil | return nil | ||||
| } | } | ||||
| func isLimited(userId int64, config *models.TaskConfig) bool { | |||||
| if err := limiter.CheckLimit(config.TaskCode, models.LimitTypeTask, userId, 1); err != nil { | |||||
| func isLimited(userId int64, config *models.TaskConfig, rejectPolicy limiter.LimiterRejectPolicy) bool { | |||||
| if _, err := limiter.CheckLimit(config.TaskCode, models.LimitTypeTask, userId, 1, rejectPolicy); err != nil { | |||||
| return true | return true | ||||
| } | } | ||||
| return false | return false | ||||