#5180 邀请好友得算力积分

Merged
ychao_1983 merged 13 commits from invite_point into V20240116 3 months ago
  1. +40
    -1
      models/action.go
  2. +5
    -0
      models/task_config.go
  3. +13
    -0
      modules/notification/action/action.go
  4. +1
    -0
      modules/notification/base/notifier.go
  5. +4
    -0
      modules/notification/base/null.go
  6. +7
    -0
      modules/notification/notification.go
  7. +1
    -0
      options/locale/locale_en-US.ini
  8. +2
    -1
      options/locale/locale_zh-CN.ini
  9. +9
    -6
      routers/user/Invitation.go
  10. +9
    -1
      routers/user/auth.go
  11. +2
    -2
      services/ai_task_service/cluster/cloudbrain_two.go
  12. +7
    -1
      templates/reward/point/rule.tmpl
  13. +1
    -0
      templates/user/auth/activate.tmpl
  14. +1
    -1
      web_src/js/features/globalModalDlg.js
  15. +1
    -1
      web_src/js/index.js
  16. +1
    -1
      web_src/vuepages/const/index.js
  17. +2
    -0
      web_src/vuepages/langs/config/en-US.js
  18. +3
    -1
      web_src/vuepages/langs/config/zh-CN.js
  19. +5
    -1
      web_src/vuepages/pages/cloudbrain/list/index.vue
  20. +5
    -0
      web_src/vuepages/pages/reward/point/utils.js
  21. +8
    -6
      web_src/vuepages/pages/user/invite/index.vue

+ 40
- 1
models/action.go View File

@@ -78,6 +78,7 @@ const (
ActionCreateGrampusMETAXDebugTask //49
ActionCreateGrampusGPUInferenceTask //50
ActionCreateGrampusILUVATARInferenceTask //51
ActionInviteFriendRegister //52
)

// Action represents user operation type and other information to
@@ -112,6 +113,15 @@ type ActionShow struct {
IssueInfos []string
CommentLink string
Cloudbrain *CloudbrainShow4Action
Data map[string]interface{}
}

func (a *ActionShow) AddData(key string, val interface{}) {
if a.Data == nil {
a.Data = map[string]interface{}{key: val}
} else {
a.Data[key] = val
}
}

// GetOpType gets the ActionType of this action.
@@ -293,7 +303,28 @@ func (a *Action) ToShow() *ActionShow {
if strings.Contains(a.Content, "|") && a.IsIssueAction() {
actionShow.IssueInfos = a.GetIssueInfos()
}

if strings.Contains(a.Content, "|") && a.IsInviteAction() {
ids := strings.Split(a.Content, "|")
if len(ids) >= 2 {
var invitedId int64
var invitedName string
if len(ids) >= 4 {
invitedName = ids[3]
}
invitedId, _ = strconv.ParseInt(ids[1], 10, 64)
if invitedId > 0 {
invitedUser, _ := GetUserByID(invitedId)
if invitedUser != nil {
actionShow.AddData("InvitedUserName", invitedUser.Name)
actionShow.AddData("InvitedUserNotExists", false)
} else {
actionShow.AddData("InvitedUserName", invitedName)
actionShow.AddData("InvitedUserNotExists", true)
}
}
}
actionShow.IssueInfos = a.GetIssueInfos()
}
if a.Repo != nil {
actionShow.RepoLink = a.GetRepoLink()
actionShow.ShortRepoFullDisplayName = a.ShortRepoFullDisplayName()
@@ -459,6 +490,14 @@ func (a *Action) IsIssueAction() bool {
return false
}

func (a *Action) IsInviteAction() bool {
switch a.OpType {
case ActionInviteFriendRegister:
return true
}
return false
}

// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
RequestedUser *User // the user we want activity for


+ 5
- 0
models/task_config.go View File

@@ -27,6 +27,7 @@ const (
TaskImageRecommend TaskType = "ImageRecommend"
TaskChangeUserAvatar TaskType = "ChangeUserAvatar"
TaskPushCommits TaskType = "PushCommits"
TaskInviteFriendRegister TaskType = "TaskInviteFriendRegister"
)

func (t TaskType) ChineseName() string {
@@ -57,6 +58,8 @@ func (t TaskType) ChineseName() string {
return "首次更换头像"
case TaskPushCommits:
return "每日commit"
case TaskInviteFriendRegister:
return "邀请好友"
}
return "--"
}
@@ -111,6 +114,8 @@ func GetTaskTypeFromAction(a ActionType) TaskType {
return TaskPushCommits
case ActionCreateIssue:
return TaskCreateIssue
case ActionInviteFriendRegister:
return TaskInviteFriendRegister
}
return ""
}


+ 13
- 0
modules/notification/action/action.go View File

@@ -421,3 +421,16 @@ func (t *actionNotifier) NotifyChangeUserAvatar(user *models.User, form auth.Ava
log.Error("notifyWatchers: %v", err)
}
}

