Skip to content

Commit cac9d3c

Browse files
committed
feat: 完成用户详情页面数据对接,完成用户状态变更功能
前端 - 收拢登录渠道icon逻辑 - 增加登录页背景图轮播 - 完成用户详情页数据拉取和绑定 - 后端 - 跨域配置增加允许Content-Type - 增加用户详情信息接口 - 增加用户状态变更接口
1 parent bf84af9 commit cac9d3c

File tree

13 files changed

+278
-127
lines changed

13 files changed

+278
-127
lines changed

common/errs/errors.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const (
1515

1616
ErrNoRecord = -10900 // 没有匹配到预期的记录,无数据
1717
ErrDbSelect = -10901 // 查询异常
18+
ErrParam = -10902 // 参数错误,缺少必要参数
19+
ErrDbUpdate = -10903 // 数据库更新异常
1820
)
1921

2022
// 定义错误码对应的错误描述
@@ -29,7 +31,9 @@ var errorMsg = map[int]string{
2931
ErrAuthUnexpired: "刷新登录态失败,当前登录态还有足够长的有效期",
3032
ErrMenu: "获取菜单失败,请稍后重试",
3133
ErrNoRecord: "未查询到相关数据",
32-
ErrDbSelect: "查询失败",
34+
ErrDbSelect: "查询失败,请稍后重试",
35+
ErrParam: "缺少必要参数或参数错误",
36+
ErrDbUpdate: "更新失败,请稍后重试",
3337
}
3438

3539
// GetErrorMsg 获取错误码对应的错误描述

dal/userdal.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ func NewUserDal(ctx *gin.Context) *UserDal {
2222
}
2323
}
2424

25+
// UpdateUser 会更新零值,无主键则创建记录
26+
func (u *UserDal) UpdateUser(user *model.User) (int64, error) {
27+
result := u.db.Save(user)
28+
if result.Error != nil {
29+
return 0, result.Error
30+
}
31+
return result.RowsAffected, nil
32+
}
33+
2534
// GetUserList 获取用户基本信息
2635
func (u *UserDal) GetUserList(where *model.User, offset int, limit int) (*model.UserList, error) {
2736
var count int64
@@ -162,17 +171,17 @@ func (u *UserDal) GetUserBySource(source string, openid string) *model.UserSocia
162171
}
163172

