Browse Source

1.支持上传头像

2.支持修改个人信息
master
xie.bx 3 years ago
parent
commit
6d862060fc
  1. 8
      im-platform/src/main/java/com/lx/implatform/controller/FileController.java
  2. 9
      im-platform/src/main/java/com/lx/implatform/controller/FriendsController.java
  3. 10
      im-platform/src/main/java/com/lx/implatform/controller/UserController.java
  4. 20
      im-platform/src/main/java/com/lx/implatform/entity/User.java
  5. 3
      im-platform/src/main/java/com/lx/implatform/service/IFriendsService.java
  6. 2
      im-platform/src/main/java/com/lx/implatform/service/IUserService.java
  7. 20
      im-platform/src/main/java/com/lx/implatform/service/impl/FriendsServiceImpl.java
  8. 22
      im-platform/src/main/java/com/lx/implatform/service/impl/UserServiceImpl.java
  9. 17
      im-platform/src/main/java/com/lx/implatform/service/thirdparty/FileService.java
  10. 33
      im-platform/src/main/java/com/lx/implatform/util/FileUtil.java
  11. 3
      im-platform/src/main/java/com/lx/implatform/util/ImageUtil.java
  12. 13
      im-platform/src/main/java/com/lx/implatform/util/MinioUtil.java
  13. 9
      im-platform/src/main/java/com/lx/implatform/vo/FriendsVO.java
  14. 4
      im-platform/src/main/java/com/lx/implatform/vo/RegisterVO.java
  15. 2
      im-platform/src/main/java/com/lx/implatform/vo/SingleMessageVO.java
  16. 27
      im-platform/src/main/java/com/lx/implatform/vo/UnfriendsUserVO.java
  17. 2
      im-platform/src/main/java/com/lx/implatform/vo/UploadImageVO.java
  18. 20
      im-platform/src/main/java/com/lx/implatform/vo/UserVO.java
  19. 2
      im-platform/src/main/resources/application.yml
  20. 3
      im-platform/src/main/resources/db/db.sql
  21. 1
      im-ui/src/App.vue
  22. 1
      im-ui/src/components/ChatItem.vue
  23. 4
      im-ui/src/components/ChatTime.vue
  24. 2
      im-ui/src/components/FriendsItem.vue
  25. 2
      im-ui/src/components/HeadImage.vue
  26. 2
      im-ui/src/router/index.js
  27. 5
      im-ui/src/store/chatStore.js
  28. 19
      im-ui/src/store/friendsStore.js
  29. 74
      im-ui/src/view/Chat.vue
  30. 44
      im-ui/src/view/Friends.vue
  31. 19
      im-ui/src/view/Home.vue

8
im-platform/src/main/java/com/lx/implatform/controller/FileController.java

@ -3,6 +3,7 @@ package com.lx.implatform.controller;
import com.lx.common.result.Result; import com.lx.common.result.Result;
import com.lx.common.result.ResultUtils; import com.lx.common.result.ResultUtils;
import com.lx.implatform.service.thirdparty.FileService; import com.lx.implatform.service.thirdparty.FileService;
import com.lx.implatform.vo.UploadImageVO;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -28,15 +29,10 @@ public class FileController {
@ApiOperation(value = "上传图片",notes="上传图片") @ApiOperation(value = "上传图片",notes="上传图片")
@PostMapping("/image/upload") @PostMapping("/image/upload")
public Result upload(MultipartFile file) { public Result<UploadImageVO> upload(MultipartFile file) {
return ResultUtils.success(fileService.uploadImage(file)); return ResultUtils.success(fileService.uploadImage(file));
} }
@GetMapping("/online")
@ApiOperation(value = "查找用户",notes="根据昵称查找用户")
public Result checkOnline(){
return ResultUtils.success("");
}
} }

9
im-platform/src/main/java/com/lx/implatform/controller/FriendsController.java