func (t *actionNotifier) NotifyInviteFriendRegister(inviter, invited *models.User) {
act := &models.Action{
ActUserID: inviter.ID,
ActUser: inviter,
OpType: models.ActionInviteFriendRegister,
IsPrivate: true,
Content: fmt.Sprintf("%d|%d|%s|%s", inviter.ID, invited.ID, inviter.Name, invited.Name),
}
if err := models.NotifyWatchers(act); err != nil {
log.Error("notifyWatchers: %v", err)
}
}

+ 1
- 0
modules/notification/base/notifier.go View File

@@ -67,4 +67,5 @@ type Notifier interface {
NotifyChangeCloudbrainStatus(cloudbrain *models.Cloudbrain, oldStatus string)
NotifyCloudbrainTaskComingToFinished(cloudbrain *models.Cloudbrain, endTime timeutil.TimeStamp, account *models.PointAccount)
NotifyChangeFinetuneStatus(deployment *models.ModelartsDeploy)
NotifyInviteFriendRegister(inviter, invited *models.User)
}

+ 4
- 0
modules/notification/base/null.go View File

@@ -189,3 +189,7 @@ func (*NullNotifier) NotifyCloudbrainTaskComingToFinished(cloudbrain *models.Clo
func (*NullNotifier) NotifyChangeFinetuneStatus(deployment *models.ModelartsDeploy) {

}

func (*NullNotifier) NotifyInviteFriendRegister(inviter, invited *models.User) {

}

+ 7
- 0
modules/notification/notification.go View File

@@ -333,3 +333,10 @@ func NotifyChangeFinetuneStatus(deployment *models.ModelartsDeploy) {
notifier.NotifyChangeFinetuneStatus(deployment)
}
}

// NotifyInviteFriendRegister
func NotifyInviteFriendRegister(inviter, invited *models.User) {
for _, notifier := range notifiers {
notifier.NotifyInviteFriendRegister(inviter, invited)
}
}

+ 1
- 0
options/locale/locale_en-US.ini View File

@@ -568,6 +568,7 @@ all = All
form.name_reserved = The username '%s' is reserved.
form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username.
form.name_chars_not_allowed = User name '%s' contains invalid characters.
form.username_and_invited_code_duplicated = The username and referrer cannot be the same.

static.invitationdetailsheetname=User Invitation Detail
static.invitationNum=User Invitation Count


+ 2
- 1
options/locale/locale_zh-CN.ini View File

@@ -420,7 +420,7 @@ change_email_address=修改邮箱地址
new_email_address=新邮箱地址
openi_community_really_awesome=启智社区 确实给力
protocol_header=请仔细阅读下方内容:
protocol_title=尊敬的启智用户
protocol_title=尊敬的启智用户
protocol_context=感谢您一直以来对Openl启智社区AI协作平台的支持。为了保障您的使用权益和确保网络安全,我们于2024年1月份更新了《Openl启智社区AI协作平台使用协议》。更新后的协议明确了用户<font color="#ff2525">禁止使用内网穿透工具</font>的条例。您单击“同意并继续”后,便可以继续使用我们的服务。感谢您的合作与理解。
protocol_context_sub=更多协议内容,请参考<u><font color="#3291f8"><a href="/home/term" target="_blank">《Openl启智社区AI协作平台使用协议》</a></font></u>
protocol_confirm=同意并继续
@@ -572,6 +572,7 @@ all = 所有
form.name_reserved='%s' 用户名被保留。
form.name_pattern_not_allowed=用户名中不允许使用 "%s"。
form.name_chars_not_allowed=用户名 '%s' 包含无效字符。
form.username_and_invited_code_duplicated = 注册用户名和推荐人不能相同。

static.invitationdetailsheetname=用户邀请详细数据
static.invitationNum=邀请用户数


+ 9
- 6
routers/user/Invitation.go View File

@@ -1,6 +1,7 @@
package user