164173
// GetUserSource 批量获取用户绑定的渠道
165-
func (u *UserDal) GetUserSource(id []uint) (map[uint][]string, error) {
174+
func (u *UserDal) GetUserSource(id []uint) (map[uint][]*model.UserSocialInfo, error) {
166175
var result []*model.UserSocialInfo
167176
if err := u.db.Model(&model.UserSocialInfo{}).Where("bind_user_id IN ?", id).Find(&result).Error; err != nil {
168177
return nil, err
169178
}
170-
source := make(map[uint][]string, len(result))
179+
source := make(map[uint][]*model.UserSocialInfo, len(result))
171180
for _, info := range result {
172181
if _, exists := source[info.BindUserId]; exists {
173-
source[info.BindUserId] = append(source[info.BindUserId], info.Source)
182+
source[info.BindUserId] = append(source[info.BindUserId], info)
174183
} else {
175-
source[info.BindUserId] = []string{info.Source}
184+
source[info.BindUserId] = []*model.UserSocialInfo{info}
176185
}
177186
}
178187
return source, nil

model/user.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,29 @@ type User struct {
1313
Status uint8 `gorm:"default:0;not null;comment:0-正常,1-禁用" json:"status"` // 用户状态
1414
}
1515

16+
type UserSocialInfo struct {
17+
gorm.Model
18+
Source string `gorm:"uniqueIndex:idx_s_id;not null;size:255" json:"source"`
19+
OpenId string `gorm:"uniqueIndex:idx_s_id;not null;size:255" json:"open_id"` // 第三方系统传递过来的用户ID
20+
BindUserId uint `gorm:"not null" json:"bind_user_id"` // 关联的用户ID
21+
Email string `gorm:"size:255;not null;default:''" json:"email"`
22+
Avatar string `gorm:"size:255;not null;default:''" json:"avatar"`
23+
Username string `gorm:"size:255;not null;default:''" json:"username"`
24+
Nickname string `gorm:"size:255;not null;default:''" json:"nickname"`
25+
Bio string `gorm:"size:255;not null;default:''" json:"bio"`
26+
Phone string `gorm:"size:64;not null;default:''" json:"phone"` // 手机
27+
Gender uint8 `gorm:"default:0;not null;comment:0-未知,1-男,2-女" json:"gender"` //性别
28+
}
29+
1630
type UserList struct {
1731
Count int64 //总记录数
1832
List []*User // 用户信息
1933
}
2034

2135
type UserWithExt struct {
2236
User
23-
Group []string `json:"group"` // 用户权限组
24-
Source []string `json:"source"` // 用户绑定的登录源
37+
Group []string `json:"group"` // 用户权限组
38+
Source []*UserSocialInfo `json:"source"` // 用户绑定的登录源
2539
}
2640

2741
type UserListWithExt struct {

model/usersocialinfo.go

Lines changed: 0 additions & 19 deletions
This file was deleted.

router/api/user.go

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,53 @@ func UserCurrent(ctx *gin.Context) {
1919
return
2020
}
2121
userService := service.NewUserService(ctx)
22-
user := userService.GetUserInfo(uint(userId))
23-
if user == nil {
22+
if user, err := userService.GetUserInfo(uint(userId)); err != nil {
23+
c.CJSON(errs.ErrDbSelect)
24+
} else if user == nil {
2425
c.CJSON(errs.ErrNoRecord, "没有用户信息")
26+
} else {
27+
c.CJSON(errs.Success, user)
28+
}
29+
}
30+
31+
// UserDetail 获取用户详细信息
32+
func UserDetail(ctx *gin.Context) {
33+
c := context.CustomContext{Context: ctx}
34+
id := ctx.Query("id")
35+
if len(id) == 0 {
36+
c.CJSON(errs.ErrParam, "用户ID")
2537
return
2638
}
27-
c.CJSON(errs.Success, user)
39+
userService := service.NewUserService(ctx)
40+
userDetail, err := userService.GetUserList(id, 0, 1, []string{})
41+
if err != nil {
42+
c.CJSON(errs.ErrDbSelect, "用户详情")
43+
} else if userDetail.Count == 0 {
44+
c.CJSON(errs.ErrNoRecord, "用户详情")
45+
} else {
46+
c.CJSON(errs.Success, userDetail.List[0])
47+
}
48+
}
49+
50+
// UpdateUserStatus 更新用户状态
51+
func UpdateUserStatus(ctx *gin.Context) {
52+
c := context.CustomContext{Context: ctx}
53+
type idStatus struct {
54+
Id uint `json:"id"`
55+
Status uint8 `json:"status"`
56+
}
57+
param := &idStatus{}
58+
if err := ctx.ShouldBindBodyWithJSON(param); err != nil {
59+
c.CJSON(errs.ErrParam, "用户id或状态值不符合要求")
60+
return
61+
}
62+
userService := service.NewUserService(ctx)
63+
if _, err := userService.UpdateUserStatus(param.Id, param.Status); err != nil {
64+
c.CJSON(errs.ErrDbUpdate, err.Error())
65+
return
66+
}
67+
c.CJSON(errs.Success)
68+
2869
}
2970

3071
// UserList 获取用户列表

router/router.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ func SetupRouter(s *gin.Engine, feEmbed embed.FS) {
2525
backend := s.Group("/api")
2626
// TODO: 移除跨域支持
2727
backend.Use(cors.New(cors.Config{
28-
AllowOrigins: []string{"*"},
29-
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "HEAD", "PATCH"},
30-
AllowHeaders: []string{"Origin", "X-Token", "X-Csrf-Token", "X-Request-Id"},
31-
//AllowCredentials: true,
32-
MaxAge: 12 * time.Hour,
28+
AllowOrigins: []string{"*"},
29+
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "HEAD", "PATCH"},
30+
AllowHeaders: []string{"Origin", "Content-Type", "X-Token", "X-Csrf-Token", "X-Request-Id"},
31+
AllowCredentials: true,
32+
MaxAge: 12 * time.Hour,
3333
}))
3434
backend.OPTIONS("/*any", func(ctx *gin.Context) {
3535
// 处理OPTIONS请求,例如返回204 No Content
@@ -53,6 +53,8 @@ func SetupRouter(s *gin.Engine, feEmbed embed.FS) {
5353
backendAdmin := backend.Group("/admin")
5454
backendAdmin.GET("/menu", api.MenuAdmin)
5555
backendAdmin.GET("/user/list", api.UserList)
56+
backendAdmin.GET("/user/detail", api.UserDetail)
57+
backendAdmin.POST("/user/status", api.UpdateUserStatus)
5658

5759
s.Use(gzip.Gzip(gzip.DefaultCompression)).StaticFS("/static", getFileSystem(feEmbed, "web/build/static"))
5860
s.NoRoute(func(ctx *gin.Context) {

service/userservice.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package service
22

33
import (
4+
"errors"
45
"github.com/gin-gonic/gin"
56
"gorm.io/gorm"
67
"league/dal"
@@ -24,20 +25,41 @@ func NewUserService(ctx *gin.Context) *UserService {
2425
}
2526
}
2627

28+
func (u *UserService) UpdateUserStatus(id uint, status uint8) (bool, error) {
29+
user, err := u.GetUserInfo(id)
30+
if err != nil {
31+
return false, err
32+
}
33+
if user == nil {
34+
return false, errors.New("user not found")
35+
}
36+
if user.Status == status {
37+
log.Debug(u.Ctx, "Current status is equal to target status")
38+
return true, nil
39+
}
40+
user.Status = status
41+
_, err = u.UserDal.UpdateUser(user)
42+
if err != nil {
43+
log.Errorf(u.Ctx, "Update user failed, err: %s", err.Error())
44+
return false, err
45+
}
46+
return true, nil
47+
}
48+
2749
// GetUserInfo 根据用户ID获取用户基本信息
28-
func (u *UserService) GetUserInfo(id uint) *model.User {
50+
func (u *UserService) GetUserInfo(id uint) (*model.User, error) {
2951
user := &model.User{
3052
Model: gorm.Model{ID: id},
3153
}
3254
result, err := u.UserDal.GetUserList(user, 0, 1)
3355
if err != nil {
3456
log.Errorf(u.Ctx, "Get userinfo failed, err: %s", err.Error())
35-
return nil
57+
return nil, err
3658
}
3759
if result == nil || result.Count == 0 || len(result.List) == 0 {
38-
return nil
60+
return nil, nil
3961
}
40-
return result.List[0]
62+
return result.List[0], nil
4163
}
4264

4365
// GetUserList 查询用户列表
@@ -73,7 +95,7 @@ func (u *UserService) GetUserList(search string, offset int, limit int, order []
7395
log.Errorf(u.Ctx, "Get user source list failed, err: %s", err.Error())
7496
return nil, err
7597
}
76-
log.Debugf(u.Ctx, "user source: %v", source)
98+
//log.Debugf(u.Ctx, "user source: %v", source)
7799

78100
for _, user := range result.List {
79101
userExt := &model.UserWithExt{

web/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"start": "react-scripts start",
2121
"build": "react-scripts build",
2222
"test": "react-scripts test",
23-
"eject": "react-scripts eject"
23+
"eject": "react-scripts eject",
24+
"release": "GENERATE_SOURCEMAP=false react-scripts build"
2425
},
2526
"eslintConfig": {
2627
"extends": [

web/src/components/BrandIcon.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from "react";
2+
import {
3+
GoogleOutlined,
4+
GithubOutlined,
5+
QqOutlined,
6+
WechatOutlined,
7+
QuestionCircleOutlined,
8+
} from "@ant-design/icons";
9+
import CONSTANTS from "../constants";
10+
11+
const BrandIcon = ({ name, size }) => {
12+
if (name === CONSTANTS.USER_PROVIDER.GOOGLE) {
13+
return <GoogleOutlined style={{ fontSize: size + "px" }} />;
14+
} else if (name === CONSTANTS.USER_PROVIDER.GITHUB) {
15+
return <GithubOutlined style={{ fontSize: size + "px" }} />;
16+
} else if (name === CONSTANTS.USER_PROVIDER.QQ) {
17+
return <QqOutlined style={{ fontSize: size + "px" }} />;
18+
} else if (name === CONSTANTS.USER_PROVIDER.WECHAT) {
19+
return <WechatOutlined style={{ fontSize: size + "px" }} />;
20+
} else {
21+
return <QuestionCircleOutlined style={{ fontSize: size + "px" }} />;
22+
}
23+
};
24+
25+
export default BrandIcon;

web/src/constants.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ const CONSTANTS = {
88
HEADER_KEY_JWT: "X-Token", // jwt header key
99
HEADER_KEY_CSRF: "X-Csrf-Token",
1010
DEFAULT_PAGESIZE: 20, // 默认的pagesize
11+
USER_PROVIDER: {
12+
// 用户来源渠道
13+
WECHAT: "wechat",
14+
QQ: "qq",
15+
GOOGLE: "google",
16+
GITHUB: "github",
17+
},
1118
};
1219

1320
export default CONSTANTS;

web/src/pages/Login.js

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
Flex,
1212
Spin,
1313
message,
14+
Carousel,
1415
} from "antd";
1516
import {
1617
QqOutlined,
@@ -39,18 +40,6 @@ const Login = () => {
3940

4041
const [messageApi, contextHolder] = message.useMessage();
4142

42-
const backgroundImages = [
43-
BackgroundImg00,
44-
BackgroundImg01,
45-
BackgroundImg02,
46-
BackgroundImg03,
47-
BackgroundImg04,
48-
];
49-
50-
// 随机选择一个图片
51-
const randomBGI =
52-
backgroundImages[Math.floor(Math.random() * backgroundImages.length)];
53-
5443
useEffect(() => {
5544
if (isCallback) {
5645
//登录回调场景
@@ -125,14 +114,21 @@ const Login = () => {
125114
}}
126115
>
127116
<Row>
128-
<Col
129-
span={8}
130-
style={{
131-
height: 680,
132-
backgroundImage: `url(${randomBGI})`,
133-
backgroundSize: "cover",
134-
}}
135-
></Col>
117+
<Col span={8}>
118+
<Carousel
119+
autoplay={true}
120+
dots={false}
121+
fade={true}
122+
speed={2000}
123+
autoplaySpeed={10000}
124+
>
125+
<img src={BackgroundImg00} alt="" width="400" />
126+
<img src={BackgroundImg01} alt="" width="400" />
127+
<img src={BackgroundImg02} alt="" width="400" />
128+
<img src={BackgroundImg03} alt="" width="400" />
129+
<img src={BackgroundImg04} alt="" width="400" />
130+
</Carousel>
131+
</Col>
136132
<Col span={16}>
137133
<Row style={{ padding: "12px", justifyContent: "center" }}>
138134
<Logo collapsed={false} theme="light" />

0 commit comments

Comments
 (0)