Jelajahi Sumber

用户拉黑功能

mazhenhang 1 bulan lalu
induk
melakukan
84ae96b63a

+ 7 - 2
LiangZhiYUMao/manifest.json

@@ -46,7 +46,7 @@
     },
     "quickapp" : {},
     "mp-weixin" : {
-        "appid" : "",
+        "appid" : "wx3e90d662a801266e",
         "setting" : {
             "urlCheck" : false,
             "postcss" : true,
@@ -54,7 +54,12 @@
             "es6" : true
         },
         "usingComponents" : true,
-        "lazyCodeLoading" : "requiredComponents"
+        "lazyCodeLoading" : "requiredComponents",
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "用于获取用户位置"
+            }
+        }
     },
     "mp-alipay" : {
         "usingComponents" : true

+ 1 - 1
LiangZhiYUMao/package-lock.json

@@ -395,7 +395,7 @@
         },
         "node_modules/tim-wx-sdk": {
             "version": "2.27.6",
-            "resolved": "https://mirrors.huaweicloud.com/repository/npm/tim-wx-sdk/-/tim-wx-sdk-2.27.6.tgz",
+            "resolved": "https://registry.npmjs.org/tim-wx-sdk/-/tim-wx-sdk-2.27.6.tgz",
             "integrity": "sha512-zB+eRdmigdhEDeqrXC0bLJonUQZzS5uKNPLFtrje503WAnmuxVQjq/n4Zle4FYHG4FiKHKhsrVd0aCYXABlFEg==",
             "license": "ISC"
         }

+ 2 - 2
LiangZhiYUMao/pages/index/index.vue

@@ -206,7 +206,7 @@
 			<view class="tabbar-item" @click="switchTab('message')">
 				<text class="tabbar-icon">💬</text>
 				<text class="tabbar-text">消息</text>
-				<view v-if="unreadCount > 0" class="tabbar-badge">{{ unreadCount }}</view>
+				<view v-if="unreadCount >= 1" class="tabbar-badge">{{ unreadCount }}</view>
 			</view>
 			<view class="tabbar-item" @click="switchTab('mine')">
 				<text class="tabbar-icon">👤</text>
@@ -230,7 +230,7 @@
 					userId: null
 				},
 				matchCount: 3,
-				unreadCount: 2,
+				unreadCount: 0,
 
 				// 轮播图数据
 				bannerList: [

+ 267 - 5
LiangZhiYUMao/pages/message/chat.vue

@@ -1,5 +1,48 @@
 <template>
   <view class="chat-page">
+	  <!-- 更多选项弹窗 -->
+	  <view v-if="showMoreOptionsModal" class="modal-mask" @click="closeMoreOptions">
+	    <view class="modal-content" @click.stop>
+	      <view class="modal-header">
+	        <text class="modal-title">{{ targetUserName }}</text>
+	        <text class="modal-subtitle">好友操作</text>
+	      </view>
+	      <view class="modal-body">
+	        <!-- <view class="modal-item" @click="goToUserProfile">
+	          <text class="item-icon">👤</text>
+	          <text class="item-text">查看资料</text>
+	        </view> -->
+	        <view class="modal-item" @click="blockFriend" :class="{ danger: true }">
+	          <text class="item-icon">🚫</text>
+	          <text class="item-text">拉黑好友</text>
+	        </view>
+	       <!-- <view class="modal-item" @click="reportUser" :class="{ danger: true }">
+	          <text class="item-icon">🚨</text>
+	          <text class="item-text">举报用户</text>
+	        </view> -->
+	      </view>
+	      <view class="modal-footer">
+	        <button class="modal-close-btn" @click="closeMoreOptions">取消</button>
+	      </view>
+	    </view>
+	  </view>
+	  
+	  <!-- 拉黑确认弹窗 -->
+	  <view v-if="showBlockConfirmModal" class="modal-mask" @click="closeBlockConfirm">
+	    <view class="confirm-modal" @click.stop>
+	      <view class="confirm-title">确认拉黑</view>
+	      <view class="confirm-content">
+	        <text>拉黑后将无法接收该用户的消息,是否确定?</text>
+	      </view>
+	      <view class="confirm-buttons">
+	        <button class="confirm-btn cancel" @click="closeBlockConfirm">取消</button>
+	        <button class="confirm-btn confirm" @click="confirmBlockFriend">确认</button>
+	      </view>
+	    </view>
+	  </view>
+	  
+	  
+	  
     <!-- 顶部导航 -->
     <view class="chat-header">
       <view class="header-left" @click="goBack">
@@ -11,7 +54,7 @@
           {{ isTargetOnline ? '在线' : '离线' }}
         </text>
       </view>
-      <view class="header-right">
+      <view class="header-right" @click="showMoreOptions">
         <text class="icon-more">⋯</text>
       </view>
     </view>
@@ -230,8 +273,11 @@ export default {
       nextReqMessageID: '',
       isTargetOnline: false,
       
-      emojis: ['😀', '😃', '😄', '😁', '😆', '😅', '😂', '🤣', '😊', '😇', '🙂', '🙃', '😉', '😌', '😍', '🥰', '😘', '😗', '😙', '😚', '😋', '😛', '😝', '😜', '🤪', '🤨', '🧐', '🤓', '😎', '🤩', '🥳']
-    };
+      emojis: ['😀', '😃', '😄', '😁', '😆', '😅', '😂', '🤣', '😊', '😇', '🙂', '🙃', '😉', '😌', '😍', '🥰', '😘', '😗', '😙', '😚', '😋', '😛', '😝', '😜', '🤪', '🤨', '🧐', '🤓', '😎', '🤩', '🥳'],
+		showMoreOptionsModal: false, // 控制更多选项弹窗显示
+	    showBlockConfirmModal: false // 控制拉黑确认弹窗显示
+	
+	};
   },
   
   async onLoad(options) {
@@ -287,6 +333,7 @@ export default {
       });
       return;
     }