@ -13,6 +13,7 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -27,7 +28,7 @@ public class FriendsController {
@GetMapping("/list") @GetMapping("/list")
@ApiOperation(value = "好友列表",notes="获取好友列表") @ApiOperation(value = "好友列表",notes="获取好友列表")
public Result findFriends(){ public Result< List<FriendsVO>> findFriends(){
List<Friends> friendsList = friendsService.findFriendsByUserId(SessionContext.getSession().getId()); List<Friends> friendsList = friendsService.findFriendsByUserId(SessionContext.getSession().getId());
List<FriendsVO> vos = friendsList.stream().map(f->{ List<FriendsVO> vos = friendsList.stream().map(f->{
FriendsVO vo = BeanUtils.copyProperties(f,FriendsVO.class); FriendsVO vo = BeanUtils.copyProperties(f,FriendsVO.class);
@ -52,6 +53,12 @@ public class FriendsController {
return ResultUtils.success(); return ResultUtils.success();
} }
@PutMapping("/update")
@ApiOperation(value = "更新好友信息",notes="更新好友头像或昵称")
public Result delFriends(@Valid @RequestBody FriendsVO vo){
friendsService.update(vo);
return ResultUtils.success();
}
} }

10
im-platform/src/main/java/com/lx/implatform/controller/UserController.java

@ -14,6 +14,7 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import java.util.List; import java.util.List;
@ -52,6 +53,15 @@ public class UserController {
return ResultUtils.success(userVO); return ResultUtils.success(userVO);
} }
@PutMapping("/update")
@ApiOperation(value = "修改用户信息",notes="修改用户信息,仅允许修改登录用户信息")
public Result update(@Valid @RequestBody UserVO vo){
userService.update(vo);
return ResultUtils.success();
}
@GetMapping("/findByNickName") @GetMapping("/findByNickName")
@ApiOperation(value = "查找用户",notes="根据昵称查找用户") @ApiOperation(value = "查找用户",notes="根据昵称查找用户")

20
im-platform/src/main/java/com/lx/implatform/entity/User.java

@ -45,11 +45,29 @@ public class User extends Model<User> {
private String nickName; private String nickName;
/** /**
* 用户名 * 性别
*/
@TableField("sex")
private Integer sex;
/**
* 头像
*/ */
@TableField("head_image") @TableField("head_image")
private String headImage; private String headImage;
/**
* 头像缩略图
*/
@TableField("head_image_thumb")
private String headImageThumb;
/**
* 个性签名
*/
@TableField("signature")
private String signature;
/** /**
* 密码(明文) * 密码(明文)
*/ */

3
im-platform/src/main/java/com/lx/implatform/service/IFriendsService.java

@ -2,6 +2,7 @@ package com.lx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.lx.implatform.entity.Friends; import com.lx.implatform.entity.Friends;
import com.lx.implatform.vo.FriendsVO;
import java.util.List; import java.util.List;
@ -23,4 +24,6 @@ public interface IFriendsService extends IService<Friends> {
void delFriends(long friendId); void delFriends(long friendId);
void update(FriendsVO vo);
} }

2
im-platform/src/main/java/com/lx/implatform/service/IUserService.java

@ -21,6 +21,8 @@ public interface IUserService extends IService<User> {
User findUserByName(String username); User findUserByName(String username);
void update(UserVO vo);
List<UserVO> findUserByNickName(String nickname); List<UserVO> findUserByNickName(String nickname);
List<Long> checkOnline(String userIds); List<Long> checkOnline(String userIds);

20
im-platform/src/main/java/com/lx/implatform/service/impl/FriendsServiceImpl.java

@ -10,6 +10,7 @@ import com.lx.implatform.mapper.FriendsMapper;
import com.lx.implatform.service.IFriendsService; import com.lx.implatform.service.IFriendsService;
import com.lx.implatform.service.IUserService; import com.lx.implatform.service.IUserService;
import com.lx.implatform.session.SessionContext; import com.lx.implatform.session.SessionContext;
import com.lx.implatform.vo.FriendsVO;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -72,6 +73,25 @@ public class FriendsServiceImpl extends ServiceImpl<FriendsMapper, Friends> impl
return this.count(queryWrapper) > 0; return this.count(queryWrapper) > 0;
} }
@Override
public void update(FriendsVO vo) {
long userId = SessionContext.getSession().getId();
QueryWrapper<Friends> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Friends::getUserId,userId)
.eq(Friends::getFriendId,vo.getFriendId());
Friends f = this.getOne(queryWrapper);
if(f == null){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"对方不是您的好友");
}
f.setFriendHeadImage(vo.getFriendHeadImage());
f.setFriendNickName(vo.getFriendNickName());
this.updateById(f);
}
private void bindFriends(long userId, long friendsId) { private void bindFriends(long userId, long friendsId) {
QueryWrapper<Friends> queryWrapper = new QueryWrapper<>(); QueryWrapper<Friends> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda() queryWrapper.lambda()

22
im-platform/src/main/java/com/lx/implatform/service/impl/UserServiceImpl.java

@ -9,6 +9,8 @@ import com.lx.implatform.entity.User;
import com.lx.implatform.exception.GlobalException; import com.lx.implatform.exception.GlobalException;
import com.lx.implatform.mapper.UserMapper; import com.lx.implatform.mapper.UserMapper;
import com.lx.implatform.service.IUserService; import com.lx.implatform.service.IUserService;
import com.lx.implatform.session.SessionContext;
import com.lx.implatform.session.UserSession;
import com.lx.implatform.vo.RegisterVO; import com.lx.implatform.vo.RegisterVO;
import com.lx.implatform.vo.UserVO; import com.lx.implatform.vo.UserVO;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -57,6 +59,25 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
} }
@Override
public void update(UserVO vo) {
UserSession session = SessionContext.getSession();
if(session.getId() != vo.getId()){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"不允许修改其他用户的信息!");
}
User user = this.getById(vo.getId());
if(null == user){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"用户不存在");
}
user.setNickName(vo.getNickName());
user.setSex(vo.getSex());
user.setSignature(vo.getSignature());
user.setHeadImage(vo.getHeadImage());
user.setHeadImageThumb(vo.getHeadImageThumb());
this.updateById(user);
}
@Override @Override
public List<UserVO> findUserByNickName(String nickname) { public List<UserVO> findUserByNickName(String nickname) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>(); QueryWrapper<User> queryWrapper = new QueryWrapper<>();
@ -69,7 +90,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
vo.setOnline(isOnline(u.getId())); vo.setOnline(isOnline(u.getId()));
return vo; return vo;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
return vos; return vos;
} }

