用户中心(优化)

文章目录

    • 功能扩充
      • 管理员修改用户信息
      • 管理员删除用户
      • 管理员添加用户
      • 添加个人主页,可以完善个人信息(上传头像没有实现)
      • 添加默认头像
      • 打造一个所有用户可发帖的页面
        • 前端页面,√
        • 后端建表,接口,√
        • 前后端联调√
      • 后端添加全局请求拦截器(统一去判断用户权限、统计记录请求日志)
    • BUG
      • 前端页面不能同步操作需要刷新
      • 上传头像的BUG


  1. 功能扩充
    1. 管理员修改用户信息、删除用户、添加用户√
    2. 添加个人主页,可以完善个人信息上传头像√ ×
    3. 添加默认头像√
    4. 更改星球编号,换成验证码形式×
    5. 借鉴星球思路,打造一个所有用户可发帖的页面(不然普通用户太单调了)
      1. 前端页面,√
      2. 后端建表,接口,√
      3. 前后端联调√
    6. 按照更多的条件去查询用户×
    7. 更新密码加密×
    8. 更改权限×
  2. 修改 bug×
  3. 项目登录改为分布式 session(单点登录 - Redis)×
  4. 通用性 ×
    1. set-cookie domain 域名更通用,比如改为 *.xxx.com
    2. 把用户管理系统 => 用户中心(之后所有的服务器都请求这个后端)
  5. 后端添加全局请求拦截器(统一去判断用户权限、统计记录请求日志)√
  6. 优化统一返回体处理×

功能扩充

前端代码比较多就不附带了(本人也不是很懂,好多bug)

管理员修改用户信息

image.png
在 model/domain/request 包下创建 UserUpdateRequest 类,表示更新用户请求体

@Data
public class UserUpdateRequest {private Long id;private String userAccount;private String userName;private String userPassword;// 你可以决定是否允许通过此API修改密码private Integer gender;private String avatarUrl;private String phone;private String email;private Integer userStatus;private String planetCode;
}

在 service 包下的 UserService 接口创建新的方法

 * 管理员更新用户* @param userUpdateRequest* @return*/
boolean updateUser(UserUpdateRequest userUpdateRequest);

这里为了减少代码, 因为校验账户是否存在、星球编号是否存在的操作,在上面的用户注册方法里面有过,于是我就提取出来了

/*** 判断账户是否已经存在* @param userAccount* @return*/private boolean checkUserAccountExists(String userAccount) {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("userAccount", userAccount);return count(queryWrapper) > 0;}/*** 判断星球编号是否已经存在* @param planetCode* @return*/private boolean checkPlanetCodeExists(String planetCode) {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("planetCode", planetCode);return count(queryWrapper) > 0;}