+	
     
     // 保存用户ID(TIM需要字符串格式)
     this.userId = String(rawUserId);
@@ -354,7 +401,23 @@ export default {
         });
       }
     },
-    
+    more(userid) {
+      console.log('点击了更多按钮,开始跳转...', userid);
+      // 如果目标页面是普通页面,用navigateTo(保留当前页面);如果是tabbar页面,用switchTab
+      uni.navigateTo({
+        url: `/pages/message/more?userid=${userid}`,
+        success: () => {
+          console.log('跳转成功');
+        },
+        fail: (err) => {
+          console.error('跳转失败:', err);
+          uni.showToast({
+            title: '页面不存在或路径错误',
+            icon: 'none'
+          });
+        }
+      });
+    },
     /**
      * 导入用户到腾讯云IM
      */
@@ -1046,12 +1109,211 @@ export default {
         title: '语音播放功能开发中',
         icon: 'none'
       });
-    }
+    },
+	showMoreOptions() {
+	    this.showMoreOptionsModal = true;
+	  },
+	  closeMoreOptions() {
+	      this.showMoreOptionsModal = false;
+	    },
+		/**
+		   * 查看用户资料(跳转到原有more页面)
+		   */
+		  goToUserProfile() {
+		    this.closeMoreOptions();
+		    uni.navigateTo({
+		      url: `/pages/message/more?userid=${this.targetUserId}`,
+		      fail: (err) => {
+		        console.error('跳转失败:', err);
+		        uni.showToast({
+		          title: '页面不存在',
+		          icon: 'none'
+		        });
+		      }
+		    });
+		  },
+		  /**
+		     * 显示拉黑确认弹窗
+		     */
+		    blockFriend() {
+		      this.closeMoreOptions();
+		      this.showBlockConfirmModal = true;
+		    },
+		    
+		    /**
+		     * 关闭拉黑确认弹窗
+		     */
+		    closeBlockConfirm() {
+		      this.showBlockConfirmModal = false;
+		    },
+		    
+		    /**
+		     * 确认拉黑好友
+		     */
+		    async confirmBlockFriend() {
+		      try {
+		        uni.showLoading({
+		          title: '处理中...'
+		        });
+		        
+		        // 调用后端拉黑接口(根据实际接口调整)
+		        const [err, res] = await uni.request({
+		          url: 'http://localhost:8083/api/chatfriend/block',
+		          method: 'POST',
+		          data: {
+		            userId: this.userId,
+		            targetUserId: this.targetUserId,
+					targetUserName: this.targetUserName,
+					targetUserAvatar: this.targetUserAvatar
+					
+		          },
+		          header: {
+		            'Content-Type': 'application/json',
+		            // 'Authorization': 'Bearer ' + uni.getStorageSync('token')
+		          }
+		        });
+		        
+		        uni.hideLoading();
+		        
+		        if (err) throw new Error('网络请求失败');
+		        
+		        if (res.data && res.data.code === 200) {
+		          uni.showToast({ title: '拉黑成功', icon: 'success' });
+		          this.closeBlockConfirm();
+		          // 拉黑成功后返回上一页
+		          setTimeout(() => this.goBack(), 1500);
+		        } else {
+		          throw new Error(res.data?.message || '拉黑失败');
+		        }
+		      } catch (error) {
+		        console.error('拉黑失败:', error);
+		        uni.showToast({ title: error.message || '拉黑失败', icon: 'none' });
+		      }
+		    },
   }
 };
 </script>
 
 <style scoped>
