Просмотр исходного кода

用户注销后禁止再次注册

CaIon 2 лет назад
Родитель
Сommit
ad6842da7f
3 измененных файлов с 99 добавлено и 53 удалено
  1. 16 1
      controller/user.go
  2. 46 21
      model/user.go
  3. 37 31
      web/src/components/UsersTable.js

+ 16 - 1
controller/user.go

@@ -152,6 +152,21 @@ func Register(c *gin.Context) {
 			return
 		}
 	}
+	exist, err := model.CheckUserExistOrDeleted(user.Username, user.Email)
+	if err != nil {
+		c.JSON(http.StatusOK, gin.H{
+			"success": false,
+			"message": err.Error(),
+		})
+		return
+	}
+	if exist {
+		c.JSON(http.StatusOK, gin.H{
+			"success": false,
+			"message": "用户名已存在,或已注销",
+		})
+		return
+	}
 	affCode := user.AffCode // this code is the inviter's code, not the user's own code
 	inviterId, _ := model.GetUserIdByAffCode(affCode)
 	cleanUser := model.User{
@@ -525,7 +540,7 @@ func DeleteUser(c *gin.Context) {
 		})
 		return
 	}
-	err = model.DeleteUserById(id)
+	err = model.HardDeleteUserById(id)
 	if err != nil {
 		c.JSON(http.StatusOK, gin.H{
 			"success": true,

+ 46 - 21
model/user.go

@@ -11,26 +11,43 @@ import (
 // User if you add sensitive fields, don't forget to clean them in setupLogin function.
 // Otherwise, the sensitive information will be saved on local storage in plain text!
 type User struct {
-	Id               int    `json:"id"`
-	Username         string `json:"username" gorm:"unique;index" validate:"max=12"`
-	Password         string `json:"password" gorm:"not null;" validate:"min=8,max=20"`
-	DisplayName      string `json:"display_name" gorm:"index" validate:"max=20"`
-	Role             int    `json:"role" gorm:"type:int;default:1"`   // admin, common
-	Status           int    `json:"status" gorm:"type:int;default:1"` // enabled, disabled
-	Email            string `json:"email" gorm:"index" validate:"max=50"`
-	GitHubId         string `json:"github_id" gorm:"column:github_id;index"`
-	WeChatId         string `json:"wechat_id" gorm:"column:wechat_id;index"`
-	VerificationCode string `json:"verification_code" gorm:"-:all"`                                    // this field is only for Email verification, don't save it to database!
-	AccessToken      string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management
-	Quota            int    `json:"quota" gorm:"type:int;default:0"`
-	UsedQuota        int    `json:"used_quota" gorm:"type:int;default:0;column:used_quota"` // used quota
-	RequestCount     int    `json:"request_count" gorm:"type:int;default:0;"`               // request number
-	Group            string `json:"group" gorm:"type:varchar(32);default:'default'"`
-	AffCode          string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"`
-	AffCount         int    `json:"aff_count" gorm:"type:int;default:0;column:aff_count"`
-	AffQuota         int    `json:"aff_quota" gorm:"type:int;default:0;column:aff_quota"`           // 邀请剩余额度
-	AffHistoryQuota  int    `json:"aff_history_quota" gorm:"type:int;default:0;column:aff_history"` // 邀请历史额度
-	InviterId        int    `json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
+	Id               int            `json:"id"`
+	Username         string         `json:"username" gorm:"unique;index" validate:"max=12"`
+	Password         string         `json:"password" gorm:"not null;" validate:"min=8,max=20"`
+	DisplayName      string         `json:"display_name" gorm:"index" validate:"max=20"`
+	Role             int            `json:"role" gorm:"type:int;default:1"`   // admin, common
+	Status           int            `json:"status" gorm:"type:int;default:1"` // enabled, disabled
+	Email            string         `json:"email" gorm:"index" validate:"max=50"`
+	GitHubId         string         `json:"github_id" gorm:"column:github_id;index"`
+	WeChatId         string         `json:"wechat_id" gorm:"column:wechat_id;index"`
+	VerificationCode string         `json:"verification_code" gorm:"-:all"`                                    // this field is only for Email verification, don't save it to database!
+	AccessToken      string         `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management
+	Quota            int            `json:"quota" gorm:"type:int;default:0"`
+	UsedQuota        int            `json:"used_quota" gorm:"type:int;default:0;column:used_quota"` // used quota
+	RequestCount     int            `json:"request_count" gorm:"type:int;default:0;"`               // request number
+	Group            string         `json:"group" gorm:"type:varchar(32);default:'default'"`
+	AffCode          string         `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"`
+	AffCount         int            `json:"aff_count" gorm:"type:int;default:0;column:aff_count"`
+	AffQuota         int            `json:"aff_quota" gorm:"type:int;default:0;column:aff_quota"`           // 邀请剩余额度
+	AffHistoryQuota  int            `json:"aff_history_quota" gorm:"type:int;default:0;column:aff_history"` // 邀请历史额度
+	InviterId        int            `json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
+	DeletedAt        gorm.DeletedAt `json:"deleted_at" gorm:"index"`
+}
+
+// CheckUserExistOrDeleted check if user exist or deleted, if not exist, return false, nil, if deleted or exist, return true, nil
+func CheckUserExistOrDeleted(username string, email string) (bool, error) {
+	var user User
+	err := DB.Unscoped().First(&user, "username = ? or email = ?", username, email).Error
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			// not exist, return false, nil
+			return false, nil
+		}
+		// other error, return false, err
+		return false, err
+	}
+	// exist, return true, nil
+	return true, nil
 }
 
 func GetMaxUserId() int {
@@ -40,7 +57,7 @@ func GetMaxUserId() int {
 }
 
 func GetAllUsers(startIdx int, num int) (users []*User, err error) {
-	err = DB.Order("id desc").Limit(num).Offset(startIdx).Omit("password").Find(&users).Error
+	err = DB.Unscoped().Order("id desc").Limit(num).Offset(startIdx).Omit("password").Find(&users).Error
 	return users, err
 }
 
@@ -80,6 +97,14 @@ func DeleteUserById(id int) (err error) {
 	return user.Delete()
 }
 
+func HardDeleteUserById(id int) error {
+	if id == 0 {
+		return errors.New("id 为空!")
+	}
+	err := DB.Unscoped().Delete(&User{}, "id = ?", id).Error
+	return err
+}
+
 func inviteUser(inviterId int) (err error) {
 	user, err := GetUserById(inviterId, true)
 	if err != nil {

+ 37 - 31
web/src/components/UsersTable.js

@@ -72,32 +72,49 @@ const UsersTable = () => {
     }, {
         title: '状态', dataIndex: 'status', render: (text, record, index) => {
             return (<div>
-                {renderStatus(text)}
+                {record.deleted_at !== null? <Tag color='red'>已注销</Tag> : renderStatus(text)}
             </div>);
         },
     }, {
         title: '', dataIndex: 'operate', render: (text, record, index) => (<div>
-            <Popconfirm
-                title="确定?"
-                okType={'warning'}
-                onConfirm={() => {
-                    manageUser(record.username, 'promote', record)
-                }}
-            >
-                <Button theme='light' type='warning' style={{marginRight: 1}}>提升</Button>
-            </Popconfirm>
-            <Popconfirm
-                title="确定?"
-                okType={'warning'}
-                onConfirm={() => {
-                    manageUser(record.username, 'demote', record)
-                }}
-            >
-                <Button theme='light' type='secondary' style={{marginRight: 1}}>降级</Button>
-            </Popconfirm>
+            {
+                record.deleted_at !== null ? <></>:
+                    <>
+                        <Popconfirm
+                            title="确定?"
+                            okType={'warning'}
+                            onConfirm={() => {
+                                manageUser(record.username, 'promote', record)
+                            }}
+                        >
+                            <Button theme='light' type='warning' style={{marginRight: 1}}>提升</Button>
+                        </Popconfirm>
+                        <Popconfirm
+                            title="确定?"
+                            okType={'warning'}
+                            onConfirm={() => {
+                                manageUser(record.username, 'demote', record)
+                            }}
+                        >
+                            <Button theme='light' type='secondary' style={{marginRight: 1}}>降级</Button>
+                        </Popconfirm>
+                        {record.status === 1 ?
+                            <Button theme='light' type='warning' style={{marginRight: 1}} onClick={async () => {
+                                manageUser(record.username, 'disable', record)
+                            }}>禁用</Button> :
+                            <Button theme='light' type='secondary' style={{marginRight: 1}} onClick={async () => {
+                                manageUser(record.username, 'enable', record);
+                            }} disabled={record.status === 3}>启用</Button>}
+                        <Button theme='light' type='tertiary' style={{marginRight: 1}} onClick={() => {
+                            setEditingUser(record);
+                            setShowEditUser(true);
+                        }}>编辑</Button>
+                    </>
+
+            }
             <Popconfirm
                 title="确定是否要删除此用户?"
-                content="此修改将不可逆"
+                content="硬删除,此修改将不可逆"
                 okType={'danger'}
                 position={'left'}
                 onConfirm={() => {
@@ -108,17 +125,6 @@ const UsersTable = () => {
             >
                 <Button theme='light' type='danger' style={{marginRight: 1}}>删除</Button>
             </Popconfirm>
-            {record.status === 1 ?
-                <Button theme='light' type='warning' style={{marginRight: 1}} onClick={async () => {
-                    manageUser(record.username, 'disable', record)
-                }}>禁用</Button> :
-                <Button theme='light' type='secondary' style={{marginRight: 1}} onClick={async () => {
-                    manageUser(record.username, 'enable', record);
-                }} disabled={record.status === 3}>启用</Button>}
-            <Button theme='light' type='tertiary' style={{marginRight: 1}} onClick={() => {
-                setEditingUser(record);
-                setShowEditUser(true);
-            }}>编辑</Button>
         </div>),
     },];