17
im-platform/src/main/java/com/lx/implatform/service/thirdparty/FileService.java

@ -2,11 +2,14 @@ package com.lx.implatform.service.thirdparty;
import com.lx.common.enums.FileTypeEnum; import com.lx.common.enums.FileTypeEnum;
import com.lx.common.enums.ResultCode; import com.lx.common.enums.ResultCode;
import com.lx.implatform.util.FileUtil;
import com.lx.implatform.exception.GlobalException; import com.lx.implatform.exception.GlobalException;
import com.lx.implatform.util.FileUtil;
import com.lx.implatform.util.ImageUtil; import com.lx.implatform.util.ImageUtil;
import com.lx.implatform.util.MinioUtil; import com.lx.implatform.util.MinioUtil;
import com.lx.implatform.vo.UploadImageVO; import com.lx.implatform.vo.UploadImageVO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -47,14 +50,24 @@ public class FileService {
public UploadImageVO uploadImage(MultipartFile file){ public UploadImageVO uploadImage(MultipartFile file){
try { try {
// 图片格式校验
if(!FileUtil.isImage(file.getOriginalFilename())){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片格式不合法");
}
// 上传原图
UploadImageVO vo = new UploadImageVO(); UploadImageVO vo = new UploadImageVO();
String fileName = minioUtil.upload(bucketName,imagePath,file); String fileName = minioUtil.upload(bucketName,imagePath,file);
if(StringUtils.isEmpty(fileName)){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片上传失败");
}
vo.setOriginUrl(generUrl(FileTypeEnum.IMAGE,fileName)); vo.setOriginUrl(generUrl(FileTypeEnum.IMAGE,fileName));
// 上传缩略图 // 上传缩略图
byte[] imageByte = ImageUtil.compressForScale(file.getBytes(),100); byte[] imageByte = ImageUtil.compressForScale(file.getBytes(),100);
fileName = minioUtil.upload(bucketName,imagePath,file.getOriginalFilename(),imageByte,file.getContentType()); fileName = minioUtil.upload(bucketName,imagePath,file.getOriginalFilename(),imageByte,file.getContentType());
vo.setCompressUrl(generUrl(FileTypeEnum.IMAGE,fileName)); if(StringUtils.isEmpty(fileName)){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片上传失败");
}
vo.setThumbUrl(generUrl(FileTypeEnum.IMAGE,fileName));
return vo; return vo;
} catch (IOException e) { } catch (IOException e) {
log.error("上传图片失败,{}",e.getMessage(),e); log.error("上传图片失败,{}",e.getMessage(),e);

33
im-platform/src/main/java/com/lx/implatform/util/FileUtil.java

@ -0,0 +1,33 @@
package com.lx.implatform.util;
public class FileUtil {
/**
* 获取文件后缀
*
* @param fileName 文件名
* @return boolean
*/
public static String getFileExtension(String fileName) {
String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
return extension;
}
/**
* 判断文件是否图片类型
*
* @param fileName 文件名
* @return boolean
*/
public static boolean isImage(String fileName) {
String extension = getFileExtension(fileName);
String[] imageExtension = new String[]{"jpeg", "jpg", "bmp", "png"};
for (String e : imageExtension){
if (extension.toLowerCase().equals(e)) {
return true;
}
}
return false;
}
}

3
im-platform/src/main/java/com/lx/implatform/util/ImageUtil.java

@ -52,6 +52,9 @@ public class ImageUtil {
return imageBytes; return imageBytes;
} }
/** /**
* 自动调节精度(经验数值) * 自动调节精度(经验数值)
* *

13
im-platform/src/main/java/com/lx/implatform/util/MinioUtil.java

@ -3,6 +3,7 @@ package com.lx.implatform.util;
import com.lx.common.util.DateTimeUtils; import com.lx.common.util.DateTimeUtils;
import io.minio.*; import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -12,6 +13,7 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.Date; import java.util.Date;
@Slf4j
@Component @Component
public class MinioUtil { public class MinioUtil {
@ -28,7 +30,7 @@ public class MinioUtil {
try { try {
found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.error("查询bucket失败",e);
return false; return false;
} }
return found; return found;
@ -44,7 +46,7 @@ public class MinioUtil {
.bucket(bucketName) .bucket(bucketName)
.build()); .build());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.error("创建bucket失败,",e);
return false; return false;
} }
return true; return true;
@ -60,7 +62,7 @@ public class MinioUtil {
.bucket(bucketName) .bucket(bucketName)
.build()); .build());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.error("删除bucket失败,",e);
return false; return false;
} }
return true; return true;
@ -87,7 +89,7 @@ public class MinioUtil {
//文件名称相同会覆盖 //文件名称相同会覆盖
minioClient.putObject(objectArgs); minioClient.putObject(objectArgs);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.error("上传图片失败,",e);
return null; return null;
} }
return objectName; return objectName;
@ -113,7 +115,7 @@ public class MinioUtil {
//文件名称相同会覆盖 //文件名称相同会覆盖
minioClient.putObject(objectArgs); minioClient.putObject(objectArgs);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.error("上传图片失败,",e);
return null; return null;
} }
return objectName; return objectName;
@ -132,6 +134,7 @@ public class MinioUtil {
try { try {
minioClient.removeObject( RemoveObjectArgs.builder().bucket(bucketName).object(path+fileName).build()); minioClient.removeObject( RemoveObjectArgs.builder().bucket(bucketName).object(path+fileName).build());
}catch (Exception e){ }catch (Exception e){
log.error("删除图片失败,",e);
return false; return false;
} }
return true; return true;

9
im-platform/src/main/java/com/lx/implatform/vo/FriendsVO.java

@ -5,18 +5,21 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotNull;
@Data @Data
@ApiModel("好友信息VO") @ApiModel("好友信息VO")
public class FriendsVO { public class FriendsVO {
@NotNull(message = "好友id不可为空")
@ApiModelProperty(value = "好友id") @ApiModelProperty(value = "好友id")
private Long friendId; private Long friendId;
@NotNull(message = "好友昵称不可为空")
@ApiModelProperty(value = "用户昵称") @ApiModelProperty(value = "好友昵称")
private String friendNickName; private String friendNickName;
@ApiModelProperty(value = "用户头像") @ApiModelProperty(value = "好友头像")
private String friendHeadImage; private String friendHeadImage;
} }

4
im-platform/src/main/java/com/lx/implatform/vo/RegisterVO.java

@ -3,6 +3,7 @@ package com.lx.implatform.vo;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
@ -10,14 +11,17 @@ import javax.validation.constraints.NotEmpty;
@ApiModel("用户注册VO") @ApiModel("用户注册VO")
public class RegisterVO { public class RegisterVO {
@Length(max = 64,message = "用户名不能大于64字符")
@NotEmpty(message="用户名不可为空") @NotEmpty(message="用户名不可为空")
@ApiModelProperty(value = "用户名") @ApiModelProperty(value = "用户名")
private String userName; private String userName;
@Length(min=5,max = 20,message = "密码长度必须在5-20个字符之间")
@NotEmpty(message="用户密码不可为空") @NotEmpty(message="用户密码不可为空")
@ApiModelProperty(value = "用户密码") @ApiModelProperty(value = "用户密码")
private String password; private String password;
@Length(max = 64,message = "昵称不能大于64字符")
@NotEmpty(message="用户昵称不可为空") @NotEmpty(message="用户昵称不可为空")
@ApiModelProperty(value = "用户昵称") @ApiModelProperty(value = "用户昵称")
private String nickName; private String nickName;

2
im-platform/src/main/java/com/lx/implatform/vo/SingleMessageVO.java

@ -4,6 +4,7 @@ package com.lx.implatform.vo;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ -18,6 +19,7 @@ public class SingleMessageVO {
private Long recvUserId; private Long recvUserId;
@Length(max=1024,message = "内容长度不得大于1024")
@NotEmpty(message="发送内容不可为空") @NotEmpty(message="发送内容不可为空")
@ApiModelProperty(value = "发送内容") @ApiModelProperty(value = "发送内容")
private String content; private String content;

27
im-platform/src/main/java/com/lx/implatform/vo/UnfriendsUserVO.java

@ -1,27 +0,0 @@
package com.lx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("非好友用户信息VO")
public class UnfriendsUserVO {
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "用户名")
private String userName;
@ApiModelProperty(value = "用户昵称")
private String nickName;
@ApiModelProperty(value = "头像")
private String headImage;
@ApiModelProperty(value = "是否已是好友")
private Boolean isFriend;
}

2
im-platform/src/main/java/com/lx/implatform/vo/UploadImageVO.java

@ -12,5 +12,5 @@ public class UploadImageVO {
private String originUrl; private String originUrl;
@ApiModelProperty(value = "缩略图") @ApiModelProperty(value = "缩略图")
private String compressUrl; private String thumbUrl;
} }

20
im-platform/src/main/java/com/lx/implatform/vo/UserVO.java

@ -4,23 +4,43 @@ package com.lx.implatform.vo;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data @Data
@ApiModel("用户信息VO") @ApiModel("用户信息VO")
public class UserVO { public class UserVO {
@NotNull(message = "用户id不能为空")
@ApiModelProperty(value = "id") @ApiModelProperty(value = "id")
private Long id; private Long id;
@NotEmpty(message = "用户名不能为空")
@Length(max = 64,message = "用户名不能大于64字符")
@ApiModelProperty(value = "用户名") @ApiModelProperty(value = "用户名")
private String userName; private String userName;
@NotEmpty(message = "用户昵称不能为空")
@Length(max = 64,message = "昵称不能大于64字符")
@ApiModelProperty(value = "用户昵称") @ApiModelProperty(value = "用户昵称")
private String nickName; private String nickName;
@ApiModelProperty(value = "性别")
private Integer sex;
@Length(max = 64,message = "个性签名不能大于1024个字符")
@ApiModelProperty(value = "个性签名")
private String signature;
@ApiModelProperty(value = "头像") @ApiModelProperty(value = "头像")
private String headImage; private String headImage;
@ApiModelProperty(value = "头像缩略图")
private String headImageThumb;
@ApiModelProperty(value = "是否在线") @ApiModelProperty(value = "是否在线")
private Boolean online; private Boolean online;

2
im-platform/src/main/resources/application.yml

@ -5,7 +5,7 @@ server:
spring: spring:
datasource: datasource:
driver-class-name: com.mysql.jdbc.Driver driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/lx-im?useUnicode=true&characterEncoding=utf-8 url: jdbc:mysql://localhost:3306/simple-im?useUnicode=true&characterEncoding=utf-8
username: root username: root
password: root password: root

3
im-platform/src/main/resources/db/db.sql

@ -4,7 +4,10 @@ create table `user`(
`user_name` varchar(255) not null comment '用户名', `user_name` varchar(255) not null comment '用户名',
`nick_name` varchar(255) not null comment '用户昵称', `nick_name` varchar(255) not null comment '用户昵称',
`head_image` varchar(255) default '' comment '用户头像', `head_image` varchar(255) default '' comment '用户头像',
`head_image_thumb` varchar(255) default '' comment '用户头像缩略图',
`password` varchar(255) not null comment '密码(明文)', `password` varchar(255) not null comment '密码(明文)',
`sex` tinyint(1) default 0 comment '性别 0:男 1::女',
`signature` varchar(1024) not null comment '个性签名',
`last_login_time` datetime DEFAULT null comment '最后登录时间', `last_login_time` datetime DEFAULT null comment '最后登录时间',
`created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间', `created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间',
unique key `idx_user_name`(user_name), unique key `idx_user_name`(user_name),

1
im-ui/src/App.vue

@ -21,7 +21,6 @@ export default {
font-family: 'Avenir', Helvetica, Arial, sans-serif; font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50; color: #2c3e50;
position: absolute; position: absolute;
height: 100%; height: 100%;

1
im-ui/src/components/ChatItem.vue

@ -47,6 +47,7 @@
} }
}, },
methods: { methods: {
onClickClose(){ onClickClose(){
this.$emit("del"); this.$emit("del");
} }

4
im-ui/src/components/ChatTime.vue

@ -21,9 +21,9 @@
let curTime = new Date(); let curTime = new Date();
let dayDiff =curTime.getDate() - time.getDate() ; let dayDiff =curTime.getDate() - time.getDate() ;
if (time.getDate() === new Date().getDate()) { if (time.getDate() === new Date().getDate()) {
strtime = time.getHours() < 9 ? "0" + time.getHours() : time.getHours(); strtime = time.getHours() <= 9 ? "0" + time.getHours() : time.getHours();
strtime += ":" strtime += ":"
strtime += time.getMinutes() < 9 ? "0" + time.getMinutes() : time.getMinutes(); strtime += time.getMinutes() <= 9 ? "0" + time.getMinutes() : time.getMinutes();
} else if (dayDiff === 1) { } else if (dayDiff === 1) {
strtime = "昨天"; strtime = "昨天";
} else if (dayDiff < 7) { } else if (dayDiff < 7) {

2
im-ui/src/components/FriendsItem.vue

@ -1,7 +1,7 @@
<template> <template>
<div class="item" :class="active ? 'active' : ''"> <div class="item" :class="active ? 'active' : ''">
<div class="avatar"> <div class="avatar">
<head-image :src="friendsInfo.friendHeadImage" :size="40"> </head-image> <head-image :url="friendsInfo.friendHeadImage" > </head-image>
</div> </div>
<div class="text"> <div class="text">
<div>{{ friendsInfo.friendNickName}}</div> <div>{{ friendsInfo.friendNickName}}</div>

2
im-ui/src/components/HeadImage.vue

@ -1,6 +1,6 @@
<template> <template>
<div class='img-box'> <div class='img-box'>
<img src="../assets/default_head.png" style="width: 100%;height: 100%;cursor: pointer;" /> <img :src="url" style="width: 100%;height: 100%;cursor: pointer;" />
</div> </div>
</template> </template>

2
im-ui/src/router/index.js

@ -36,7 +36,7 @@ export default new VueRouter({
name: "Friends", name: "Friends",
path: "/home/friends", path: "/home/friends",
component: () => import("../view/Friends"), component: () => import("../view/Friends"),
}, }
] ]
} }
] ]

5
im-ui/src/store/chatStore.js

@ -47,6 +47,7 @@ export default {
state.activeIndex = state.chats.length-1; state.activeIndex = state.chats.length-1;
} }
}, },
insertMessage(state, msgInfo) { insertMessage(state, msgInfo) {
let targetId = msgInfo.selfSend?msgInfo.recvUserId:msgInfo.sendUserId; let targetId = msgInfo.selfSend?msgInfo.recvUserId:msgInfo.sendUserId;
let chat = state.chats.find((chat)=>chat.targetId==targetId); let chat = state.chats.find((chat)=>chat.targetId==targetId);
@ -60,9 +61,11 @@ export default {
} }
}, },
setChatUserInfo(state, userInfo){ setChatUserInfo(state, userInfo){
console.log(userInfo)
for(let i in state.chats){ for(let i in state.chats){
if(state.chats[i].targetId == userInfo.id){ if(state.chats[i].targetId == userInfo.id){
state.chats[i].headImage = userInfo.headImage; state.chats[i].headImage = userInfo.headImageThumb;
state.chats[i].showName = userInfo.nickName; state.chats[i].showName = userInfo.nickName;
break; break;
} }

19
im-ui/src/store/friendsStore.js

@ -5,7 +5,6 @@ export default {
state: { state: {
friendsList: [], friendsList: [],
activeIndex: -1, activeIndex: -1,
activeUserInfo: {},
timer: null timer: null
}, },
mutations: { mutations: {
@ -18,20 +17,20 @@ export default {
this.commit("refreshOnlineStatus"); this.commit("refreshOnlineStatus");
}) })
}, },
setActiveUserInfo(state, userInfo){
state.activeUserInfo = userInfo;
},
setFriendsList(state, friendsList) { setFriendsList(state, friendsList) {
state.friendsList = friendsList; state.friendsList = friendsList;
}, },
updateFriends(state,friendsInfo){
console.log(friendsInfo)
state.friendsList.forEach((f,index)=>{
if(f.friendId==friendsInfo.friendId){
state.friendsList[index] = friendsInfo;
}
})
},
activeFriends(state, index) { activeFriends(state, index) {
state.activeIndex = index; state.activeIndex = index;
httpRequest({
url: `/api/user/find/${state.friendsList[index].friendId}`,
method: 'get'
}).then((userInfo) => {
this.commit("setActiveUserInfo",userInfo);
})
}, },
removeFriends(state, index) { removeFriends(state, index) {
state.friendsList.splice(index, 1); state.friendsList.splice(index, 1);

74
im-ui/src/view/Chat.vue

@ -25,7 +25,7 @@
<li v-for="item in messages" :key="item.id" <li v-for="item in messages" :key="item.id"
:class="{ 'im-chat-mine': item.sendUserId == $store.state.userStore.userInfo.id }"> :class="{ 'im-chat-mine': item.sendUserId == $store.state.userStore.userInfo.id }">
<div class="head-image"> <div class="head-image">
<head-image :url="headImage" ></head-image> <head-image :url="headImage(item)" ></head-image>
</div> </div>
<div class="im-msg-content"> <div class="im-msg-content">
<div class="im-msg-top"> <div class="im-msg-top">
@ -41,7 +41,8 @@
</div> </div>
</el-main> </el-main>
<el-footer height="150px" class="im-chat-footer"> <el-footer height="25%" class="im-chat-footer">
<div class="chat-tool-bar"></div>
<textarea v-model="messageContent" ref="sendBox" class="textarea" @keyup.enter="onSendMessage()"></textarea> <textarea v-model="messageContent" ref="sendBox" class="textarea" @keyup.enter="onSendMessage()"></textarea>
<div class="im-chat-send"> <div class="im-chat-send">
<el-button type="primary" @click="onSendMessage()">发送</el-button> <el-button type="primary" @click="onSendMessage()">发送</el-button>
@ -72,6 +73,19 @@
methods: { methods: {
onClickItem(index) { onClickItem(index) {
this.$store.commit("activeChat", index); this.$store.commit("activeChat", index);
//
let userId = this.chatStore.chats[index].targetId;
this.$http({
url: `/api/user/find/${userId}`,
method: 'get'
}).then((userInfo) => {
//
let chat = this.chatStore.chats[index];
if (userInfo.headImageThumb != chat.headImage ||
userInfo.nickName != chat.showName) {
this.updateFriendInfo(userInfo,index)
}
})
}, },
onSendMessage() { onSendMessage() {
let msgInfo = { let msgInfo = {
@ -89,8 +103,9 @@
msgInfo.sendTime = new Date().getTime(); msgInfo.sendTime = new Date().getTime();
msgInfo.sendUserId = this.$store.state.userStore.userInfo.id; msgInfo.sendUserId = this.$store.state.userStore.userInfo.id;
msgInfo.selfSend = true; msgInfo.selfSend = true;
console.log(msgInfo);
this.$store.commit("insertMessage", msgInfo); this.$store.commit("insertMessage", msgInfo);
console.log(this.$refs.sendBox)
// //
this.$refs.sendBox.focus(); this.$refs.sendBox.focus();
// //
@ -105,43 +120,67 @@
onDelItem(chat,index){ onDelItem(chat,index){
this.$store.commit("removeChat",index); this.$store.commit("removeChat",index);
}, },
showName(item) { updateFriendInfo(userInfo,index){
if (item.sendUserId == this.$store.state.userStore.userInfo.id) { let friendsInfo={
friendId: userInfo.id,
friendNickName: userInfo.nickName,
friendHeadImage: userInfo.headImageThumb
};
this.$http({
url: "/api/friends/update",
method: "put",
data: friendsInfo
}).then(() => {
this.$store.commit("updateFriends",friendsInfo);
this.$store.commit("setChatUserInfo",userInfo);
})
},
showName(msg) {
if (msg.sendUserId == this.$store.state.userStore.userInfo.id) {
return this.$store.state.userStore.userInfo.nickName; return this.$store.state.userStore.userInfo.nickName;
} else { } else {
let index = this.$store.state.chatStore.activeIndex; let index = this.$store.state.chatStore.activeIndex;
let chats = this.$store.state.chatStore.chats let chats = this.$store.state.chatStore.chats
return chats[index].showName; return chats[index].showName;
} }
},
headImage(msg){
if(msg.sendUserId == this.$store.state.userStore.userInfo.id){
return this.$store.state.userStore.userInfo.headImageThumb;
}else{
let index = this.$store.state.chatStore.activeIndex;
let chats = this.$store.state.chatStore.chats
if(index>=0 && chats.length > 0){
let chats = this.$store.state.chatStore.chats;
return chats[index].headImage;
}
}
return "";
} }
}, },
computed: { computed: {
chatStore(){
return this.$store.state.chatStore;
},
messages() { messages() {
let index = this.$store.state.chatStore.activeIndex; let index = this.$store.state.chatStore.activeIndex;
let chats = this.$store.state.chatStore.chats let chats = this.$store.state.chatStore.chats
if (index >= 0 && chats.length > 0) { if (index >= 0 && chats.length > 0) {
console.log(chats[index].messages)
return chats[index].messages; return chats[index].messages;
} }
return []; return [];
}, },
titleName(){ titleName(){
let index = this.$store.state.chatStore.activeIndex; let index = this.$store.state.chatStore.activeIndex;
let chats = this.$store.state.chatStore.chats
if(index>=0 && chats.length > 0){
let chats = this.$store.state.chatStore.chats; let chats = this.$store.state.chatStore.chats;
return chats[index].showName;
}
return "";
},
headImage(){
let index = this.$store.state.chatStore.activeIndex;
let chats = this.$store.state.chatStore.chats
if(index>=0 && chats.length > 0){ if(index>=0 && chats.length > 0){
let chats = this.$store.state.chatStore.chats; let chats = this.$store.state.chatStore.chats;
return chats[index].headImage; return chats[index].showName;
} }
return ""; return "";
} }
} }
} }
</script> </script>
@ -300,6 +339,11 @@
flex-direction: column; flex-direction: column;
padding: 0; padding: 0;
.chat-tool-bar {
width: 100%;
height: 40px;
}
textarea { textarea {
box-sizing: border-box; box-sizing: border-box;
padding: 5px; padding: 5px;

44
im-ui/src/view/Friends.vue

@ -10,8 +10,7 @@
</el-col> </el-col>
<el-col :span="1"></el-col> <el-col :span="1"></el-col>
<el-col :span="3"> <el-col :span="3">
<el-button plain icon="el-icon-plus" style="border: none; font-size: 20px;color: black;" <el-button plain icon="el-icon-plus" style="border: none; font-size: 20px;color: black;" title="添加好友" @click="onShowAddFriends"></el-button>
title="添加好友" @click="onShowAddFriends"></el-button>
</el-col> </el-col>
</el-row> </el-row>
<add-friends :dialogVisible="showAddFriend" @close="onCloseAddFriends" @add="onAddFriend()"> <add-friends :dialogVisible="showAddFriend" @close="onCloseAddFriends" @add="onAddFriend()">
@ -19,9 +18,8 @@
</el-header> </el-header>
<el-main> <el-main>
<div v-for="(friendsInfo,index) in $store.state.friendsStore.friendsList" :key="friendsInfo.id"> <div v-for="(friendsInfo,index) in $store.state.friendsStore.friendsList" :key="friendsInfo.id">
<friends-item v-show="friendsInfo.friendNickName.startsWith(searchText)" :friendsInfo="friendsInfo" <friends-item v-show="friendsInfo.friendNickName.startsWith(searchText)" :friendsInfo="friendsInfo" :index="index"
:index="index" :active="index === $store.state.friendsStore.activeIndex" :active="index === $store.state.friendsStore.activeIndex" @del="onDelItem(friendsInfo,index)" @click.native="onClickItem(friendsInfo,index)">
@del="onDelItem(friendsInfo,index)" @click.native="onClickItem(friendsInfo,index)">
</friends-item> </friends-item>
</div> </div>
</el-main> </el-main>
@ -29,14 +27,12 @@
<el-container class="r-friend-box"> <el-container class="r-friend-box">
<div v-show="$store.state.friendsStore.activeIndex>=0"> <div v-show="$store.state.friendsStore.activeIndex>=0">
<div class="user-detail"> <div class="user-detail">
<div class="detail-head-image"> <head-image class="detail-head-image" :url="activeUserInfo.headImage"></head-image>
<head-image :url="$store.state.friendsStore.activeUserInfo.headImage" ></head-image>
</div>
<div class="info-item"> <div class="info-item">
<el-descriptions title="好友信息" class="description" :column="1"> <el-descriptions title="好友信息" class="description" :column="1">
<el-descriptions-item label="用户名">{{ $store.state.friendsStore.activeUserInfo.userName }} <el-descriptions-item label="用户名">{{ activeUserInfo.userName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="昵称">{{ $store.state.friendsStore.activeUserInfo.nickName }} <el-descriptions-item label="昵称">{{ activeUserInfo.nickName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="备注">好基友</el-descriptions-item> <el-descriptions-item label="备注">好基友</el-descriptions-item>
<el-descriptions-item label="签名">世界这么大我想去看看</el-descriptions-item> <el-descriptions-item label="签名">世界这么大我想去看看</el-descriptions-item>
@ -66,7 +62,8 @@
data() { data() {
return { return {
searchText: "", searchText: "",
showAddFriend: false showAddFriend: false,
activeUserInfo: {}
} }
}, },
methods: { methods: {
@ -78,6 +75,17 @@
}, },
onClickItem(friendsInfo, index) { onClickItem(friendsInfo, index) {
this.$store.commit("activeFriends", index); this.$store.commit("activeFriends", index);
this.$http({
url: `/api/user/find/${friendsInfo.friendId}`,
method: 'get'
}).then((userInfo) => {
this.activeUserInfo = userInfo;
//
if (userInfo.headImageThumb != friendsInfo.friendHeadImage ||
userInfo.nickName != friendsInfo.friendNickName) {
this.updateFriendInfo(friendsInfo, userInfo,index)
}
})
}, },
onDelItem(friendsInfo, index) { onDelItem(friendsInfo, index) {
this.$http({ this.$http({
@ -92,7 +100,7 @@
}) })
}, },
onSend() { onSend() {
let userInfo = this.$store.state.friendsStore.activeUserInfo let userInfo = this.activeUserInfo;
let chatInfo = { let chatInfo = {
type: 'single', type: 'single',
targetId: userInfo.id, targetId: userInfo.id,
@ -102,6 +110,18 @@
this.$store.commit("openChat", chatInfo); this.$store.commit("openChat", chatInfo);
this.$store.commit("activeChat", 0); this.$store.commit("activeChat", 0);
this.$router.push("/home/chat"); this.$router.push("/home/chat");
},
updateFriendInfo(friendsInfo, userInfo,index) {
friendsInfo.friendHeadImage = userInfo.headImageThumb;
friendsInfo.friendNickName = userInfo.nickName;
this.$http({
url: "/api/friends/update",
method: "put",
data: friendsInfo
}).then(() => {
this.$store.commit("updateFriends",friendsInfo);
this.$store.commit("setChatUserInfo",userInfo);
})
} }
} }

19
im-ui/src/view/Home.vue

@ -2,7 +2,7 @@
<el-container> <el-container>
<el-aside width="80px" class="navi-bar"> <el-aside width="80px" class="navi-bar">
<div class="user-head-image" @click="onClickHeadImage"> <div class="user-head-image" @click="onClickHeadImage">
<head-image :src="$store.state.userStore.userInfo.headImage" > </head-image> <head-image :url="$store.state.userStore.userInfo.headImageThumb" > </head-image>
</div> </div>
<el-menu background-color="#333333" text-color="#ddd" style="margin-top: 30px;" > <el-menu background-color="#333333" text-color="#ddd" style="margin-top: 30px;" >
@ -16,7 +16,7 @@
<span class="el-icon-user"></span> <span class="el-icon-user"></span>
</router-link> </router-link>
</el-menu-item> </el-menu-item>
<el-menu-item title="设置" index="/group"> <el-menu-item title="设置" @click="onClickSetting()" >
<span class="el-icon-setting"></span> <span class="el-icon-setting"></span>
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
@ -27,14 +27,21 @@
<el-main class="content-box"> <el-main class="content-box">
<router-view></router-view> <router-view></router-view>
</el-main> </el-main>
<setting :visible="showSettingDialog" @close="onCloseSetting()"></setting>
</el-container> </el-container>
</template> </template>
<script> <script>
import HeadImage from '../components/HeadImage.vue'; import HeadImage from '../components/HeadImage.vue';
import Setting from '../components/setting/Setting.vue';
export default { export default {
components:{HeadImage}, components:{HeadImage,Setting},
data(){
return {
showSettingDialog: false
}
},
methods: { methods: {
init(userInfo){ init(userInfo){
this.$store.commit("setUserInfo", userInfo); this.$store.commit("setUserInfo", userInfo);
@ -82,6 +89,12 @@
}, },
onClickHeadImage(){ onClickHeadImage(){
this.$message.success(JSON.stringify(this.$store.state.userStore.userInfo)); this.$message.success(JSON.stringify(this.$store.state.userStore.userInfo));
},
onClickSetting(){
this.showSettingDialog = true;
},
onCloseSetting(){
this.showSettingDialog = false;
} }
}, },
mounted() { mounted() {

Loading…
Cancel
Save