然后在 service/impl 包下的 UserServiceImpl 类

 /*** 根据提供的用户信息更新用户。* 此方法会根据 userUpdateRequest 中提供的信息更新数据库中相应的用户记录。* 具体校验逻辑如下:* - 用户ID不能为空,确保能正确定位需要更新的用户记录。* - 用户名如果提供,其长度不能少于4位。* - 账号如果提供,不能包含特殊字符。* - 密码如果提供,其长度不能少于8位,并对密码进行MD5加密处理。* - 星球编号如果提供,其长度不能超过5位。* - 账号和星球编号如果提供,需要检查它们的唯一性,以避免与其他用户的账号或星球编号冲突。** @param userUpdateRequest 包含要更新用户信息的请求体。* @return 更新成功返回true,否则返回false。*/@Overridepublic boolean updateUser(UserUpdateRequest userUpdateRequest) {// 检查更新请求中的用户ID是否为空if (userUpdateRequest.getId() == null) {throw new BusinessException(ErrorCode.NULL_ERROR, "用户ID不能为空");}// 若更新请求中包含用户名,则进行长度校验if (StringUtils.isNotBlank(userUpdateRequest.getUsername()) && userUpdateRequest.getUsername().length() < 4) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名长度过短,至少需要4位。");}// 账号特殊字符校验if (StringUtils.isNotBlank(userUpdateRequest.getUserAccount())) {String validPattern = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";Matcher matcher = Pattern.compile(validPattern).matcher(userUpdateRequest.getUserAccount());if (matcher.find()) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号包含非法字符。");}}// 若更新请求中包含用户密码,则进行长度校验if (StringUtils.isNotBlank(userUpdateRequest.getUserPassword()) && userUpdateRequest.getUserPassword().length() < 8) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度过短,至少需要8位。");}// 若更新请求中包含星球编号,则进行长度校验if (StringUtils.isNotBlank(userUpdateRequest.getPlanetCode()) && userUpdateRequest.getPlanetCode().length() > 5) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "星球编号过长,不得超过5位。");}// 账户是否已存在if(checkUserAccountExists(userUpdateRequest.getUserAccount())) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在");}// 星球编号是否已存在if(checkPlanetCodeExists(userUpdateRequest.getPlanetCode())) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "星球编号已存在");}// 构造更新条件UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();updateWrapper.eq("id", userUpdateRequest.getId());// 准备更新的用户实体User user = new User();BeanUtils.copyProperties(userUpdateRequest, user);// 如果提供了密码,则进行加密处理if (StringUtils.isNotBlank(user.getUserPassword())) {String encryptedPassword = DigestUtils.md5DigestAsHex((SALT + user.getUserPassword()).getBytes());user.setUserPassword(encryptedPassword);}// 执行更新操作int updateCount = userMapper.update(user, updateWrapper);return updateCount > 0;}

然后是 controller 包下的 UserController 类

@PostMapping("/update")
public BaseResponse<Boolean> updateUser(@RequestBody UserUpdateRequest userUpdateRequest,HttpServletRequest request){// 权限校验逻辑,确保是管理员操作if(!isAdmin(request)){throw new BusinessException(ErrorCode.NO_AUTH);}boolean result=userService.updateUser(userUpdateRequest);return ResultUtils.success(result);
}

管理员删除用户

逻辑删除
image.png
在 service 包下的 UserService 接口创建新的方法

/*** 删除指定ID的用户* @param id 用户ID* @return 操作是否成功*/
boolean deleteUserById(Long id);

然后在 service/impl 包下的 UserServiceImpl 类

/*** 根据用户ID删除用户。* 调用MyBatis Plus的deleteById方法,根据提供的用户ID执行删除操作。* 如果删除操作影响的行数大于0,则返回true表示删除成功;否则返回false表示删除失败。* @param id 用户的ID。* @return 删除成功返回true,失败返回false。*/
@Override
public boolean deleteUserById(Long id) {// 这里简单地调用了MyBatis Plus的内置方法进行删除,实际情况可能需要更复杂的逻辑int rows = userMapper.deleteById(id);return rows > 0;
}

然后是 controller 包下的 UserController 类

/*** 管理员通过用户ID删除用户。此操作要求执行者具有管理员权限。* 通过HTTP请求中的路径变量接收要删除的用户ID,并进行权限验证。* 如果当前操作用户是管理员,调用Service层执行删除操作。* @param id 用户的唯一标识ID,通过URL路径传递。* @param request 用于获取当前HTTP请求的对象,主要用于执行权限校验。* @return 返回一个包含操作结果的响应体,操作成功返回true,失败返回false。*/
@DeleteMapping("/delete/{id}")
public BaseResponse<Boolean> deleteUser(@PathVariable Long id, HttpServletRequest request) {// 权限校验,确保是管理员操作if (!isAdmin(request)) {throw new BusinessException(ErrorCode.NO_AUTH);}boolean result = userService.deleteUserById(id);return ResultUtils.success(result);
}

管理员添加用户

image.png
在 model/domain/request 包下创建 UserCreateRequest 类,表示创建用户请求体

    private String userPassword;private String checkPassword;private String userName;private String avatarUrl; // 可选private Integer gender; // 可选private String phone; // 可选private String email; // 可选private Integer userRole; // 可选,管理员在创建用户时指定角色private String planetCode;
}

在 service 包下的 UserService 接口创建新的方法

 * 管理员创建用户* @param createRequest* @return*/
boolean createUser(UserCreateRequest createRequest);

这里我考虑到创建用户可能要传的参数可能有很多,就不把这个请求里的参数从 UserController 里提取出来再传入了,那样太多太杂了

然后在 service/impl 包下的 UserServiceImpl 类

