diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolUserFollowController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolUserFollowController.java index d9adeed3..2fddb852 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolUserFollowController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolUserFollowController.java @@ -1,20 +1,20 @@ package org.jeecg.modules.aiol.controller; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import org.jeecg.common.api.vo.Result; import org.jeecg.common.system.query.QueryGenerator; import org.jeecg.common.system.query.QueryRuleEnum; import org.jeecg.common.util.oConvertUtils; import org.jeecg.modules.aiol.entity.AiolUserFollow; +import org.jeecg.modules.aiol.entity.UserFollow; import org.jeecg.modules.aiol.service.IAiolUserFollowService; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.system.util.JwtUtil; @@ -26,6 +26,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.extern.slf4j.Slf4j; +import org.jeecg.modules.system.entity.SysUser; +import org.jeecg.modules.system.service.impl.SysUserServiceImpl; import org.jeecgframework.poi.excel.ExcelImportUtil; import org.jeecgframework.poi.excel.def.NormalExcelConstants; import org.jeecgframework.poi.excel.entity.ExportParams; @@ -33,231 +35,343 @@ import org.jeecgframework.poi.excel.entity.ImportParams; import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; import org.jeecg.common.system.base.controller.JeecgController; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.servlet.ModelAndView; -import java.util.Date; import com.alibaba.fastjson.JSON; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; import org.jeecg.common.aspect.annotation.AutoLog; import org.apache.shiro.authz.annotation.RequiresPermissions; - /** + + +/** * @Description: 关注关系 * @Author: jeecg-boot - * @Date: 2025-09-11 + * @Date: 2025-09-11 * @Version: V1.0 */ -@Tag(name="关注关系") +@Tag(name = "关注关系") @RestController @RequestMapping("/aiol/aiolUserFollow") @Slf4j public class AiolUserFollowController extends JeecgController { - @Autowired - private IAiolUserFollowService aiolUserFollowService; - @Autowired - private ISysBaseAPI sysBaseApi; - - /** - * 分页列表查询 - * - * @param aiolUserFollow - * @param pageNo - * @param pageSize - * @param req - * @return - */ - //@AutoLog(value = "关注关系-分页列表查询") - @Operation(summary="关注关系-分页列表查询") - @GetMapping(value = "/list") - public Result> queryPageList(AiolUserFollow aiolUserFollow, - @RequestParam(name="pageNo", defaultValue="1") Integer pageNo, - @RequestParam(name="pageSize", defaultValue="10") Integer pageSize, - HttpServletRequest req) { + @Autowired + private IAiolUserFollowService aiolUserFollowService; + @Autowired + private ISysBaseAPI sysBaseApi; + @Autowired + private SysUserServiceImpl sysUserService; + @Autowired + private RedisTemplate redisTemplate; + + + /** + * 分页列表查询 + * + * @param aiolUserFollow + * @param pageNo + * @param pageSize + * @param req + * @return + */ + //@AutoLog(value = "关注关系-分页列表查询") + @Operation(summary = "关注关系-分页列表查询") + @GetMapping(value = "/list") + public Result> queryPageList(AiolUserFollow aiolUserFollow, + @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, + HttpServletRequest req) { QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(aiolUserFollow, req.getParameterMap()); - Page page = new Page(pageNo, pageSize); - IPage pageList = aiolUserFollowService.page(page, queryWrapper); - return Result.OK(pageList); - } - - /** - * 添加 - * - * @param aiolUserFollow - * @return - */ - @AutoLog(value = "关注关系-添加") - @Operation(summary="关注关系-添加") - @RequiresPermissions("aiol:aiol_user_follow:add") - @PostMapping(value = "/add") - public Result add(@RequestBody AiolUserFollow aiolUserFollow) { - aiolUserFollowService.save(aiolUserFollow); - - return Result.OK("添加成功!"); - } - - /** - * 编辑 - * - * @param aiolUserFollow - * @return - */ - @AutoLog(value = "关注关系-编辑") - @Operation(summary="关注关系-编辑") - @RequiresPermissions("aiol:aiol_user_follow:edit") - @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST}) - public Result edit(@RequestBody AiolUserFollow aiolUserFollow) { - aiolUserFollowService.updateById(aiolUserFollow); - return Result.OK("编辑成功!"); - } - - /** - * 通过id删除 - * - * @param id - * @return - */ - @AutoLog(value = "关注关系-通过id删除") - @Operation(summary="关注关系-通过id删除") - @RequiresPermissions("aiol:aiol_user_follow:delete") - @DeleteMapping(value = "/delete") - public Result delete(@RequestParam(name="id",required=true) String id) { - aiolUserFollowService.removeById(id); - return Result.OK("删除成功!"); - } - - /** - * 批量删除 - * - * @param ids - * @return - */ - @AutoLog(value = "关注关系-批量删除") - @Operation(summary="关注关系-批量删除") - @RequiresPermissions("aiol:aiol_user_follow:deleteBatch") - @DeleteMapping(value = "/deleteBatch") - public Result deleteBatch(@RequestParam(name="ids",required=true) String ids) { - this.aiolUserFollowService.removeByIds(Arrays.asList(ids.split(","))); - return Result.OK("批量删除成功!"); - } - - /** - * 通过id查询 - * - * @param id - * @return - */ - //@AutoLog(value = "关注关系-通过id查询") - @Operation(summary="关注关系-通过id查询") - @GetMapping(value = "/queryById") - public Result queryById(@RequestParam(name="id",required=true) String id) { - AiolUserFollow aiolUserFollow = aiolUserFollowService.getById(id); - if(aiolUserFollow==null) { - return Result.error("未找到对应数据"); - } - return Result.OK(aiolUserFollow); - } - - /** - * 关注用户 - * - * @param followedId 被关注者ID - * @param request - * @return - */ - @AutoLog(value = "关注关系-关注用户") - @Operation(summary="关注用户", description = "当前登录用户关注指定用户") - @PostMapping(value = "/follow") - public Result follow(@RequestParam(name="followedId", required=true) String followedId, - HttpServletRequest request) { - try { - // 1. 获取当前登录用户信息 - String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); - String username = JwtUtil.getUsername(token); - LoginUser sysUser = sysBaseApi.getUserByName(username); - if (sysUser == null) { - return Result.error("用户未登录或登录已过期"); - } - - // 2. 检查是否已经关注 - QueryWrapper checkWrapper = new QueryWrapper<>(); - checkWrapper.eq("follower_id", sysUser.getId()) - .eq("followed_id", followedId); - AiolUserFollow existingFollow = aiolUserFollowService.getOne(checkWrapper); - - if (existingFollow != null) { - return Result.error("已经关注过该用户"); - } - - // 3. 检查是否关注自己 - if (sysUser.getId().equals(followedId)) { - return Result.error("不能关注自己"); - } - - // 4. 创建关注关系 - AiolUserFollow userFollow = new AiolUserFollow(); - userFollow.setFollowerId(sysUser.getId()); - userFollow.setFollowedId(followedId); - userFollow.setCreateBy(sysUser.getUsername()); - userFollow.setCreateTime(new Date()); - - aiolUserFollowService.save(userFollow); - return Result.OK("关注成功!"); - - } catch (Exception e) { - log.error("关注用户失败: followedId={}, error={}", followedId, e.getMessage(), e); - return Result.error("关注失败: " + e.getMessage()); - } - } - - /** - * 取消关注用户 - * - * @param followedId 被关注者ID - * @param request - * @return - */ - @AutoLog(value = "关注关系-取消关注用户") - @Operation(summary="取消关注用户", description = "当前登录用户取消关注指定用户") - @DeleteMapping(value = "/unfollow") - public Result unfollow(@RequestParam(name="followedId", required=true) String followedId, - HttpServletRequest request) { - try { - // 1. 获取当前登录用户信息 - String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); - String username = JwtUtil.getUsername(token); - LoginUser sysUser = sysBaseApi.getUserByName(username); - if (sysUser == null) { - return Result.error("用户未登录或登录已过期"); - } - - // 2. 查找关注关系 - QueryWrapper deleteWrapper = new QueryWrapper<>(); - deleteWrapper.eq("follower_id", sysUser.getId()) - .eq("followed_id", followedId); - - boolean removed = aiolUserFollowService.remove(deleteWrapper); - if (removed) { - return Result.OK("取消关注成功!"); - } else { - return Result.error("未找到关注关系"); - } - - } catch (Exception e) { - log.error("取消关注用户失败: followedId={}, error={}", followedId, e.getMessage(), e); - return Result.error("取消关注失败: " + e.getMessage()); - } - } + Page page = new Page(pageNo, pageSize); + IPage pageList = aiolUserFollowService.page(page, queryWrapper); + return Result.OK(pageList); + } /** - * 导出excel - * - * @param request - * @param aiolUserFollow - */ + * 添加 + * + * @param aiolUserFollow + * @return + */ + @AutoLog(value = "关注关系-添加") + @Operation(summary = "关注关系-添加") + @RequiresPermissions("aiol:aiol_user_follow:add") + @PostMapping(value = "/add") + public Result add(@RequestBody AiolUserFollow aiolUserFollow) { + aiolUserFollowService.save(aiolUserFollow); + + return Result.OK("添加成功!"); + } + + /** + * 编辑 + * + * @param aiolUserFollow + * @return + */ + @AutoLog(value = "关注关系-编辑") + @Operation(summary = "关注关系-编辑") + @RequiresPermissions("aiol:aiol_user_follow:edit") + @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST}) + public Result edit(@RequestBody AiolUserFollow aiolUserFollow) { + aiolUserFollowService.updateById(aiolUserFollow); + return Result.OK("编辑成功!"); + } + + /** + * 通过id删除 + * + * @param id + * @return + */ + @AutoLog(value = "关注关系-通过id删除") + @Operation(summary = "关注关系-通过id删除") + @RequiresPermissions("aiol:aiol_user_follow:delete") + @DeleteMapping(value = "/delete") + public Result delete(@RequestParam(name = "id", required = true) String id) { + aiolUserFollowService.removeById(id); + return Result.OK("删除成功!"); + } + + /** + * 批量删除 + * + * @param ids + * @return + */ + @AutoLog(value = "关注关系-批量删除") + @Operation(summary = "关注关系-批量删除") + @RequiresPermissions("aiol:aiol_user_follow:deleteBatch") + @DeleteMapping(value = "/deleteBatch") + public Result deleteBatch(@RequestParam(name = "ids", required = true) String ids) { + this.aiolUserFollowService.removeByIds(Arrays.asList(ids.split(","))); + return Result.OK("批量删除成功!"); + } + + /** + * 通过id查询 + * + * @param id + * @return + */ + //@AutoLog(value = "关注关系-通过id查询") + @Operation(summary = "关注关系-通过id查询") + @GetMapping(value = "/queryById") + public Result queryById(@RequestParam(name = "id", required = true) String id) { + AiolUserFollow aiolUserFollow = aiolUserFollowService.getById(id); + if (aiolUserFollow == null) { + return Result.error("未找到对应数据"); + } + return Result.OK(aiolUserFollow); + } + + /** + * 关注用户 + * + * @param followedId 被关注者ID + * @param request + * @return + */ + @AutoLog(value = "关注关系-关注用户") + @Operation(summary = "关注用户", description = "当前登录用户关注指定用户") + @PostMapping(value = "/follow") + public Result follow(@RequestParam(name = "followedId", required = true) String followedId, + HttpServletRequest request) { + try { + // 1. 获取当前登录用户信息 + String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); + String username = JwtUtil.getUsername(token); + LoginUser sysUser = sysBaseApi.getUserByName(username); + if (sysUser == null) { + return Result.error("用户未登录或登录已过期"); + } + + // 2. 检查是否已经关注 + QueryWrapper checkWrapper = new QueryWrapper<>(); + checkWrapper.eq("follower_id", sysUser.getId()) + .eq("followed_id", followedId); + AiolUserFollow existingFollow = aiolUserFollowService.getOne(checkWrapper); + + if (existingFollow != null) { + return Result.error("已经关注过该用户"); + } + + // 3. 检查是否关注自己 + if (sysUser.getId().equals(followedId)) { + return Result.error("不能关注自己"); + } + + // 4. 创建关注关系 + AiolUserFollow userFollow = new AiolUserFollow(); + userFollow.setFollowerId(sysUser.getId()); + userFollow.setFollowedId(followedId); + userFollow.setCreateBy(sysUser.getUsername()); + userFollow.setCreateTime(new Date()); + + aiolUserFollowService.save(userFollow); + + // 添加到Redis缓存 + String recentFollowKey = "user:recent:follow:" + sysUser.getId(); + String frequentVisitKey = "user:frequent:visit:" + sysUser.getId(); + long currentTime = System.currentTimeMillis(); + // 使用RedisTemplate操作Redis + // 存储最近关注(使用时间戳作为分数) + redisTemplate.opsForZSet().add(recentFollowKey, followedId, currentTime); + // 存储访问频率(初始值为1) + redisTemplate.opsForZSet().add(frequentVisitKey, followedId, 1); + // 设置过期时间(例如30天) + redisTemplate.expire(recentFollowKey, 30, TimeUnit.DAYS); + redisTemplate.expire(frequentVisitKey, 30, TimeUnit.DAYS); + + return Result.OK("关注成功!"); + + } catch (Exception e) { + log.error("关注用户失败: followedId={}, error={}", followedId, e.getMessage(), e); + return Result.error("关注失败: " + e.getMessage()); + } + } + + //查询关注列表 + @AutoLog(value = "关注关系-查询关注列表") + @Operation(summary = "查询关注列表", description = "当前登录用户关注的所有用户") + @GetMapping(value = "/followList") + public Result> followList(HttpServletRequest req) { + try { + // 1. 获取当前登录用户信息 + String token = req.getHeader(CommonConstant.X_ACCESS_TOKEN); + String username = JwtUtil.getUsername(token); + LoginUser sysUser = sysBaseApi.getUserByName(username); + if (sysUser == null) { + return Result.error("用户未登录或登录已过期"); + } + // 2. 查询关注列表 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("follower_id", sysUser.getId()); + List list = aiolUserFollowService.list(queryWrapper); + if (list != null && !list.isEmpty()) { + // 使用Stream提取被关注者的ID列表 + List followedIds = list.stream() + .map(AiolUserFollow::getFollowedId) + .collect(Collectors.toList()); + // 根据被关注者的ID列表查询用户信息 + List followedUsers = sysUserService.listByIds(followedIds); + if (followedUsers != null && !followedUsers.isEmpty()) { + List userFollows = new ArrayList<>(); + for (SysUser followedUser : followedUsers) { + UserFollow userFollow = new UserFollow(); + userFollow.setId(followedUser.getId()); + userFollow.setIsFollow(true); + userFollow.setRealName(followedUser.getRealname()); + userFollow.setAvatar(followedUser.getAvatar()); + userFollows.add(userFollow); + } + return Result.OK(userFollows); + } + } + } catch (Exception e) { + return Result.error(e.getMessage()); + } + return Result.OK(); + } + + //查询最近关注 + @AutoLog(value = "关注关系-查询最近关注") + @Operation(summary = "查询最近关注", description = "当前登录用户最近关注的用户") + @GetMapping(value = "/recentFollowList") + public Result> recentFollowList(HttpServletRequest req) { + try { + // 1. 获取当前登录用户信息 + String token = req.getHeader(CommonConstant.X_ACCESS_TOKEN); + String username = JwtUtil.getUsername(token); + LoginUser sysUser = sysBaseApi.getUserByName(username); + if (sysUser == null) { + return Result.error("用户未登录或登录已过期"); + } + // 2. 查询最近关注列表 + String recentFollowKey = "user:recent:follow:" + sysUser.getId(); + Set objects = redisTemplate.opsForZSet().range(recentFollowKey, 0, -1); + Set followedIds = objects.stream() + .map(obj -> String.valueOf(obj)) + .collect(Collectors.toSet()); + if (followedIds != null && !followedIds.isEmpty()) { + // 根据被关注者的ID列表查询用户信息 + List followedUsers = sysUserService.listByIds(followedIds); + if (followedUsers != null && !followedUsers.isEmpty()) { + List userFollows = new ArrayList<>(); + for (SysUser followedUser : followedUsers) { + UserFollow userFollow = new UserFollow(); + userFollow.setId(followedUser.getId()); + userFollow.setIsFollow(true); + userFollow.setRealName(followedUser.getRealname()); + userFollow.setAvatar(followedUser.getAvatar()); + userFollows.add(userFollow); + } + return Result.OK(userFollows); + } + } + } catch (Exception e) { + return Result.error(e.getMessage()); + } + return Result.OK(); + } + + /** + * 取消关注用户 + * + * @param followedId 被关注者ID + * @param request + * @return + */ + @AutoLog(value = "关注关系-取消关注用户") + @Operation(summary = "取消关注用户", description = "当前登录用户取消关注指定用户") + @DeleteMapping(value = "/unfollow") + public Result unfollow(@RequestParam(name = "followedId", required = true) String followedId, + HttpServletRequest request) { + try { + // 1. 获取当前登录用户信息 + String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); + String username = JwtUtil.getUsername(token); + LoginUser sysUser = sysBaseApi.getUserByName(username); + if (sysUser == null) { + return Result.error("用户未登录或登录已过期"); + } + + // 2. 查找关注关系 + QueryWrapper deleteWrapper = new QueryWrapper<>(); + deleteWrapper.eq("follower_id", sysUser.getId()) + .eq("followed_id", followedId); + + boolean removed = aiolUserFollowService.remove(deleteWrapper); + if (removed) { + // 从Redis中删除关注记录 + String recentFollowKey = "user:recent:follow:" + sysUser.getId(); + String frequentVisitKey = "user:frequent:visit:" + sysUser.getId(); + // 从最近关注列表中删除 + redisTemplate.opsForZSet().remove(recentFollowKey, followedId); + // 从访问频率列表中删除 + redisTemplate.opsForZSet().remove(frequentVisitKey, followedId); + return Result.OK("取消关注成功!"); + } else { + return Result.error("未找到关注关系"); + } + + } catch (Exception e) { + log.error("取消关注用户失败: followedId={}, error={}", followedId, e.getMessage(), e); + return Result.error("取消关注失败: " + e.getMessage()); + } + } + + /** + * 导出excel + * + * @param request + * @param aiolUserFollow + */ @RequiresPermissions("aiol:aiol_user_follow:exportXls") @RequestMapping(value = "/exportXls") public ModelAndView exportXls(HttpServletRequest request, AiolUserFollow aiolUserFollow) { @@ -265,12 +379,12 @@ public class AiolUserFollowController extends JeecgController importExcel(HttpServletRequest request, HttpServletResponse response) { diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/entity/UserFollow.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/entity/UserFollow.java new file mode 100644 index 00000000..73ff80d1 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/entity/UserFollow.java @@ -0,0 +1,18 @@ +package org.jeecg.modules.aiol.entity; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class UserFollow { + @Schema(description = "id") + private String id; + @Schema(description = "用户名称") + private String realName; + @Schema(description = "头像") + private String avatar; + @Schema(description = "是否关注") + private Boolean isFollow; + +}