+	/* 弹窗遮罩 */
+	.modal-mask {
+	  position: fixed;
+	  top: 0;
+	  left: 0;
+	  right: 0;
+	  bottom: 0;
+	  background-color: rgba(0, 0, 0, 0.5);
+	  z-index: 10000;
+	  display: flex;
+	  align-items: center;
+	  justify-content: center;
+	}
+	
+	/* 更多选项弹窗内容 */
+	.modal-content {
+	  width: 70%;
+	  background-color: #fff;
+	  border-radius: 20rpx;
+	  overflow: hidden;
+	}
+	
+	.modal-header {
+	  padding: 30rpx;
+	  text-align: center;
+	  border-bottom: 1px solid #eee;
+	}
+	
+	.modal-title {
+	  font-size: 32rpx;
+	  font-weight: bold;
+	  display: block;
+	}
+	
+	.modal-subtitle {
+	  font-size: 24rpx;
+	  color: #999;
+	  display: block;
+	  margin-top: 10rpx;
+	}
+	
+	.modal-body {
+	  padding: 20rpx 0;
+	}
+	
+	.modal-item {
+	  display: flex;
+	  align-items: center;
+	  padding: 25rpx 40rpx;
+	  font-size: 30rpx;
+	}
+	
+	.modal-item.danger {
+	  color: #fa5151;
+	}
+	
+	.item-icon {
+	  font-size: 32rpx;
+	  margin-right: 20rpx;
+	}
+	
+	.modal-footer {
+	  padding: 20rpx;
+	  border-top: 1px solid #eee;
+	}
+	
+	.modal-close-btn {
+	  width: 100%;
+	  background-color: #f5f5f5;
+	  border: none;
+	  border-radius: 10rpx;
+	  padding: 20rpx;
+	  font-size: 30rpx;
+	}
+	
+	/* 确认弹窗样式 */
+	.confirm-modal {
+	  width: 60%;
+	  background-color: #fff;
+	  border-radius: 20rpx;
+	  padding: 40rpx;
+	  text-align: center;
+	}
+	
+	.confirm-title {
+	  font-size: 32rpx;
+	  font-weight: bold;
+	  margin-bottom: 30rpx;
+	}
+	
+	.confirm-content {
+	  font-size: 28rpx;
+	  color: #666;
+	  margin-bottom: 40rpx;
+	  line-height: 1.5;
+	}
+	
+	.confirm-buttons {
+	  display: flex;
+	  justify-content: space-between;
+	}
+	
+	.confirm-btn {
+	  flex: 1;
+	  margin: 0 10rpx;
+	  padding: 20rpx;
+	  border-radius: 10rpx;
+	  border: none;
+	  font-size: 28rpx;
+	}
+	
+	.confirm-btn.cancel {
+	  background-color: #f5f5f5;
+	}
+	
+	.confirm-btn.confirm {
+	  background-color: #fa5151;
+	  color: #fff;
+	}
 .chat-page {
   display: flex;
   flex-direction: column;

+ 10 - 1
gateway/src/main/resources/application.yml

@@ -103,6 +103,14 @@ spring:
           filters:
             - StripPrefix=0
 
+        # 聊天REST API路由
+        - id: chat-api-routev
+          uri: http://localhost:1004
+          predicates:
+            - Path=/api/chatfriend/**
+          filters:
+            - StripPrefix=0
+
         # 首页服务路由(兜底路由)
         - id: homepage-route
           uri: http://localhost:8081
@@ -110,7 +118,8 @@ spring:
             - Path=/api/**
           filters:
             - StripPrefix=0
-      
+
+
       # WebSocket支持配置
       websocket:
         enabled: true

+ 10 - 1
gateway/target/classes/application.yml

@@ -103,6 +103,14 @@ spring:
           filters:
             - StripPrefix=0
 
+        # 聊天REST API路由
+        - id: chat-api-routev
+          uri: http://localhost:1004
+          predicates:
+            - Path=/api/chatfriend/**
+          filters:
+            - StripPrefix=0
+
         # 首页服务路由(兜底路由)
         - id: homepage-route
           uri: http://localhost:8081
@@ -110,7 +118,8 @@ spring:
             - Path=/api/**
           filters:
             - StripPrefix=0
-      
+
+
       # WebSocket支持配置
       websocket:
         enabled: true

+ 24 - 0
service/websocket/src/main/java/com/zhentao/controller/ChatFriendController.java

@@ -0,0 +1,24 @@
+package com.zhentao.controller;
+
+import com.zhentao.dto.BlockDto;
+import com.zhentao.service.ChatFriendService;
+import com.zhentao.vo.ResultVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/chatfriend")
+public class ChatFriendController {
+    @Autowired
+    private ChatFriendService chatFriendService;
+
+    @PostMapping("/block")
+    public ResultVo block(@RequestBody BlockDto blockDto){
+        return chatFriendService.block(blockDto);
+    }
+}

+ 9 - 0
service/websocket/src/main/java/com/zhentao/service/ChatConversationService.java

@@ -0,0 +1,9 @@
+package com.zhentao.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhentao.dto.BlockDto;
+import com.zhentao.entity.ChatConversation;
+
+public interface ChatConversationService extends IService<ChatConversation> {
+    void deleteChatConversation(BlockDto dto);
+}

+ 20 - 0
service/websocket/src/main/java/com/zhentao/service/impl/ChatConversationServiceImpl.java

@@ -0,0 +1,20 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.dto.BlockDto;
+import com.zhentao.entity.ChatConversation;
+import com.zhentao.repository.ChatConversationMapper;
+import com.zhentao.service.ChatConversationService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ChatConversationServiceImpl extends ServiceImpl<ChatConversationMapper,ChatConversation> implements ChatConversationService {
+
+    @Override
+    public void deleteChatConversation(BlockDto dto) {
+        QueryWrapper<ChatConversation> chatConversationQueryWrapper = new QueryWrapper<>();
+        chatConversationQueryWrapper.lambda().eq(ChatConversation::getUserId,dto.getUserId()).eq(ChatConversation::getTargetUserId,dto.getTargetUserId());
+        this.baseMapper.delete(chatConversationQueryWrapper);
+    }
+}

+ 81 - 0
service/websocket/src/main/java/com/zhentao/vo/ResultVo.java

@@ -0,0 +1,81 @@
+package com.zhentao.vo;
+
+
+import lombok.Data;
+
+/**
+ * 通用接口返回结果封装类
+ * @param <T> 数据泛型类型
+ */
+@Data
+public class ResultVo<T> {
+
+    private Integer code;
+
+    private String message;
+
+    private T data;
+
+    /** 私有构造方法,禁止外部直接实例化 */
+    private ResultVo() {}
+
+    /**
+     * 成功返回(无数据)
+     * @param message 提示消息
+     * @return 成功结果对象
+     */
+    public static ResultVo<Object> success(String message) {
+        ResultVo<Object> result = new ResultVo<>();
+        result.setCode(200);
+        result.setMessage(message);
+        result.setData(null);
+        return result;
+    }
+
+    /**
+     * 成功返回(带数据)
+     * @param message 提示消息
+     * @param data 返回数据
+     * @param <T> 数据类型
+     * @return 成功结果对象
+     */
+    public static <T> ResultVo<T> success(String message, T data) {
+        ResultVo<T> result = new ResultVo<>();
+        result.setCode(200);
+        result.setMessage(message);
+        result.setData(data);
+        return result;
+    }
+
+    /**
+     * 失败返回(自定义状态码和消息)
+     * @param code 状态码
+     * @param message 提示消息
+     * @return 失败结果对象
+     */
+    public static ResultVo<Object> fail(Integer code, String message) {
+        ResultVo<Object> result = new ResultVo<>();
+        result.setCode(code);
+        result.setMessage(message);
+        result.setData(null);
+        return result;
+    }
+
+    /**
+     * 失败返回(默认状态码500)
+     * @param message 提示消息
+     * @return 失败结果对象
+     */
+    public static ResultVo<Object> fail(String message) {
+        return fail(500, message);
+    }
+
+    /**
+     * 参数错误返回(默认状态码400)
+     * @param message 提示消息
+     * @return 参数错误结果对象
+     */
+    public static ResultVo<Object> paramError(String message) {
+        return fail(400, message);
+    }
+}