/*** 创建用户操作* @param createRequest* @return*/@Overridepublic boolean createUser(UserCreateRequest createRequest) {// 校验账户、密码、用户名、星球编号不能为空if(StringUtils.isAnyBlank(createRequest.getUserAccount(),createRequest.getUsername(),createRequest.getPlanetCode())){throw new BusinessException(ErrorCode.NULL_ERROR, "账户、用户名、星球编号不能为空");}// 账户是否已存在if(checkUserAccountExists(createRequest.getUserAccount())) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在");}// 星球编号是否已存在if(checkPlanetCodeExists(createRequest.getPlanetCode())) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "星球编号已存在");}// 创建用户对象并设置字段User user = new User();user.setUserAccount(createRequest.getUserAccount());user.setUsername(createRequest.getUsername());user.setPlanetCode(createRequest.getPlanetCode());user.setAvatarUrl(createRequest.getAvatarUrl());user.setGender(createRequest.getGender());user.setPhone(createRequest.getPhone());user.setEmail(createRequest.getEmail());user.setUserRole(createRequest.getUserRole() != null ? createRequest.getUserRole() : UserConstant.DEFAULT_ROLE); // 默认为普通用户,如果未指定角色// 密码加密String encryptedPassword = DigestUtils.md5DigestAsHex((SALT + createRequest.getUserPassword()).getBytes());user.setUserPassword(encryptedPassword);return save(user);}

然后是 controller 包下的 UserController 类

/*** 处理管理员创建用户的请求。* 此接口仅允许管理员调用,用于创建新用户。* @param createRequest 包含用户信息的请求体,需要符合用户创建的各项要求。* @param request       HTTP请求对象,用于权限校验,确保当前操作者具有管理员权限。* @return 返回操作的结果,创建成功时返回true,否则返回false。* @throws BusinessException 如果当前操作者不具备管理员权限,则抛出无权限的业务异常。*/
@PostMapping("/create")
public BaseResponse<Boolean> createUser(@RequestBody UserCreateRequest createRequest, HttpServletRequest request){// 权限校验逻辑,确保是管理员操作if(!isAdmin(request)){throw new BusinessException(ErrorCode.NO_AUTH);}boolean result=userService.createUser(createRequest);return ResultUtils.success(result);
}

添加个人主页,可以完善个人信息(上传头像没有实现)

上传头像没有实现,主要我不知道怎么把本地上传的图片远程保存,前端代码这里不太会写(网上有,但不知道怎么改自己的代码)
image.png

import React, { useState, useRef, useLayoutEffect } from 'react';
import { GridContent } from '@ant-design/pro-layout';
import { Menu } from 'antd';
import BaseView from './components/base';
import styles from './style.less';
import SecurityView from './components/security';const { Item } = Menu;type AccountSettingsStateKeys = 'base' | 'security' | 'binding' | 'notification';
type AccountSettingsState = {mode: 'inline' | 'horizontal';selectKey: AccountSettingsStateKeys;
};const AccountSettings: React.FC = () => {const menuMap: Record<string, React.ReactNode> = {base: '基本设置',security: '安全设置',// binding: '账号绑定',// notification: '新消息通知',};const [initConfig, setInitConfig] = useState<AccountSettingsState>({mode: 'inline',selectKey: 'base',});const dom = useRef<HTMLDivElement>();const resize = () => {requestAnimationFrame(() => {if (!dom.current) {return;}let mode: 'inline' | 'horizontal' = 'inline';const { offsetWidth } = dom.current;if (dom.current.offsetWidth < 641 && offsetWidth > 400) {mode = 'horizontal';}if (window.innerWidth < 768 && offsetWidth > 400) {mode = 'horizontal';}setInitConfig({ ...initConfig, mode: mode as AccountSettingsState['mode'] });});};useLayoutEffect(() => {if (dom.current) {window.addEventListener('resize', resize);resize();}return () => {window.removeEventListener('resize', resize);};}, [dom.current]);const getMenu = () => {return Object.keys(menuMap).map((item) => <Item key={item}>{menuMap[item]}</Item>);};const renderChildren = () => {const { selectKey } = initConfig;switch (selectKey) {case 'base':return <BaseView />;case 'security':return <SecurityView />;default:return null;}};return (<GridContent><divclassName={styles.main}ref={(ref) => {if (ref) {dom.current = ref;}}}><div className={styles.leftMenu}><Menumode={initConfig.mode}selectedKeys={[initConfig.selectKey]}onClick={({ key }) => {setInitConfig({...initConfig,selectKey: key as AccountSettingsStateKeys,});}}>{getMenu()}</Menu></div><div className={styles.right}><div className={styles.title}>{menuMap[initConfig.selectKey]}</div>{renderChildren()}</div></div></GridContent>
);
};
export default AccountSettings;
import { CheckOutlined, UploadOutlined } from '@ant-design/icons';
import { ProForm, ProFormInstance, ProFormSelect, ProFormText } from '@ant-design/pro-components';// @ts-ignore
import { Button, GetProp, message, Upload, UploadProps } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
import styles from './BaseView.less';
import { currentUser as queryCurrent, updateLoginUser } from '@/services/ant-design-pro/api';const isDev = process.env.NODE_ENV === 'development';const BaseView: React.FC = () => {//const { styles } = useStyles();// @ts-ignoreconst [currentUser, setCurrentUser] = useState<API.CurrentUser>(null);const [loading, setLoading] = useState(true);const [avatar, setAvatar] = useState<string>('');const [modifyPhone, setModifyPhone] = useState<boolean>(true);const [modifyEmail, setmodifyEmail] = useState<boolean>(true);type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];const props: UploadProps = {name: 'avatar',action: isDev ? '/user-center/upload' : 'http://47.109.85.240/user-center/upload',listType: 'picture',defaultFileList: [],onChange(info) {if (info.file.status !== 'uploading') {console.log(info.file, info.fileList);}if (info.file.status === 'done') {message.success(`${info.file.name} file uploaded successfully`);setAvatar(info.file.response.data);} else if (info.file.status === 'error') {message.error(`${info.file.name} file upload failed.`);}},beforeUpload(file: FileType) {// @ts-ignoreconst isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';if (!isJpgOrPng) {message.error('只能上传 JPG/PNG 文件!');}// @ts-ignoreconst isLt2M = file.size / 1024 / 1024 < 2;if (!isLt2M) {message.error('文件大小必须小于 2MB!');}return isJpgOrPng && isLt2M;},};// 头像组件 方便以后独立,增加裁剪之类的功能// eslint-disable-next-line @typescript-eslint/no-shadowconst AvatarView = ({ avatar }: { avatar: string }) => (<><div className={styles.avatar_title}>头像</div><div className={styles.avatar}><img src={avatar} alt="avatar" /></div><div className={styles.button_view}>/<Upload {...props}><Button icon={<UploadOutlined />}>上传头像</Button></Upload></div></>);const fetchData = async () => {try {const userData = await queryCurrent();// @ts-ignoresetCurrentUser(userData);setLoading(false);} catch (error) {// 处理错误}};useEffect(() => {fetchData();// 清除副作用return () => {// 如果有需要,在组件卸载时进行清理工作};}, []); // 传入空的依赖数组,确保 useEffect 只在组件挂载时执行一次const getAvatarURL = () => {console.log('currentser', currentUser);if (currentUser) {if (currentUser.avatarUrl) {return currentUser.avatarUrl;}const url = 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png';return url;}return '';};const handleFinish = async (values: API.CurrentUser) => {try {// 打印 values 和 avatar 的值console.log('values:', values);console.log('avatar:', avatar);// 更新// @ts-ignoreconst res = await updateLoginUser({...values,avatar: avatar === '' ? currentUser.avatar : avatar,id: currentUser.id,});// @ts-ignoreif (res === 0) {message.success('更新基本信息成功');fetchData();return;} else {message.error('更新基本信息失败');}// 如果失败去设置用户错误信息} catch (error) {console.log(error);message.error('更新基本信息失败');}};const formRef = useRef<ProFormInstance>();return (<div className={styles.baseView}>{loading ? null : (<><div className={styles.left}><ProFormformRef={formRef}layout="vertical"onFinish={async (values) => {await handleFinish(values as API.CurrentUser);}}submitter={{searchConfig: {submitText: '更新基本信息',},render: (_, dom) => dom[1],}}initialValues={{...currentUser,phone: currentUser?.phone,}}><ProFormTextwidth="md"name="username"label="用户名"rules={[{required: true,message: '请输入您的用户名!',},]}/><ProFormSelectwidth="md"options={[{value: 0,label: '男',},{value: 1,label: '女',},]}name="gender"label="性别"/><div style={{ display: 'flex', alignItems: 'center' }}><ProFormText width="md" name="phone" label="电话" disabled={modifyPhone} />{modifyPhone ? (<span style={{ marginLeft: '15px' }}><akey="Modify"style={{ fontSize: 15, lineHeight: '32px' }}onClick={() => {setModifyPhone(false);formRef?.current?.setFieldsValue({ phone: '' });}}>修改</a></span>) : (<CheckOutlinedtwoToneColor="#52c41a"style={{ marginLeft: '15px' }}onClick={() => {setModifyPhone(true);}}/>)}</div><div style={{ display: 'flex', alignItems: 'center' }}><ProFormText width="md" name="email" label="邮箱" disabled={modifyEmail} />{modifyEmail ? (<span style={{ marginLeft: '15px' }}><akey="Modify"style={{ fontSize: 15, lineHeight: '32px' }}onClick={() => {setmodifyEmail(false);formRef?.current?.setFieldsValue({ email: '' });}}>修改</a></span>) : (<CheckOutlinedtwoToneColor="#52c41a"style={{ marginLeft: '15px' }}onClick={() => {setmodifyEmail(true);}}/>)}</div></ProForm></div><div className={styles.right}><AvatarView avatar={getAvatarURL()} /></div></>)}</div>);
};
export default BaseView;

添加默认头像

注册时就把默认头像添加进去
image.png
因为右上角头像位置还有用户名所以在注册时添加用户名强制这样登录进去右上角就不会转圈了
image.png

打造一个所有用户可发帖的页面

前端页面,√

image.png

后端建表,接口,√

后端跟用户类似
建表语句

create table post(id            bigint auto_increment comment 'id' primary key,content       text                               null comment '内容',photo         varchar(1024)                      null comment '照片地址',reviewStatus  int      default 0                 not null comment '状态(0-待审核, 1-通过, 2-拒绝)',reviewMessage varchar(512)                       null comment '审核信息',viewNum       int                                not null default 0 comment '浏览数',thumbNum      int                                not null default 0 comment '点赞数',userId        bigint                             not null comment '创建用户 id',createTime    datetime default CURRENT_TIMESTAMP not null comment '创建时间',updateTime    datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',isDelete      tinyint  default 0                 not null comment '是否删除'
);alter table post add FOREIGN KEY (userId) REFERENCES user(id);create table post_thumb
(id         bigint auto_increment comment 'id'primary key,postId     bigint                             not null comment '帖子 id',userId     bigint                             not null comment '创建用户 id',createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间'
)comment '帖子点赞记录';

MVC架构这里就省略(controller, service, mapper)
前端也省略
image.png

前后端联调√

后端添加全局请求拦截器(统一去判断用户权限、统计记录请求日志)

我们通过 AOP 实现,先引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

我们重新创建个包 aspect,和 controller 这些同级

在包里创建 LogAndAuthAspect 类

package com.ivy.usercenter.aspect;import com.ivy.usercenter.common.ErrorCode;
import com.ivy.usercenter.contant.UserConstant;
import com.ivy.usercenter.exception.BusinessException;
import com.ivy.usercenter.model.domain.User;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;/*** 全局请求拦截器** @author ivy* @date 2024/4/22 14:19*/
@Aspect
@Slf4j
@Component
public class LogAndAuthAspect {// 定义切入点,这里指的是UserController下的所有方法,排除登录和注册方法@Pointcut("execution(* com.ivy.usercenter.controller.UserController.*(..)) " +"&& !execution(* com.ivy.usercenter.controller.UserController.userLogin(..)) " +"&& !execution(* com.ivy.usercenter.controller.UserController.userRegister(..))" +"&& !execution(* com.ivy.usercenter.controller.UserController.updateCurrentUser(..))"//            + "execution(* com.ivy.usercenter.controller.PostController.*(..)) " +//            "&& !execution(* com.ivy.usercenter.controller.PostController.listPostWithUser(..)) " +//            "&& !execution(* com.ivy.usercenter.controller.PostController.addPost(..))" +//            "&& !execution(* com.ivy.usercenter.controller.PostController.postDoThumb(..))")public void controllerLogAndAuth() {}// 定义前置通知,记录请求信息和进行权限验证@Before("controllerLogAndAuth()")public void beforeAdvice(JoinPoint joinPoint) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();// 记录请求内容,使用log对象进行日志记录log.info("Request URL: {}", request.getRequestURL().toString());log.info("HTTP Method: {}", request.getMethod());log.info("IP: {}", request.getRemoteAddr());log.info("Class Method: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());// 对于特定的方法,允许所有登录用户访问if ("getCurrentUser".equals(joinPoint.getSignature().getName())) {// 只要用户登录即可if (request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE) == null) {throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");}// 直接返回,不进行后续的管理员权限验证return;}// 权限验证逻辑User user = (User) request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);if (user == null) {throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");} else if (user.getUserRole() != UserConstant.ADMIN_ROLE) {// 需要管理员权限throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无管理员权限");}}// 环绕通知用于记录方法执行时间@Around("controllerLogAndAuth()")public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object result = joinPoint.proceed(); // 执行目标方法long endTime = System.currentTimeMillis();log.info("执行时间:{} ms", endTime - startTime);return result;}
}

BUG

前端页面不能同步操作需要刷新

比如删除,添加用户或帖子时,还有发帖时都会有错误弹窗(不知道哪里的)
刷新后页面才好(不刷新,操作后仍是原样)
image.png
image.png

上传头像的BUG

解决上传头像,更新头像的问题,还有信息的更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/6847.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

W801学习笔记二十:宋词学习应用

前三章完成了唐诗的应用&#xff0c;本章将实现宋词的学习应用。 宋词与唐诗的区别不大&#xff0c;马上开始。 1、我们需要参考前面唐诗的方式&#xff0c;把宋词文本下载下来&#xff0c;并进行格式整理。 W801学习笔记十七&#xff1a;古诗学习应用——上 2、在菜单中添加…

[论文阅读]Adversarial Autoencoders(aae)和代码

In this paper, we propose the “adversarial autoencoder” (AAE), which is a probabilistic autoencoder that uses the recently proposed generative adversarial networks (GAN) to perform variational inference by matching the aggregated posterior of the hidden …

proxmox宿主机安装桌面

装完proxmox启动后一般进入shell界面&#xff0c;之后都是另外一台电脑连接web管理等操作&#xff0c;一直用起来还好。不过这样需要另外一台电脑连接管理操作&#xff0c;有时候调试时毕竟还是会有些不方便&#xff0c;就想能不能在宿主机上装个桌面做这类事&#xff0c;今天用…

python数据分析——大数据和云计算

大数据和云计算 前言一、大数据二、大数据定义三、数据存储单位四、大数据存储技术五、大数据应用技术六、大数据特征七、数据容量八、数据类型的多样性8.1结构化数据8.2半结构化数据8.3非结构化数据 九、获取数据的速度十、可变性十一、真实性十二、复杂性十三、价值十四、云计…

传输控制协议TCP

一、TCP简介 TCP是面向连接的&#xff1a; TCP连接只能有两个端点&#xff0c;TCP连接是点对点的&#xff1b; TCP提供可靠交互的服务&#xff1b; TCP提供全双工通信。 面向字节流&#xff1a; TCP中的“流”指的是流入或流出进程的字节序列&#xff1b; 虽然应用程序和…

Mac基于Docker-ubuntu构建c/c++编译环境

编译环境安装和使用被充分验证&#xff0c;如有期望补充的内容欢迎留言评论。 目录 前言 Docker desktop下载安装 修改镜像源 选择ubuntu镜像 docker容器启动 参数说明: 宿主机与docker容器文件共享 宿主机与docker容器拷贝文件 为 Ubuntu 配置 ssh、vim、make 相关工…

Flyway使用教程

Flyway使用教程 背景&#xff1a; 在开发环境对多个不同版本的分支进行开发&#xff0c;如果此时涉及到多张表结构修改&#xff08;比如新增字段&#xff09;&#xff0c;而在测试环境时却忘了整理SQL 脚本给测试人员执行&#xff0c;就会导致出现 bug&#xff0c;从而影响测试…

Android selinux权限

一.SE 概述 SELinux 是由美国NSA&#xff08;国安局&#xff09;和 SCC 开发的 Linux的一个扩张强制访问控制安全模块。原先是在Fluke上开发的&#xff0c;2000年以 GNU GPL 发布。从 fedora core 2开始&#xff0c; 2.6内核的版本都支持SELinux。 在 SELinux 出现之前&#…

PHP ASCII码的字符串用mb_convert_encoding 转utf-8之后不生效

检测数据类型是ascii,转码之后再检测还是utf-8没生效 private function toUTF8($str){$encode mb_detect_encoding($str, array("ASCII",UTF-8,"GB2312","GBK",BIG5,LATIN1));if ($encode ! UTF-8) {$str1 mb_convert_encoding($str, UTF-8, …

抓包证书安装到安卓7.0+手机

前言: 首先理解一下,这个不只是证书到浏览器,而是抓包证书到安卓7.0+手机上的文章; 还有一点区分,在浏览器上装的证书,只是让抓包工具可以抓取手机浏览器的包,而不是抓取手机app上的包; 如果你的证书只是简单的在浏览器下进行安装,那么你的手机app是走不了代理网络的…

【微服务】服务保护(通过Sentinel解决雪崩问题)

Sentinel解决雪崩问题 雪崩问题服务保护方案服务降级保护 服务保护技术SentinelFallback服务熔断 雪崩问题 在微服务调用链中如果有一个服务的问题导致整条链上的服务都不可用&#xff0c;称为雪崩 原因 微服务之间的相互调用&#xff0c;服务提供者出现故障服务的消费者没有…

【微服务】配置管理

Nacos配置管理 配置管理配置共享配置热更新 配置管理 将微服务集群中常用&#xff0c;经常变化的配置都写到一个独立的配置文件微服务中进行统一管理 配置共享 在Nacos的界面当中进行配置管理&#xff0c;在配置列表中添加配置 比如各个服务中的jdbc的连接配置&#xff1a; …

HTML/CSS1

1.前置说明 请点这里 2.img元素 格式&#xff1a; <img src"图片地址" alt"占位文字" width"图片宽度" height"图片高度">其中alt是当图片加载失败时显示的文字 而且不同内核的浏览器显示出来的占位文字的效果也是不尽相同的…

【Android学习】简单的登录页面和业务逻辑实现

实现功能 1 登录页&#xff1a;密码登录和验证码登录 2 忘记密码页&#xff1a;修改密码 3 页面基础逻辑 java代码 基础页面 XML login_main.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.and…

Servlet_JSP

1.一些回顾 对于Tomcat部署中 我们有一些补充的点需要在此说明一下 1.如果我们想要查询MINEType的话 可以到TOMCAT_HOME/conf/web.xml中进行查询 里面记录了不同类型对应的MINEType 2.我们客户端发送请求数据给服务器之后 服务器会调用父类中的service方法 然后在内部决定调用…

CMakeLists.txt语法规则:while 循环

一. 简介 前面几篇文章学习了 CMakeLists.txt语法中的一种循环写法&#xff0c;文章如下&#xff1a; CMakeLists.txt语法规则&#xff1a;foreach 循环基本用法-CSDN博客 CMakeLists.txt语法规则&#xff1a;foreach循环的关键字-CSDN博客 本文学习 while循环写法。 二. …

Day08-JavaWeb开发-MySQL(多表查询内外连接子查询事务索引)Mybatis入门

1. MySQL多表查询 1.1 概述 1.2 内连接 -- 内连接 -- A. 查询员工的姓名, 及所属的部门名称(隐式内连接实现) select tb_emp.name, tb_dept.name from tb_emp,tb_dept where tb_emp.dept_id tb_dept.id;-- 起别名 select * from tb_emp e, tb_dept d where e.dept_id d.id…

tomcat+maven+java+mysql图书管理系统2-完善项目结构,添加相关依赖

1.创建java目录 接着选择java&#xff0c;回车&#xff0c;则创建成功&#xff0c;成功后在左侧栏能看见 2.修改pom.xml文件,(添加依赖) <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi…

vs 2022 Xamarin 生成 Android apk

再保存&#xff0c;如果没有生成apk就重启软件 再试一次

Windows内核开发:如何使用STL

前言 大家都知道应用层c的STL非常强大&#xff0c;非常好用&#xff0c;但是在内核下就没法用了。针对这个问题&#xff0c;经过我不懈的寻找&#xff0c;终于找到了解决内核无法使用STL的方法。 使用new/delete关键字 先说一下常用关键字如何在内核中使用。其实只需要在一个全…