import (
"code.gitea.io/gitea/modules/notification"
"errors"
"strconv"
"strings"
@@ -63,14 +64,14 @@ func InviationTpl(ctx *context.Context) {
ctx.HTML(200, tplInvitation)
}

func RegisteUserByInvitaionCode(invitationcode string, newUserId int64, newPhoneNumber string, email string) error {
func RegisteUserByInvitaionCode(invitationcode string, newUser *models.User) error {
user := parseInvitaionCode(invitationcode)
if user == nil {
return errors.New("The invitated user not existed.")
}

if newPhoneNumber != "" {
re := models.QueryInvitaionByPhone(newPhoneNumber)
if newUser.PhoneNumber != "" {
re := models.QueryInvitaionByPhone(newUser.PhoneNumber)
if re != nil {
if len(re) > 0 {
log.Info("The phone has been invitated. so ingore it.")
@@ -83,14 +84,16 @@ func RegisteUserByInvitaionCode(invitationcode string, newUserId int64, newPhone

invitation := &models.Invitation{
SrcUserID: user.ID,
UserID: newUserId,
Phone: newPhoneNumber,
Email: email,
UserID: newUser.ID,
Phone: newUser.PhoneNumber,
Email: newUser.Email,
}

err := models.InsertInvitaion(invitation)
if err != nil {
log.Info("insert error," + err.Error())
} else {
notification.NotifyInviteFriendRegister(user, newUser)
}
return err
}


+ 9
- 1
routers/user/auth.go View File

@@ -1434,6 +1434,11 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
}
}

if strings.ToLower(invitationCode) == strings.ToLower(form.UserName) {
ctx.RenderWithErr(ctx.Tr("user.form.username_and_invited_code_duplicated"), tplSignUp, &form)
return
}

if !form.IsEmailDomainWhitelisted() {
ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplSignUp, &form)
return
@@ -1499,7 +1504,10 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo

log.Info("enter here, and form.InvitaionCode =" + invitationCode)
if invitationCode != "" {
RegisteUserByInvitaionCode(invitationCode, u.ID, u.PhoneNumber, u.Email)
tmpErr := RegisteUserByInvitaionCode(invitationCode, u)
if tmpErr != nil {
log.Error("RegisteUserByInvitaionCode err.u=%+v invitationCode=%s", u, invitationCode)
}
}

err := models.AddEmailAddress(&models.EmailAddress{


+ 2
- 2
services/ai_task_service/cluster/cloudbrain_two.go View File

@@ -259,8 +259,8 @@ func (c CloudbrainTwoClusterAdapter) QueryNoteBook(opts entity.JobIdAndVersionId

func convertCloudbrainTwo2QueryRes(res *models.GetNotebook2Result) *entity.QueryTaskResponse {
startedAt := timeutil.TimeStamp(0)
if res.Lease.UpdateTime > 0 {
startedAt = timeutil.TimeStamp(res.Lease.UpdateTime / 1000)
if res.Lease.CreateTime > 0 {
startedAt = timeutil.TimeStamp(res.Lease.CreateTime / 1000)
}
completedAt := timeutil.TimeStamp(0)
if models.IsCloudbrainTerminalStatus(res.Status) {


+ 7
- 1
templates/reward/point/rule.tmpl View File

@@ -79,7 +79,7 @@
<td class="t-center">导入新模型</td>
<td class="t-center point">-</td>
<td class="t-center"><span class="typ">每日</span>积分获取上限<span class="limit"> - </span></td>
<td>请注意模型质量,请勿重复导入相同模型,任何非常规的以导入新模型去获取 积分的行为将被认定为积分舞弊,将扣除所有积分。</td>
<td>请注意模型质量,请勿重复导入相同模型,任何非常规的以导入新模型去获取积分的行为将被认定为积分舞弊,将扣除所有积分。</td>
</tr>
<tr key="CreateCloudbrainTask">
<td class="t-center">每日运行云脑任务</td>
@@ -104,6 +104,12 @@
<td class="t-center point">-</td>
<td class="t-center"><span class="typ">累计</span>积分获取上限<span class="limit"> - </span></td>
<td>首次更换头像,获得积分。</td>
</tr>
<tr key="TaskInviteFriendRegister">
<td class="t-center">邀请好友</td>
<td class="t-center point">-</td>
<td class="t-center"><span class="typ">累计</span>积分获取上限<span class="limit"> - </span></td>
<td>邀请好友获得积分。</td>
</tr>
</table>
</div>


+ 1
- 0
templates/user/auth/activate.tmpl View File

@@ -1,4 +1,5 @@
{{template "base/head" .}}
<script>window.IsActivatePage = true;</script>
<div class="user activate">
<div class="ui middle very relaxed page grid">
<div class="column">


+ 1
- 1
web_src/js/features/globalModalDlg.js View File

@@ -18,7 +18,7 @@ import highlight from "./highlight.js";
}
curStorageKey = `g-models-${userID}`;
var pathName = window.location.pathname;
if (exceptPages.indexOf(pathName) > -1) return; // 排除页,不显示
if (exceptPages.indexOf(pathName) > -1 || window.IsActivatePage) return; // 排除页,不显示
$.ajax({
type: "GET",
url: "/dashboard/invitation",


+ 1
- 1
web_src/js/index.js View File

@@ -5475,7 +5475,7 @@ function initAddUsageAgreement() {
let params = {userName:userName}
return axios.post(`${AppSubUrl}/user/saveOtherInfo`,qs.stringify(params))
}
if ($('meta[name="_uid"]').length && ['/home/term'].indexOf(window.location.pathname) < 0) {
if ($('meta[name="_uid"]').length && ['/home/term'].indexOf(window.location.pathname) < 0 && !window.IsActivatePage) {
showLoginProtocolDialog($('meta[name="_uid"]').attr('content-ext'))
}
}


+ 1
- 1
web_src/vuepages/const/index.js View File

@@ -4,7 +4,7 @@ export const SOURCE_TYPE = [{ k: 'ACCOMPLISH_TASK', v: i18n.t('accomplishTask')
export const CONSUME_STATUS = [{ k: 'OPERATING', v: i18n.t('operating') }, { k: 'SUCCEEDED', v: i18n.t('succeeded') }];
export const POINT_ACTIONS = [
{ k: 'CreatePublicRepo', v: i18n.t('createPublicProject') }, { k: 'CreateIssue', v: i18n.t('dailyPutforwardTasks') }, { k: 'CreatePullRequest', v: i18n.t('dailyPR') }, { k: 'CommentIssue', v: i18n.t('comment') }, { k: 'UploadAttachment', v: i18n.t('uploadDatasetFile') }, { k: 'CreateNewModelTask', v: i18n.t('importNewModel') }, { k: 'BindWechat', v: i18n.t('completeWechatCodeScanningVerification') },
{ k: 'CreateCloudbrainTask', v: i18n.t('dailyRunCloudbrainTasks') }, { k: 'DatasetRecommended', v: i18n.t('datasetRecommendedByThePlatform') }, { k: 'CreateImage', v: i18n.t('submitNewPublicImage') }, { k: 'ImageRecommend', v: i18n.t('imageRecommendedByThePlatform') }, { k: 'ChangeUserAvatar', v: i18n.t('firstChangeofAvatar') }, { k: 'PushCommits', v: i18n.t('dailyCommit') },
{ k: 'CreateCloudbrainTask', v: i18n.t('dailyRunCloudbrainTasks') }, { k: 'DatasetRecommended', v: i18n.t('datasetRecommendedByThePlatform') }, { k: 'CreateImage', v: i18n.t('submitNewPublicImage') }, { k: 'ImageRecommend', v: i18n.t('imageRecommendedByThePlatform') }, { k: 'ChangeUserAvatar', v: i18n.t('firstChangeofAvatar') }, { k: 'PushCommits', v: i18n.t('dailyCommit') }, { k: 'TaskInviteFriendRegister', v: i18n.t('user.inviteFriends') }
];
export const JOB_TYPE = [{ k: 'DEBUG', v: i18n.t('debugTask') }, { k: 'TRAIN', v: i18n.t('trainTask') }, { k: 'INFERENCE', v: i18n.t('inferenceTask') }, { k: 'BENCHMARK', v: i18n.t('benchmarkTask') }, { k: 'ONLINEINFERENCE', v: i18n.t('onlineinfer') }, { k: 'HPC', v: i18n.t('superComputeTask') }];



+ 2
- 0
web_src/vuepages/langs/config/en-US.js View File

@@ -74,11 +74,13 @@ const en = {

createdRepository: 'created repository ',
repositoryWasDel: 'repository was deleted',
userWasDel: 'user was deleted',
openedIssue: 'opened issue ',
createdPullRequest: 'created pull request ',
commentedOnIssue: 'commented on issue ',
uploadDataset: 'upload dataset ',
createdNewModel: 'created new model ',
invitedFriend: 'invited friend ',
firstBindingWechatRewards: 'first binding wechat rewards',
created: 'created ',
type: ' type ',


+ 3
- 1
web_src/vuepages/langs/config/zh-CN.js View File

@@ -73,11 +73,13 @@ const zh = {

createdRepository: "创建了项目",
repositoryWasDel: "项目已删除",
userWasDel: "用户已删除",
openedIssue: "创建了任务",
createdPullRequest: "创建了合并请求",
commentedOnIssue: "评论了任务",
uploadDataset: "上传了数据集文件",
createdNewModel: "导入了新模型",
createdNewModel: "导入了新模型",
invitedFriend: "邀请了好友",
firstBindingWechatRewards: "首次绑定微信奖励",
created: "创建了",
type: "类型",


+ 5
- 1
web_src/vuepages/pages/cloudbrain/list/index.vue View File

@@ -86,9 +86,13 @@
:label="$t('modelManage.creator')" align="left" min-width="80" header-align="center">
<template slot-scope="scope">
<div class="creator-wrap">
<a :href="'/' + scope.row.creator.name" :title="scope.row.creator.full_name">
<a v-if="scope.row.creator.name" :href="'/' + scope.row.creator.name"
:title="scope.row.creator.full_name">
<img :src="scope.row.creator.rel_avatar_link">
</a>
<span v-else title="Ghost">
<img src="/user/avatar/Ghost/-1">
</span>
</div>
</template>
</el-table-column>


+ 5
- 0
web_src/vuepages/pages/reward/point/utils.js View File

@@ -161,6 +161,11 @@ export const getRewardPointRecordInfo = (record) => {
case 'ChangeUserAvatar': // 首次更换头像 - 更新了头像
out.remark = `${i18n.t('updatedAvatar')}`;
break;
case 'TaskInviteFriendRegister': // 邀请好友
out.remark = record.Action.Data && record.Action.Data.InvitedUserNotExists == false
? `${i18n.t('invitedFriend')}<a href="/${record.Action.Data.InvitedUserName}" rel="nofollow">${record.Action.Data.InvitedUserName}</a>`
: `${i18n.t('invitedFriend')}${record.Action.Data?.InvitedUserName}(${i18n.t('userWasDel')})`;
break;
case 'PushCommits': // 每日commit - 推送了xxxx分支的代码到OpenI/aiforge
if (record?.Action) {
const opType = record.Action.OpType;


+ 8
- 6
web_src/vuepages/pages/user/invite/index.vue View File

@@ -35,7 +35,9 @@
<template slot-scope="scope">
<div style="display:flex;align-items:center;padding-left:20px;">
<img :src="scope.row.avatarSrc" alt="" style="height:45px;width:45px;margin-right:10px;" />
<a :href="scope.row.userLink" style="font-weight:500;font-size:15px;">{{ scope.row.userName }}</a>
<a v-if="scope.row.userLink" :href="scope.row.userLink" style="font-weight:500;font-size:15px;">{{
scope.row.userName }}</a>
<span v-else tyle="font-weight:500;font-size:15px;">{{ scope.row.userName }}</span>
</div>
</template>
</el-table-column>
@@ -48,7 +50,7 @@
</el-table-column>
<template slot="empty">
<span>{{
loading ? $t('loading') : $t('noData')
loading ? $t('loading') : $t('noData')
}}</span>
</template>
</el-table>
@@ -117,10 +119,10 @@ export default {
},
transRowData(item) {
return {
userName: item.Name,
avatarSrc: item.Avatar,
userLink: window.origin + '/' + item.Name,
statusStr: item.IsActive ? this.$t('user.Activated') : this.$t('user.notActive'),
userName: item.Name || 'Ghost',
avatarSrc: item.Avatar || '/user/avatar/Ghost/-1',
userLink: item.Name ? (window.origin + '/' + item.Name) : '',
statusStr: item.Name ? item.IsActive ? this.$t('user.Activated') : this.$t('user.notActive') : this.$t('userWasDel'),
statusColor: item.IsActive ? 'rgb(82, 196, 26)' : 'rgb(245, 34, 45)',
regTime: formatDate(new Date(item.CreatedUnix * 1000), 'yyyy-MM-dd HH:mm:ss'),
}


Loading…
Cancel
Save