feature: 对接朋友圈评论、点赞功能
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
Target Server Version : 80030 (8.0.30)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 31/10/2025 19:00:36
|
||||
Date: 04/11/2025 19:45:03
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
@@ -37,12 +37,13 @@ CREATE TABLE `ai_api_key` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90154862069761 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI API 密钥表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91865840302593 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI API 密钥表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of ai_api_key
|
||||
-- ----------------------------
|
||||
INSERT INTO `ai_api_key` VALUES (90154862069760, 20901198351872, b'1', 'kimi', 'sk-HD0GKyxX3bYuycGuV5de202U418gczeAYcZVYZucwWrBEu2V', 'Moonshot', NULL, 0, NULL, '2025-10-30 18:43:15', NULL, '2025-10-30 11:26:16', b'0', 1);
|
||||
INSERT INTO `ai_api_key` VALUES (91865840302592, 10937855681024, b'0', 'Hula', 'sk-dmyowzmrlwjgmfogkaynoqpqfuaodyvcwmqbbxbugmauofci', 'Other', 'https://api.siliconflow.cn/v1/chat/completions', 0, NULL, '2025-11-04 12:02:04', NULL, '2025-11-04 12:02:04', b'0', 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for ai_chat_conversation
|
||||
@@ -68,14 +69,62 @@ CREATE TABLE `ai_chat_conversation` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NULL DEFAULT NULL COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90190392010241 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 聊天对话表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91963672443393 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 聊天对话表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of ai_chat_conversation
|
||||
-- ----------------------------
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90159593245184, 20901198351872, 90159576467968, 'Hula官方角色', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '我是Hula官方机器人,非常聪明', 0.8, 4096, 10, NULL, '2025-10-30 19:02:02', NULL, '2025-10-30 19:02:02', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90160268528128, 20901198351872, 90159576467968, 'Hula官方角色', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '我是Hula官方机器人,非常聪明', 0.8, 4096, 10, NULL, '2025-10-30 19:04:43', NULL, '2025-10-30 19:04:43', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90190392010240, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-10-30 21:04:26', NULL, '2025-10-30 21:04:26', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90190392010240, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-10-30 21:04:26', NULL, '2025-11-01 21:17:17', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90544185752064, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-10-31 20:30:16', NULL, '2025-11-01 07:54:13', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90639270623744, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 02:48:07', NULL, '2025-11-01 07:54:09', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90711748196864, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:36:06', NULL, '2025-11-01 07:38:22', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90711752391168, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:36:07', NULL, '2025-11-01 07:38:21', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90711756585472, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:36:08', NULL, '2025-11-01 07:38:20', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90711756585473, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:36:09', NULL, '2025-11-01 07:38:19', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90711760779776, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:36:10', NULL, '2025-11-01 07:38:18', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90711769168384, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:36:11', NULL, '2025-11-01 07:38:16', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90712415091200, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:38:45', NULL, '2025-11-01 07:38:47', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90712759024128, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:40:08', NULL, '2025-11-01 07:42:25', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90712763218432, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:40:08', NULL, '2025-11-01 07:42:24', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90712763218433, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:40:08', NULL, '2025-11-01 07:42:23', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90712767412736, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:40:09', NULL, '2025-11-01 07:42:22', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90712771607040, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:40:10', NULL, '2025-11-01 07:42:21', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90713224591872, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:41:59', NULL, '2025-11-01 07:42:20', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90714143144448, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:45:38', NULL, '2025-11-01 07:45:39', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90715393047040, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:50:35', NULL, '2025-11-01 07:50:38', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90716315793920, 11225442329600, 90190358455808, '小试牛刀', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 07:54:15', NULL, '2025-11-01 08:11:49', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90719184697856, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 08:05:40', NULL, '2025-11-01 08:11:47', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90719507659264, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 08:06:57', NULL, '2025-11-01 08:11:46', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90720778533376, 11225442329600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 08:12:00', NULL, '2025-11-01 08:12:00', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (90918409943552, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-01 21:17:19', NULL, '2025-11-02 10:56:01', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91009791244800, 10937855681024, 90190358455808, '新的聊天91009791244800', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-02 03:20:26', NULL, '2025-11-02 03:21:17', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91009925462528, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-02 03:20:58', NULL, '2025-11-02 03:21:03', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91085058030080, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-02 08:19:30', NULL, '2025-11-02 10:56:55', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91124627094016, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-02 10:56:45', NULL, '2025-11-03 17:10:25', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91124790671872, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-02 10:57:23', NULL, '2025-11-03 17:10:21', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91255690705408, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-02 19:37:32', NULL, '2025-11-03 17:10:17', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91278583216640, 91277941125632, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-02 21:08:31', NULL, '2025-11-02 21:08:31', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91278864235008, 91277941125632, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-02 21:09:37', NULL, '2025-11-02 21:09:37', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91392118831616, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-03 04:39:39', NULL, '2025-11-03 17:10:15', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91546326612480, 91542639456768, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-03 14:52:25', NULL, '2025-11-03 14:52:25', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91547207416320, 91539938324992, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-03 14:55:55', NULL, '2025-11-03 14:55:55', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91581109975552, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-03 17:10:38', NULL, '2025-11-04 13:10:59', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91582871583232, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-03 17:17:39', NULL, '2025-11-04 13:11:00', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91647820380672, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-03 21:35:44', NULL, '2025-11-04 13:11:01', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91866154875392, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 12:03:19', NULL, '2025-11-04 13:11:02', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91883204721152, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 13:11:04', NULL, '2025-11-04 14:58:33', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91883380881920, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 13:11:46', NULL, '2025-11-04 13:11:46', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91883619957248, 91880733913600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 13:12:42', NULL, '2025-11-04 13:12:42', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91883624151552, 91880733913600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 13:12:44', NULL, '2025-11-04 13:12:44', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91883645123072, 91880733913600, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 13:12:49', NULL, '2025-11-04 13:12:49', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91902066506240, 91901021762048, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 14:26:01', NULL, '2025-11-04 18:26:58', b'1', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91910266370560, 10937855681024, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 14:58:35', NULL, '2025-11-04 14:58:35', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91932869474816, 44100975922176, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 16:28:24', NULL, '2025-11-04 16:28:24', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91948023495168, 86483537072128, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 17:28:38', NULL, '2025-11-04 17:28:38', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91962976188928, 91901021762048, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 18:28:03', NULL, '2025-11-04 18:28:03', b'0', 1);
|
||||
INSERT INTO `ai_chat_conversation` VALUES (91963672443392, 91901021762048, 90190358455808, '测试HuLa', 90158209124864, 'moonshot-v1-128k', b'0', NULL, '你是一个vue专家', 0.8, 4096, 10, NULL, '2025-11-04 18:30:49', NULL, '2025-11-04 18:30:49', b'0', 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for ai_chat_message
|
||||
@@ -100,7 +149,7 @@ CREATE TABLE `ai_chat_message` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 1 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90190480090626 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 聊天消息表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91948065438210 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 聊天消息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of ai_chat_message
|
||||
@@ -131,13 +180,14 @@ CREATE TABLE `ai_chat_role` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90190358455809 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 聊天角色表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90544886200833 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 聊天角色表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of ai_chat_role
|
||||
-- ----------------------------
|
||||
INSERT INTO `ai_chat_role` VALUES (90159576467968, 20901198351872, 90158209124864, 'Hula官方角色', 'https://cdn.hulaspark.com/avatar/1046762075/43de7a13eda6b8c29622f5b4ca429156.webp', NULL, 0, '我是Hula官方机器人', '我是Hula官方机器人,非常聪明', NULL, NULL, b'0', 0, NULL, '2025-10-30 19:01:58', NULL, '2025-10-30 19:01:58', b'0', 1);
|
||||
INSERT INTO `ai_chat_role` VALUES (90190358455808, NULL, 90158209124864, '测试HuLa', 'https://cdn.hulaspark.com/avatar/2439646234/993c2cfc546fabc1ee127037102378d9.webp', '0', 0, '测试', '你是一个vue专家', NULL, NULL, b'1', 0, NULL, '2025-10-30 21:04:17', NULL, '2025-10-30 21:04:17', b'0', 1);
|
||||
INSERT INTO `ai_chat_role` VALUES (90544886200832, NULL, 90158209124864, '敲你母牛🐔', 'https://cdn.hulaspark.com/avatar/k2439646234/8421975045ef869aa5d841f7c4a050a4.webp', 'AI助手', 1, '精通编程语言', 'vue高手', NULL, NULL, b'1', 0, NULL, '2025-10-31 20:33:03', NULL, '2025-10-31 20:33:03', b'0', 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for ai_image
|
||||
@@ -348,12 +398,13 @@ CREATE TABLE `ai_model` (
|
||||
INDEX `idx_user_id`(`user_id` ASC) USING BTREE,
|
||||
INDEX `idx_public_status`(`public_status` ASC) USING BTREE,
|
||||
INDEX `idx_user_public`(`user_id` ASC, `public_status` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90158209124865 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 模型表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91866029046273 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 模型表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of ai_model
|
||||
-- ----------------------------
|
||||
INSERT INTO `ai_model` VALUES (90158209124864, 20901198351872, 90154862069760, 'Hula小模型', 'moonshot-v1-128k', 'Moonshot', 1, 0, 0, b'1', 0.8, 4096, 10, NULL, '2025-10-30 18:56:33', NULL, '2025-10-30 11:05:33', b'0', 1);
|
||||
INSERT INTO `ai_model` VALUES (91866029046272, 10937855681024, 91865840302592, '测试', '测试1', 'OpenAI', 1, 0, 0, b'0', 0.8, 4096, 10, NULL, '2025-11-04 12:02:48', NULL, '2025-11-04 12:02:48', b'0', 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for ai_music
|
||||
@@ -2232,7 +2283,7 @@ CREATE TABLE `def_user` (
|
||||
-- Records of def_user
|
||||
-- ----------------------------
|
||||
INSERT INTO `def_user` VALUES (61170828519936, 2, 'bot', 'HuLa小管家', '', '022', NULL, NULL, '', NULL, b'0', '', '', '1', b'1', '', '2025-08-11 11:11:03.139', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"120.231.232.41\", \"createIpDetail\": null, \"updateIpDetail\": null}', '2025-10-28 17:08:01', 2, NULL, 'a4d5c225e6709ba025272a31c7e90e0121d5e5ba16695afe0b61370bedb677d0', 'Dawn', '2025-07-07 15:27:02', 1, '2025-03-27 04:23:08', NULL, '2025-07-16 12:26:15', 0, 1);
|
||||
INSERT INTO `def_user` VALUES (61170828519937, 2, '2439646234', 'Dawn', '2439646234@qq.com', 'https://cdn.hulaspark.com/avatar/2439646234/6ec99d37b8ba1296c325d2d36b46a14d.webp', NULL, NULL, '', NULL, b'0', '', '', '1', b'1', '', '2025-08-11 11:11:03.189', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"183.15.179.234\", \"createIpDetail\": null, \"updateIpDetail\": {\"ip\": \"183.15.179.234\", \"isp\": \"电信\", \"area\": \"\", \"city\": \"深圳\", \"isp_id\": \"100017\", \"region\": \"广东\", \"city_id\": \"440300\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', NULL, 0, NULL, 'a4d5c225e6709ba025272a31c7e90e0121d5e5ba16695afe0b61370bedb677d0', 'Dawn', '2025-10-31 18:11:19', 1, '2025-03-27 04:23:08', NULL, '2025-10-31 18:36:18', 0, 1);
|
||||
INSERT INTO `def_user` VALUES (61170828519937, 2, '2439646234', 'Dawn', '2439646234@qq.com', 'https://cdn.hulaspark.com/avatar/2439646234/6ec99d37b8ba1296c325d2d36b46a14d.webp', NULL, NULL, '', NULL, b'0', '', '', '1', b'1', '', '2025-08-11 11:11:03.189', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"113.89.34.18\", \"createIpDetail\": null, \"updateIpDetail\": {\"ip\": \"113.89.34.18\", \"isp\": \"电信\", \"area\": \"\", \"city\": \"深圳\", \"isp_id\": \"100017\", \"region\": \"广东\", \"city_id\": \"440300\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', NULL, 0, NULL, 'a4d5c225e6709ba025272a31c7e90e0121d5e5ba16695afe0b61370bedb677d0', 'Dawn', '2025-11-04 19:34:05', 1, '2025-03-27 04:23:08', NULL, '2025-11-04 19:41:35', 0, 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for def_user_application
|
||||
@@ -2317,7 +2368,7 @@ CREATE TABLE `extend_interface_log` (
|
||||
-- Records of extend_interface_log
|
||||
-- ----------------------------
|
||||
INSERT INTO `extend_interface_log` VALUES (66567882983426, 244439130119864323, '阿里短信', 0, 1, '2025-08-26 16:37:01', '2025-08-26 16:37:00', NULL, '2025-08-26 16:37:00', NULL, 0, 0);
|
||||
INSERT INTO `extend_interface_log` VALUES (655249535051914248, 244881451621810192, '腾讯邮件', 624, 57, '2025-10-31 17:41:02', '2025-07-16 18:41:01', NULL, '2025-07-16 18:41:01', NULL, 0, 0);
|
||||
INSERT INTO `extend_interface_log` VALUES (655249535051914248, 244881451621810192, '腾讯邮件', 777, 57, '2025-11-04 19:11:50', '2025-07-16 18:41:01', NULL, '2025-07-16 18:41:01', NULL, 0, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for extend_interface_logging
|
||||
@@ -2531,7 +2582,7 @@ CREATE TABLE `worker_node` (
|
||||
`created` timestamp NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`is_del` tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 921 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'DB;WorkerID Assigner for UID Generator' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 931 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'DB;WorkerID Assigner for UID Generator' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of worker_node
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
Target Server Version : 80030 (8.0.30)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 31/10/2025 19:00:29
|
||||
Date: 04/11/2025 19:45:09
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
@@ -74,7 +74,7 @@ CREATE TABLE `im_announcements` (
|
||||
`is_del` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 89028498137601 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci COMMENT = '聊天公告表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91601993052673 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci COMMENT = '聊天公告表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_announcements
|
||||
@@ -151,7 +151,7 @@ CREATE TABLE `im_contact` (
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE,
|
||||
INDEX `idx_contact_room_uid_hide`(`room_id` ASC, `uid` ASC, `hide` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 69082079592702 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会话列表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 69082079594846 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会话列表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_contact
|
||||
@@ -183,8 +183,53 @@ CREATE TABLE `im_feed` (
|
||||
-- ----------------------------
|
||||
-- Records of im_feed
|
||||
-- ----------------------------
|
||||
INSERT INTO `im_feed` VALUES (90012511218176, 43329605667840, 'Kkk', 'open', 0, '2025-10-30 09:17:35', 1, 43329605667840, 0, 1, NULL);
|
||||
INSERT INTO `im_feed` VALUES (90191368924672, 10937855681024, '你好,测试一下朋友圈吧🤯', 'open', 0, '2025-10-30 21:08:18', 1, 10937855681024, 0, 1, NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for im_feed_comment
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `im_feed_comment`;
|
||||
CREATE TABLE `im_feed_comment` (
|
||||
`id` bigint NOT NULL,
|
||||
`feed_id` bigint NOT NULL COMMENT '朋友圈id',
|
||||
`uid` bigint NOT NULL COMMENT '评论人uid',
|
||||
`reply_comment_id` bigint NULL DEFAULT NULL COMMENT '回复的评论ID(如果是回复评论,则有值;如果是直接评论朋友圈,则为null)',
|
||||
`reply_uid` bigint NULL DEFAULT NULL COMMENT '被回复人的uid(如果是回复评论,则有值)',
|
||||
`content` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL COMMENT '评论内容',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 1,
|
||||
`create_by` bigint NOT NULL DEFAULT 0 COMMENT '创建者',
|
||||
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||
`update_by` bigint NOT NULL DEFAULT 0 COMMENT '更新者',
|
||||
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||||
`is_del` tinyint NOT NULL DEFAULT 0 COMMENT '逻辑删除',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_feed_id`(`feed_id` ASC) USING BTREE,
|
||||
INDEX `idx_uid`(`uid` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci COMMENT = '朋友圈评论表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_feed_comment
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for im_feed_like
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `im_feed_like`;
|
||||
CREATE TABLE `im_feed_like` (
|
||||
`id` bigint NOT NULL,
|
||||
`feed_id` bigint NOT NULL COMMENT '朋友圈id',
|
||||
`uid` bigint NOT NULL COMMENT '点赞人uid',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 1,
|
||||
`create_by` bigint NOT NULL DEFAULT 0 COMMENT '创建者',
|
||||
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_feed_uid`(`feed_id` ASC, `uid` ASC) USING BTREE,
|
||||
INDEX `idx_uid`(`uid` ASC) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci COMMENT = '朋友圈点赞表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_feed_like
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for im_feed_media
|
||||
@@ -279,7 +324,7 @@ CREATE TABLE `im_group_member` (
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE,
|
||||
INDEX `idx_group_member_uid_isdel_groupid`(`uid` ASC, `is_del` ASC, `group_id` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90516414902273 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '群成员表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91974099121154 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '群成员表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_group_member
|
||||
@@ -342,7 +387,7 @@ CREATE TABLE `im_message` (
|
||||
INDEX `idx_from_uid`(`from_uid` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90516414902274 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '消息表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91974099121160 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '消息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_message
|
||||
@@ -372,7 +417,7 @@ CREATE TABLE `im_message_mark` (
|
||||
INDEX `idx_uid`(`uid` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90188940422657 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '消息标记表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91948392231425 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '消息标记表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_message_mark
|
||||
@@ -404,7 +449,7 @@ CREATE TABLE `im_notice` (
|
||||
INDEX `idx_receiver_type`(`receiver_id` ASC, `event_type` ASC) USING BTREE,
|
||||
INDEX `idx_sender`(`sender_id` ASC) USING BTREE,
|
||||
INDEX `idx_related`(`apply_id` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90516389736451 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '统一通知表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91941953974787 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '统一通知表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_notice
|
||||
@@ -454,12 +499,12 @@ CREATE TABLE `im_room` (
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90505799118849 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '房间表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91974099121155 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '房间表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_room
|
||||
-- ----------------------------
|
||||
INSERT INTO `im_room` VALUES (1, 1, 1, '2025-10-31 17:41:26.004', 90501697089536, NULL, '2024-07-10 11:17:15.521', '2025-10-31 09:41:26.041', 1, 1, NULL, 0);
|
||||
INSERT INTO `im_room` VALUES (1, 1, 1, '2025-11-04 19:12:14.299', 91974099121159, NULL, '2024-07-10 11:17:15.521', '2025-11-04 11:12:14.333', 1, 1, NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for im_room_friend
|
||||
@@ -484,7 +529,7 @@ CREATE TABLE `im_room_friend` (
|
||||
INDEX `idx_room_id`(`room_id` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90505799118850 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '单聊房间表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91974099121156 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '单聊房间表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_room_friend
|
||||
@@ -513,7 +558,7 @@ CREATE TABLE `im_room_group` (
|
||||
INDEX `idx_room_id`(`room_id` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90207575715331 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '群聊房间表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91897532101123 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '群聊房间表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_room_group
|
||||
@@ -596,13 +641,13 @@ CREATE TABLE `im_user` (
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE,
|
||||
INDEX `idx_active_status_last_opt_time`(`last_opt_time` ASC) USING BTREE,
|
||||
INDEX `account_UNIQUE`(`account` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90501692895233 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91974099121153 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user
|
||||
-- ----------------------------
|
||||
INSERT INTO `im_user` VALUES (1, 61170828519936, 2, 'HuLa小管家', '022', '', 'bot', NULL, '', 0, '2025-07-07 15:27:01.711', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"120.231.232.41\", \"createIpDetail\": {\"ip\": \"206.237.119.215\", \"isp\": \"\", \"area\": \"\", \"city\": \"\", \"isp_id\": \"\", \"region\": \"\", \"city_id\": \"\", \"country\": \"美国\", \"region_id\": \"\", \"country_id\": \"US\"}, \"updateIpDetail\": {\"ip\": \"120.231.232.41\", \"isp\": \"移动\", \"area\": \"\", \"city\": \"\", \"isp_id\": \"100025\", \"region\": \"广东\", \"city_id\": \"\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', 6, 0, '', '2025-03-27 04:23:08.393', '2025-10-11 10:14:24.150', 'k.23772439646234', 0, NULL, 0, '2025-05-09 18:24:37.089', 99978, 0, 1);
|
||||
INSERT INTO `im_user` VALUES (10937855681024, 61170828519937, 3, 'Dawn', 'https://cdn.hulaspark.com/avatar/2439646234/97320189485dca88dcc7a70054445a56.webp', '2439646234@qq.com', '2439646234', NULL, '', 15, '2025-07-30 15:31:57.651', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"183.15.179.234\", \"createIpDetail\": null, \"updateIpDetail\": {\"ip\": \"183.15.179.234\", \"isp\": \"电信\", \"area\": \"\", \"city\": \"深圳\", \"isp_id\": \"100017\", \"region\": \"广东\", \"city_id\": \"440300\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', 6, 0, '', '2025-03-27 04:23:08.393', '2025-10-31 14:03:50.763', 'k.2439646234', 0, NULL, 0, '2025-09-20 21:35:31.415', 99978, 0, 1);
|
||||
INSERT INTO `im_user` VALUES (10937855681024, 61170828519937, 3, 'Dawn', 'https://cdn.hulaspark.com/avatar/2439646234/97320189485dca88dcc7a70054445a56.webp', '2439646234@qq.com', '2439646234', NULL, '', 15, '2025-07-30 15:31:57.651', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"113.89.34.18\", \"createIpDetail\": null, \"updateIpDetail\": {\"ip\": \"113.89.34.18\", \"isp\": \"电信\", \"area\": \"\", \"city\": \"深圳\", \"isp_id\": \"100017\", \"region\": \"广东\", \"city_id\": \"440300\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', 6, 0, '', '2025-03-27 04:23:08.393', '2025-11-04 12:54:01.895', 'k.2439646234', 0, NULL, 0, '2025-09-20 21:35:31.415', 99978, 0, 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for im_user_apply
|
||||
@@ -631,7 +676,7 @@ CREATE TABLE `im_user_apply` (
|
||||
INDEX `idx_target_id`(`target_id` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90516389736449 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户申请表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91941953974785 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户申请表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user_apply
|
||||
@@ -658,7 +703,7 @@ CREATE TABLE `im_user_backpack` (
|
||||
INDEX `idx_uid`(`uid` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90501697089540 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户背包表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91974099121163 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户背包表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user_backpack
|
||||
@@ -701,7 +746,7 @@ CREATE TABLE `im_user_emoji` (
|
||||
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `IDX_USER_EMOJIS_UID`(`uid` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90111324826625 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表情包' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91254163616257 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表情包' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user_emoji
|
||||
@@ -734,7 +779,7 @@ CREATE TABLE `im_user_friend` (
|
||||
INDEX `idx_uid_friend_uid`(`uid` ASC, `friend_uid` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90505799118852 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户联系人表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91974099121158 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户联系人表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user_friend
|
||||
@@ -912,7 +957,7 @@ CREATE TABLE `secure_invoke_record` (
|
||||
`is_del` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_next_retry_time`(`next_retry_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 90516414902275 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '本地消息表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91974099121162 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '本地消息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of secure_invoke_record
|
||||
@@ -931,7 +976,7 @@ CREATE TABLE `worker_node` (
|
||||
`modified` timestamp NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`created` timestamp NULL DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 217 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'DB;WorkerID Assigner for UID Generator' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 219 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'DB;WorkerID Assigner for UID Generator' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of worker_node
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.luohuo.flex.ai.controller.model;
|
||||
|
||||
import com.luohuo.flex.ai.common.pojo.PageResult;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeyBalanceRespVO;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeyPageReqVO;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeyRespVO;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeySaveReqVO;
|
||||
@@ -76,4 +77,11 @@ public class AiApiKeyController {
|
||||
return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName())));
|
||||
}
|
||||
|
||||
@GetMapping("/balance")
|
||||
@Operation(summary = "查询 API 密钥余额")
|
||||
@Parameter(name = "id", description = "API密钥编号", required = true, example = "1024")
|
||||
public R<AiApiKeyBalanceRespVO> getApiKeyBalance(@RequestParam("id") Long id) {
|
||||
return success(apiKeyService.getApiKeyBalance(id, ContextUtil.getUid()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.luohuo.flex.ai.controller.model.vo.apikey;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AI API 密钥余额查询响应 VO
|
||||
*
|
||||
* @author fansili
|
||||
*/
|
||||
@Schema(description = "管理后台 - AI API 密钥余额查询 Response VO")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AiApiKeyBalanceRespVO {
|
||||
|
||||
@Schema(description = "API Key ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "Moonshot")
|
||||
private String platform;
|
||||
|
||||
@Schema(description = "是否支持余额查询", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean supported;
|
||||
|
||||
@Schema(description = "是否查询成功", example = "true")
|
||||
private Boolean success;
|
||||
|
||||
@Schema(description = "错误信息", example = "API Key无效")
|
||||
private String errorMessage;
|
||||
|
||||
@Schema(description = "余额信息列表")
|
||||
private List<BalanceInfo> balanceInfos;
|
||||
|
||||
@Schema(description = "总余额(所有币种的总和,统一转换为人民币)", example = "100.50")
|
||||
private BigDecimal totalBalance;
|
||||
|
||||
/**
|
||||
* 余额信息
|
||||
*/
|
||||
@Schema(description = "余额信息")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class BalanceInfo {
|
||||
|
||||
@Schema(description = "币种", example = "CNY")
|
||||
private String currency;
|
||||
|
||||
@Schema(description = "总余额", example = "110.00")
|
||||
private BigDecimal totalBalance;
|
||||
|
||||
@Schema(description = "赠送余额", example = "10.00")
|
||||
private BigDecimal grantedBalance;
|
||||
|
||||
@Schema(description = "充值余额", example = "100.00")
|
||||
private BigDecimal toppedUpBalance;
|
||||
|
||||
@Schema(description = "是否可用", example = "true")
|
||||
private Boolean available;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.luohuo.flex.ai.service.model;
|
||||
|
||||
import com.luohuo.flex.ai.common.pojo.PageResult;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeyBalanceRespVO;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeyPageReqVO;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeySaveReqVO;
|
||||
import com.luohuo.flex.ai.dal.model.AiApiKeyDO;
|
||||
@@ -80,4 +81,13 @@ public interface AiApiKeyService {
|
||||
*/
|
||||
AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status);
|
||||
|
||||
/**
|
||||
* 查询 API 密钥余额
|
||||
*
|
||||
* @param id API 密钥编号
|
||||
* @param userId 用户编号
|
||||
* @return 余额信息
|
||||
*/
|
||||
AiApiKeyBalanceRespVO getApiKeyBalance(Long id, Long userId);
|
||||
|
||||
}
|
||||
@@ -1,17 +1,27 @@
|
||||
package com.luohuo.flex.ai.service.model;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.luohuo.flex.ai.common.pojo.PageResult;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeyBalanceRespVO;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeyPageReqVO;
|
||||
import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeySaveReqVO;
|
||||
import com.luohuo.flex.ai.dal.model.AiApiKeyDO;
|
||||
import com.luohuo.flex.ai.enums.AiPlatformEnum;
|
||||
import com.luohuo.flex.ai.enums.CommonStatusEnum;
|
||||
import com.luohuo.flex.ai.mapper.model.AiApiKeyMapper;
|
||||
import com.luohuo.flex.ai.utils.BeanUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.luohuo.flex.ai.enums.ErrorCodeConstants.API_KEY_DISABLE;
|
||||
@@ -23,6 +33,7 @@ import static com.luohuo.flex.ai.utils.ServiceExceptionUtil.exception;
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
public class AiApiKeyServiceImpl implements AiApiKeyService {
|
||||
@@ -135,4 +146,224 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiApiKeyBalanceRespVO getApiKeyBalance(Long id, Long userId) {
|
||||
// 1. 校验 API Key 是否存在
|
||||
AiApiKeyDO apiKey = validateApiKeyExists(id);
|
||||
|
||||
// 2. 权限校验
|
||||
if (Boolean.FALSE.equals(apiKey.getPublicStatus())) {
|
||||
// 私有密钥,只有创建者可以查询
|
||||
if (ObjectUtil.notEqual(apiKey.getUserId(), userId)) {
|
||||
throw exception(API_KEY_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 根据平台查询余额
|
||||
AiPlatformEnum platformEnum;
|
||||
try {
|
||||
platformEnum = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return AiApiKeyBalanceRespVO.builder()
|
||||
.id(id)
|
||||
.platform(apiKey.getPlatform())
|
||||
.supported(false)
|
||||
.success(false)
|
||||
.errorMessage("不支持的平台: " + apiKey.getPlatform())
|
||||
.build();
|
||||
}
|
||||
|
||||
return queryBalanceByPlatform(apiKey, platformEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据平台查询余额
|
||||
*/
|
||||
private AiApiKeyBalanceRespVO queryBalanceByPlatform(AiApiKeyDO apiKey, AiPlatformEnum platform) {
|
||||
try {
|
||||
switch (platform) {
|
||||
case MOONSHOT:
|
||||
return queryMoonshotBalance(apiKey);
|
||||
case DEEP_SEEK:
|
||||
return queryDeepSeekBalance(apiKey);
|
||||
case SILICON_FLOW:
|
||||
return querySiliconFlowBalance(apiKey);
|
||||
default:
|
||||
return AiApiKeyBalanceRespVO.builder()
|
||||
.id(apiKey.getId())
|
||||
.platform(apiKey.getPlatform())
|
||||
.supported(false)
|
||||
.success(false)
|
||||
.errorMessage("该平台暂不支持余额查询")
|
||||
.build();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("查询API Key余额失败, platform={}, error={}", platform.getPlatform(), e.getMessage(), e);
|
||||
return AiApiKeyBalanceRespVO.builder()
|
||||
.id(apiKey.getId())
|
||||
.platform(apiKey.getPlatform())
|
||||
.supported(true)
|
||||
.success(false)
|
||||
.errorMessage("查询失败: " + e.getMessage())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询月之暗面(Moonshot/Kimi)余额
|
||||
* API文档: https://platform.moonshot.cn/docs/api/balance
|
||||
*/
|
||||
private AiApiKeyBalanceRespVO queryMoonshotBalance(AiApiKeyDO apiKey) {
|
||||
String baseUrl = StrUtil.isNotBlank(apiKey.getUrl()) ? apiKey.getUrl() : "https://api.moonshot.cn";
|
||||
String url = baseUrl + "/v1/user/balance";
|
||||
|
||||
HttpResponse response = HttpRequest.get(url)
|
||||
.header("Authorization", "Bearer " + apiKey.getApiKey())
|
||||
.header("Content-Type", "application/json")
|
||||
.timeout(10000)
|
||||
.execute();
|
||||
|
||||
if (!response.isOk()) {
|
||||
throw new RuntimeException("HTTP请求失败: " + response.getStatus());
|
||||
}
|
||||
|
||||
JSONObject jsonResponse = JSONUtil.parseObj(response.body());
|
||||
|
||||
// 解析响应
|
||||
List<AiApiKeyBalanceRespVO.BalanceInfo> balanceInfos = new ArrayList<>();
|
||||
BigDecimal totalBalance = BigDecimal.ZERO;
|
||||
|
||||
if (jsonResponse.containsKey("data")) {
|
||||
JSONObject data = jsonResponse.getJSONObject("data");
|
||||
if (data.containsKey("available_balance")) {
|
||||
BigDecimal balance = data.getBigDecimal("available_balance");
|
||||
balanceInfos.add(AiApiKeyBalanceRespVO.BalanceInfo.builder()
|
||||
.currency("CNY")
|
||||
.totalBalance(balance)
|
||||
.available(true)
|
||||
.build());
|
||||
totalBalance = balance;
|
||||
}
|
||||
}
|
||||
|
||||
return AiApiKeyBalanceRespVO.builder()
|
||||
.id(apiKey.getId())
|
||||
.platform(apiKey.getPlatform())
|
||||
.supported(true)
|
||||
.success(true)
|
||||
.balanceInfos(balanceInfos)
|
||||
.totalBalance(totalBalance)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询DeepSeek余额
|
||||
* API文档: https://api-docs.deepseek.com/api/get-user-balance
|
||||
*/
|
||||
private AiApiKeyBalanceRespVO queryDeepSeekBalance(AiApiKeyDO apiKey) {
|
||||
String baseUrl = StrUtil.isNotBlank(apiKey.getUrl()) ? apiKey.getUrl() : "https://api.deepseek.com";
|
||||
String url = baseUrl + "/user/balance";
|
||||
|
||||
HttpResponse response = HttpRequest.get(url)
|
||||
.header("Authorization", "Bearer " + apiKey.getApiKey())
|
||||
.header("Accept", "application/json")
|
||||
.timeout(10000)
|
||||
.execute();
|
||||
|
||||
if (!response.isOk()) {
|
||||
throw new RuntimeException("HTTP请求失败: " + response.getStatus());
|
||||
}
|
||||
|
||||
JSONObject jsonResponse = JSONUtil.parseObj(response.body());
|
||||
|
||||
// 解析响应
|
||||
List<AiApiKeyBalanceRespVO.BalanceInfo> balanceInfos = new ArrayList<>();
|
||||
BigDecimal totalBalance = BigDecimal.ZERO;
|
||||
|
||||
if (jsonResponse.getBool("is_available", false) && jsonResponse.containsKey("balance_infos")) {
|
||||
for (Object item : jsonResponse.getJSONArray("balance_infos")) {
|
||||
JSONObject balanceInfo = (JSONObject) item;
|
||||
String currency = balanceInfo.getStr("currency");
|
||||
BigDecimal total = new BigDecimal(balanceInfo.getStr("total_balance"));
|
||||
BigDecimal granted = balanceInfo.containsKey("granted_balance")
|
||||
? new BigDecimal(balanceInfo.getStr("granted_balance")) : BigDecimal.ZERO;
|
||||
BigDecimal toppedUp = balanceInfo.containsKey("topped_up_balance")
|
||||
? new BigDecimal(balanceInfo.getStr("topped_up_balance")) : BigDecimal.ZERO;
|
||||
|
||||
balanceInfos.add(AiApiKeyBalanceRespVO.BalanceInfo.builder()
|
||||
.currency(currency)
|
||||
.totalBalance(total)
|
||||
.grantedBalance(granted)
|
||||
.toppedUpBalance(toppedUp)
|
||||
.available(true)
|
||||
.build());
|
||||
|
||||
// 简单累加(实际应该考虑汇率转换)
|
||||
totalBalance = totalBalance.add(total);
|
||||
}
|
||||
}
|
||||
|
||||
return AiApiKeyBalanceRespVO.builder()
|
||||
.id(apiKey.getId())
|
||||
.platform(apiKey.getPlatform())
|
||||
.supported(true)
|
||||
.success(true)
|
||||
.balanceInfos(balanceInfos)
|
||||
.totalBalance(totalBalance)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询硅基流动余额
|
||||
* API文档: https://docs.siliconflow.cn/cn/api-reference/userinfo/get-user-info
|
||||
*/
|
||||
private AiApiKeyBalanceRespVO querySiliconFlowBalance(AiApiKeyDO apiKey) {
|
||||
String baseUrl = StrUtil.isNotBlank(apiKey.getUrl()) ? apiKey.getUrl() : "https://api.siliconflow.cn";
|
||||
String url = baseUrl + "/v1/user/info";
|
||||
|
||||
HttpResponse response = HttpRequest.get(url)
|
||||
.header("Authorization", "Bearer " + apiKey.getApiKey())
|
||||
.header("Content-Type", "application/json")
|
||||
.timeout(10000)
|
||||
.execute();
|
||||
|
||||
if (!response.isOk()) {
|
||||
throw new RuntimeException("HTTP请求失败: " + response.getStatus());
|
||||
}
|
||||
|
||||
JSONObject jsonResponse = JSONUtil.parseObj(response.body());
|
||||
|
||||
// 解析响应
|
||||
List<AiApiKeyBalanceRespVO.BalanceInfo> balanceInfos = new ArrayList<>();
|
||||
BigDecimal totalBalance = BigDecimal.ZERO;
|
||||
|
||||
if (jsonResponse.getInt("code") == 20000 && jsonResponse.containsKey("data")) {
|
||||
JSONObject data = jsonResponse.getJSONObject("data");
|
||||
|
||||
// 硅基流动返回的余额字段
|
||||
BigDecimal balance = data.getBigDecimal("balance", BigDecimal.ZERO);
|
||||
BigDecimal chargeBalance = data.getBigDecimal("chargeBalance", BigDecimal.ZERO);
|
||||
BigDecimal total = data.getBigDecimal("totalBalance", BigDecimal.ZERO);
|
||||
|
||||
balanceInfos.add(AiApiKeyBalanceRespVO.BalanceInfo.builder()
|
||||
.currency("CNY")
|
||||
.totalBalance(total)
|
||||
.grantedBalance(balance.subtract(chargeBalance).max(BigDecimal.ZERO))
|
||||
.toppedUpBalance(chargeBalance)
|
||||
.available("normal".equals(data.getStr("status")))
|
||||
.build());
|
||||
|
||||
totalBalance = total;
|
||||
}
|
||||
|
||||
return AiApiKeyBalanceRespVO.builder()
|
||||
.id(apiKey.getId())
|
||||
.platform(apiKey.getPlatform())
|
||||
.supported(true)
|
||||
.success(true)
|
||||
.balanceInfos(balanceInfos)
|
||||
.totalBalance(totalBalance)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -489,7 +489,7 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
|
||||
// 4. 异步清理缓存
|
||||
CompletableFuture.runAsync(() -> {
|
||||
roomGroupCache.delete(roomGroup.getId());
|
||||
roomGroupCache.delete(roomGroup.getRoomId());
|
||||
roomGroupCache.evictGroup(roomGroup.getAccount());
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.luohuo.flex.im.core.user.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.luohuo.flex.im.common.utils.CursorUtils;
|
||||
import com.luohuo.flex.im.core.user.mapper.FeedCommentMapper;
|
||||
import com.luohuo.flex.im.domain.entity.FeedComment;
|
||||
import com.luohuo.flex.im.domain.vo.req.CursorPageBaseReq;
|
||||
import com.luohuo.flex.im.domain.vo.res.CursorPageBaseResp;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 朋友圈评论 Dao
|
||||
*/
|
||||
@Service
|
||||
public class FeedCommentDao extends ServiceImpl<FeedCommentMapper, FeedComment> {
|
||||
|
||||
/**
|
||||
* 获取朋友圈的评论数量
|
||||
*/
|
||||
public Integer getCommentCount(Long feedId) {
|
||||
return Math.toIntExact(lambdaQuery()
|
||||
.eq(FeedComment::getFeedId, feedId)
|
||||
.count());
|
||||
}
|
||||
|
||||
/**
|
||||
* 游标分页查询朋友圈评论(升序排列,最新评论在最后)
|
||||
*/
|
||||
public CursorPageBaseResp<FeedComment> getCommentPage(Long feedId, CursorPageBaseReq request) {
|
||||
// 使用 CursorUtils 进行游标分页(默认降序)
|
||||
CursorPageBaseResp<FeedComment> result = CursorUtils.getCursorPageByMysql(
|
||||
this,
|
||||
request,
|
||||
wrapper -> wrapper.eq(FeedComment::getFeedId, feedId),
|
||||
FeedComment::getCreateTime
|
||||
);
|
||||
|
||||
// 反向排序列表,使最新评论在最后
|
||||
Collections.reverse(result.getList());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某条朋友圈的所有评论(不分页,用于缓存)
|
||||
* 注意:MyBatis Plus 会自动过滤已删除的记录(通过 @TableLogic 注解)
|
||||
*/
|
||||
public List<FeedComment> getCommentListByFeedId(Long feedId) {
|
||||
return lambdaQuery()
|
||||
.eq(FeedComment::getFeedId, feedId)
|
||||
.orderByAsc(FeedComment::getCreateTime)
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除朋友圈的所有评论
|
||||
*/
|
||||
public boolean delByFeedId(Long feedId) {
|
||||
return remove(new LambdaQueryWrapper<FeedComment>()
|
||||
.eq(FeedComment::getFeedId, feedId));
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ public class FeedDao extends ServiceImpl<FeedMapper, Feed> {
|
||||
|
||||
/**
|
||||
* 查询朋友圈列表(包括指定用户列表的朋友圈)
|
||||
*
|
||||
* @param uidList 用户ID列表(包括当前用户和好友)
|
||||
* @param request 分页请求
|
||||
* @return 朋友圈分页结果
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.luohuo.flex.im.core.user.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.luohuo.flex.im.core.user.mapper.FeedLikeMapper;
|
||||
import com.luohuo.flex.im.domain.entity.FeedLike;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 朋友圈点赞 Dao
|
||||
*/
|
||||
@Service
|
||||
public class FeedLikeDao extends ServiceImpl<FeedLikeMapper, FeedLike> {
|
||||
|
||||
/**
|
||||
* 获取用户对某条朋友圈的点赞记录
|
||||
*/
|
||||
public FeedLike get(Long uid, Long feedId) {
|
||||
return lambdaQuery()
|
||||
.eq(FeedLike::getUid, uid)
|
||||
.eq(FeedLike::getFeedId, feedId)
|
||||
.one();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取朋友圈的点赞数量
|
||||
*/
|
||||
public Integer getLikeCount(Long feedId) {
|
||||
return Math.toIntExact(lambdaQuery()
|
||||
.eq(FeedLike::getFeedId, feedId)
|
||||
.count());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某条朋友圈的所有点赞用户ID列表
|
||||
*/
|
||||
public List<Long> getLikeUidList(Long feedId) {
|
||||
return lambdaQuery()
|
||||
.eq(FeedLike::getFeedId, feedId)
|
||||
.select(FeedLike::getUid)
|
||||
.list()
|
||||
.stream()
|
||||
.map(FeedLike::getUid)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除朋友圈的所有点赞
|
||||
*/
|
||||
public boolean delByFeedId(Long feedId) {
|
||||
return remove(new LambdaQueryWrapper<FeedLike>()
|
||||
.eq(FeedLike::getFeedId, feedId));
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,12 @@ public class FeedMediaDao extends ServiceImpl<FeedMediaMapper, FeedMedia> {
|
||||
|
||||
/**
|
||||
* 批量添加朋友圈的资源的数据
|
||||
*
|
||||
* @param feedId 朋友圈id
|
||||
* @param images 素材地址
|
||||
* @param type 0 纯文字 1 图片 2 视频
|
||||
* @param type 0 纯文字 1 图片 2 视频
|
||||
*/
|
||||
public List<FeedMedia> batchSaveMedia(Long feedId, List<String> images, Integer type){
|
||||
public List<FeedMedia> batchSaveMedia(Long feedId, List<String> images, Integer type) {
|
||||
List<FeedMedia> feedMediaList = new ArrayList<>();
|
||||
for (int i = 0; i < images.size(); i++) {
|
||||
String url = images.get(i);
|
||||
@@ -37,6 +38,7 @@ public class FeedMediaDao extends ServiceImpl<FeedMediaMapper, FeedMedia> {
|
||||
|
||||
/**
|
||||
* 删除朋友圈的资源等消息
|
||||
*
|
||||
* @param feedId
|
||||
* @return
|
||||
*/
|
||||
@@ -46,6 +48,7 @@ public class FeedMediaDao extends ServiceImpl<FeedMediaMapper, FeedMedia> {
|
||||
|
||||
/**
|
||||
* 通过id获取到朋友圈资源信息
|
||||
*
|
||||
* @param feedId
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,7 @@ public class FeedTargetDao extends ServiceImpl<FeedTargetMapper, FeedTarget> {
|
||||
|
||||
/**
|
||||
* 删除朋友圈和标签的管理
|
||||
*
|
||||
* @param feedId
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -17,9 +17,9 @@ import java.util.List;
|
||||
@Service
|
||||
public class ItemConfigDao extends ServiceImpl<ItemConfigMapper, ItemConfig> {
|
||||
|
||||
public List<ItemConfig> getByType(Integer type) {
|
||||
return lambdaQuery()
|
||||
.eq(ItemConfig::getType, type)
|
||||
.list();
|
||||
}
|
||||
public List<ItemConfig> getByType(Integer type) {
|
||||
return lambdaQuery()
|
||||
.eq(ItemConfig::getType, type)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ public class NoticeDao extends ServiceImpl<NoticeMapper, Notice> {
|
||||
|
||||
/**
|
||||
* 查询当前用户的通知
|
||||
* @param uid 登录用户
|
||||
*
|
||||
* @param uid 登录用户
|
||||
* @param onlyUnread 通知状态
|
||||
* @return
|
||||
*/
|
||||
@@ -53,7 +54,7 @@ public class NoticeDao extends ServiceImpl<NoticeMapper, Notice> {
|
||||
wsNotice.setUid(uid);
|
||||
|
||||
for (FriendUnreadDto friendUnreadDto : unReadCountByTypeMap) {
|
||||
if(friendUnreadDto.getType().equals(RoomTypeEnum.FRIEND.getType())){
|
||||
if (friendUnreadDto.getType().equals(RoomTypeEnum.FRIEND.getType())) {
|
||||
wsNotice.setUnReadCount4Friend(friendUnreadDto.getCount());
|
||||
} else {
|
||||
wsNotice.setUnReadCount4Group(friendUnreadDto.getCount());
|
||||
|
||||
@@ -25,44 +25,43 @@ import static com.luohuo.flex.im.domain.enums.NoticeStatusEnum.ACCEPTED;
|
||||
@Service
|
||||
public class UserApplyDao extends ServiceImpl<UserApplyMapper, UserApply> {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param uid uid
|
||||
* @param targetUid 目标 UID
|
||||
* @param initiator 方法调用方是否是申请记录发起方
|
||||
* @return {@link UserApply }
|
||||
*/
|
||||
public UserApply getFriendApproving(Long uid, Long targetUid, boolean initiator) {
|
||||
return lambdaQuery().eq(UserApply::getUid, uid)
|
||||
.eq(UserApply::getTargetId, targetUid)
|
||||
.eq(UserApply::getStatus, NoticeStatusEnum.UNTREATED.getStatus())
|
||||
.eq(UserApply::getType, RoomTypeEnum.FRIEND.getType())
|
||||
.notIn(initiator,UserApply::getDeleted, ApplyDeletedEnum.applyDeleted())
|
||||
.notIn(!initiator,UserApply::getDeleted, ApplyDeletedEnum.targetDeleted())
|
||||
.one();
|
||||
}
|
||||
/**
|
||||
* @param uid uid
|
||||
* @param targetUid 目标 UID
|
||||
* @param initiator 方法调用方是否是申请记录发起方
|
||||
* @return {@link UserApply }
|
||||
*/
|
||||
public UserApply getFriendApproving(Long uid, Long targetUid, boolean initiator) {
|
||||
return lambdaQuery().eq(UserApply::getUid, uid)
|
||||
.eq(UserApply::getTargetId, targetUid)
|
||||
.eq(UserApply::getStatus, NoticeStatusEnum.UNTREATED.getStatus())
|
||||
.eq(UserApply::getType, RoomTypeEnum.FRIEND.getType())
|
||||
.notIn(initiator, UserApply::getDeleted, ApplyDeletedEnum.applyDeleted())
|
||||
.notIn(!initiator, UserApply::getDeleted, ApplyDeletedEnum.targetDeleted())
|
||||
.one();
|
||||
}
|
||||
|
||||
public void agree(Long applyId) {
|
||||
lambdaUpdate()
|
||||
.eq(UserApply::getId, applyId)
|
||||
.set(UserApply::getStatus, ACCEPTED.getStatus())
|
||||
.set(UserApply::getUpdateTime, LocalDateTime.now())
|
||||
.update();
|
||||
}
|
||||
public void agree(Long applyId) {
|
||||
lambdaUpdate()
|
||||
.eq(UserApply::getId, applyId)
|
||||
.set(UserApply::getStatus, ACCEPTED.getStatus())
|
||||
.set(UserApply::getUpdateTime, LocalDateTime.now())
|
||||
.update();
|
||||
}
|
||||
|
||||
public void updateStatus(Long applyId, NoticeStatusEnum statusEnum) {
|
||||
lambdaUpdate().set(UserApply::getStatus, statusEnum.getStatus())
|
||||
.set(UserApply::getUpdateTime, LocalDateTime.now())
|
||||
.eq(UserApply::getId,applyId)
|
||||
.update();
|
||||
}
|
||||
public void updateStatus(Long applyId, NoticeStatusEnum statusEnum) {
|
||||
lambdaUpdate().set(UserApply::getStatus, statusEnum.getStatus())
|
||||
.set(UserApply::getUpdateTime, LocalDateTime.now())
|
||||
.eq(UserApply::getId, applyId)
|
||||
.update();
|
||||
}
|
||||
|
||||
public void deleteApprove(Long applyId, ApplyDeletedEnum deletedEnum) {
|
||||
lambdaUpdate().set(UserApply::getDeleted, deletedEnum.getCode())
|
||||
.set(UserApply::getUpdateTime, LocalDateTime.now())
|
||||
.eq(UserApply::getId, applyId)
|
||||
.update();
|
||||
}
|
||||
public void deleteApprove(Long applyId, ApplyDeletedEnum deletedEnum) {
|
||||
lambdaUpdate().set(UserApply::getDeleted, deletedEnum.getCode())
|
||||
.set(UserApply::getUpdateTime, LocalDateTime.now())
|
||||
.eq(UserApply::getId, applyId)
|
||||
.update();
|
||||
}
|
||||
|
||||
public List<Long> getExistingUsers(Long roomId, HashSet<Long> uidList) {
|
||||
return lambdaQuery()
|
||||
|
||||
@@ -19,57 +19,57 @@ import java.util.List;
|
||||
*/
|
||||
@Service
|
||||
public class UserBackpackDao extends ServiceImpl<UserBackpackMapper, UserBackpack> {
|
||||
private final ItemConfigDao itemConfigDao;
|
||||
private final ItemConfigDao itemConfigDao;
|
||||
|
||||
public UserBackpackDao(ItemConfigDao itemConfigDao) {
|
||||
this.itemConfigDao = itemConfigDao;
|
||||
}
|
||||
public UserBackpackDao(ItemConfigDao itemConfigDao) {
|
||||
this.itemConfigDao = itemConfigDao;
|
||||
}
|
||||
|
||||
public Integer getCountByValidItemId(Long uid, Long itemId) {
|
||||
return Math.toIntExact(lambdaQuery().eq(UserBackpack::getUid, uid)
|
||||
.eq(UserBackpack::getItemId, itemId)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.count());
|
||||
}
|
||||
public Integer getCountByValidItemId(Long uid, Long itemId) {
|
||||
return Math.toIntExact(lambdaQuery().eq(UserBackpack::getUid, uid)
|
||||
.eq(UserBackpack::getItemId, itemId)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.count());
|
||||
}
|
||||
|
||||
public UserBackpack getFirstValidItem(Long uid, Long itemId) {
|
||||
LambdaQueryWrapper<UserBackpack> wrapper = new QueryWrapper<UserBackpack>().lambda()
|
||||
.eq(UserBackpack::getUid, uid)
|
||||
.eq(UserBackpack::getItemId, itemId)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.last("limit 1");
|
||||
return getOne(wrapper);
|
||||
}
|
||||
public UserBackpack getFirstValidItem(Long uid, Long itemId) {
|
||||
LambdaQueryWrapper<UserBackpack> wrapper = new QueryWrapper<UserBackpack>().lambda()
|
||||
.eq(UserBackpack::getUid, uid)
|
||||
.eq(UserBackpack::getItemId, itemId)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.last("limit 1");
|
||||
return getOne(wrapper);
|
||||
}
|
||||
|
||||
public boolean invalidItem(Long id) {
|
||||
UserBackpack update = new UserBackpack();
|
||||
update.setId(id);
|
||||
update.setStatus(YesOrNoEnum.YES.getStatus());
|
||||
return updateById(update);
|
||||
}
|
||||
public boolean invalidItem(Long id) {
|
||||
UserBackpack update = new UserBackpack();
|
||||
update.setId(id);
|
||||
update.setStatus(YesOrNoEnum.YES.getStatus());
|
||||
return updateById(update);
|
||||
}
|
||||
|
||||
public List<UserBackpack> getByItemIds(Long uid, List<Long> itemIds) {
|
||||
return lambdaQuery().eq(UserBackpack::getUid, uid)
|
||||
.in(UserBackpack::getItemId, itemIds)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.list();
|
||||
}
|
||||
public List<UserBackpack> getByItemIds(Long uid, List<Long> itemIds) {
|
||||
return lambdaQuery().eq(UserBackpack::getUid, uid)
|
||||
.in(UserBackpack::getItemId, itemIds)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.list();
|
||||
}
|
||||
|
||||
public List<UserBackpack> getByItemIds(List<Long> uids, List<Long> itemIds) {
|
||||
return lambdaQuery().in(UserBackpack::getUid, uids)
|
||||
.in(UserBackpack::getItemId, itemIds)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.list();
|
||||
}
|
||||
public List<UserBackpack> getByItemIds(List<Long> uids, List<Long> itemIds) {
|
||||
return lambdaQuery().in(UserBackpack::getUid, uids)
|
||||
.in(UserBackpack::getItemId, itemIds)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.list();
|
||||
}
|
||||
|
||||
public UserBackpack getByIdp(String idempotent) {
|
||||
return lambdaQuery().eq(UserBackpack::getIdempotent, idempotent).one();
|
||||
}
|
||||
public UserBackpack getByIdp(String idempotent) {
|
||||
return lambdaQuery().eq(UserBackpack::getIdempotent, idempotent).one();
|
||||
}
|
||||
|
||||
public long countByUidAndItemId(Long uid, Long itemId) {
|
||||
return baseMapper.selectCount(new LambdaQueryWrapper<UserBackpack>()
|
||||
.eq(UserBackpack::getUid, uid)
|
||||
.eq(UserBackpack::getItemId, itemId)
|
||||
);
|
||||
}
|
||||
public long countByUidAndItemId(Long uid, Long itemId) {
|
||||
return baseMapper.selectCount(new LambdaQueryWrapper<UserBackpack>()
|
||||
.eq(UserBackpack::getUid, uid)
|
||||
.eq(UserBackpack::getItemId, itemId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,57 +22,57 @@ import java.util.Set;
|
||||
@Service
|
||||
public class UserDao extends ServiceImpl<UserMapper, User> {
|
||||
|
||||
public User getByOpenId(String openId) {
|
||||
LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda().eq(User::getOpenId, openId);
|
||||
return getOne(wrapper);
|
||||
}
|
||||
public User getByOpenId(String openId) {
|
||||
LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda().eq(User::getOpenId, openId);
|
||||
return getOne(wrapper);
|
||||
}
|
||||
|
||||
public void wearingBadge(Long uid, Long badgeId) {
|
||||
User update = new User();
|
||||
update.setId(uid);
|
||||
update.setItemId(badgeId);
|
||||
updateById(update);
|
||||
}
|
||||
public void wearingBadge(Long uid, Long badgeId) {
|
||||
User update = new User();
|
||||
update.setId(uid);
|
||||
update.setItemId(badgeId);
|
||||
updateById(update);
|
||||
}
|
||||
|
||||
public List<User> getByDefUserId(List<Long> defUserIdLIst) {
|
||||
return lambdaQuery().in(User::getUserId, defUserIdLIst).list();
|
||||
}
|
||||
|
||||
public User getByEmail(String email) {
|
||||
return lambdaQuery().eq(User::getEmail, email).one();
|
||||
}
|
||||
public User getByEmail(String email) {
|
||||
return lambdaQuery().eq(User::getEmail, email).one();
|
||||
}
|
||||
|
||||
public List<User> getMemberList() {
|
||||
return lambdaQuery()
|
||||
.eq(User::getState, NormalOrNoEnum.NORMAL.getStatus())
|
||||
//最近活跃的1000个人,可以用lastOptTime字段,但是该字段没索引,updateTime可平替
|
||||
.orderByDesc(User::getLastOptTime)
|
||||
//毕竟是大群聊,人数需要做个限制
|
||||
.last("limit 1000")
|
||||
.select(User::getId, User::getName, User::getAvatar, User::getAccount)
|
||||
.list();
|
||||
public List<User> getMemberList() {
|
||||
return lambdaQuery()
|
||||
.eq(User::getState, NormalOrNoEnum.NORMAL.getStatus())
|
||||
//最近活跃的1000个人,可以用lastOptTime字段,但是该字段没索引,updateTime可平替
|
||||
.orderByDesc(User::getLastOptTime)
|
||||
//毕竟是大群聊,人数需要做个限制
|
||||
.last("limit 1000")
|
||||
.select(User::getId, User::getName, User::getAvatar, User::getAccount)
|
||||
.list();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int changeUserState(Long uid, Long userStateId) {
|
||||
return baseMapper.changeUserState(uid, userStateId);
|
||||
}
|
||||
public int changeUserState(Long uid, Long userStateId) {
|
||||
return baseMapper.changeUserState(uid, userStateId);
|
||||
}
|
||||
|
||||
public List<ChatMemberListResp> getFriend(String key) {
|
||||
return baseMapper.getFriend("%" + key + "%");
|
||||
}
|
||||
public List<ChatMemberListResp> getFriend(String key) {
|
||||
return baseMapper.getFriend("%" + key + "%");
|
||||
}
|
||||
|
||||
public Boolean existsByEmailAndIdNot(Long uid, String email) {
|
||||
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
|
||||
.eq(User::getEmail, email);
|
||||
public Boolean existsByEmailAndIdNot(Long uid, String email) {
|
||||
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
|
||||
.eq(User::getEmail, email);
|
||||
|
||||
if(uid != null){
|
||||
wrapper.ne(User::getId, uid);
|
||||
}
|
||||
return baseMapper.selectCount(wrapper) > 0;
|
||||
}
|
||||
if (uid != null) {
|
||||
wrapper.ne(User::getId, uid);
|
||||
}
|
||||
return baseMapper.selectCount(wrapper) > 0;
|
||||
}
|
||||
|
||||
public List<User> getByIds(Set<Long> uidSet) {
|
||||
return baseMapper.selectBatchIds(uidSet);
|
||||
}
|
||||
public List<User> getByIds(Set<Long> uidSet) {
|
||||
return baseMapper.selectBatchIds(uidSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ import java.util.List;
|
||||
@Service
|
||||
public class UserEmojiDao extends ServiceImpl<UserEmojiMapper, UserEmoji> {
|
||||
|
||||
public List<UserEmoji> listByUid(Long uid) {
|
||||
return lambdaQuery().eq(UserEmoji::getUid, uid).list();
|
||||
}
|
||||
public List<UserEmoji> listByUid(Long uid) {
|
||||
return lambdaQuery().eq(UserEmoji::getUid, uid).list();
|
||||
}
|
||||
|
||||
public int countByUid(Long uid) {
|
||||
return Math.toIntExact(lambdaQuery().eq(UserEmoji::getUid, uid).count());
|
||||
}
|
||||
public int countByUid(Long uid) {
|
||||
return Math.toIntExact(lambdaQuery().eq(UserEmoji::getUid, uid).count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
|
||||
/**
|
||||
* 查询他不看我的好友
|
||||
*
|
||||
* @param uid 操作人
|
||||
* @return
|
||||
*/
|
||||
@@ -70,6 +71,7 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
|
||||
/**
|
||||
* 查询不让他看我的好友
|
||||
*
|
||||
* @param uid 操作人
|
||||
* @return
|
||||
*/
|
||||
@@ -85,6 +87,7 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
|
||||
/**
|
||||
* 查询我不看他的好友
|
||||
*
|
||||
* @param uid 操作人
|
||||
* @return
|
||||
*/
|
||||
@@ -100,6 +103,7 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
|
||||
/**
|
||||
* 查询仅聊天的好友
|
||||
*
|
||||
* @param uid 操作人
|
||||
* @return
|
||||
*/
|
||||
@@ -123,10 +127,11 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
/**
|
||||
* TODO 这里也要重构
|
||||
* 获取到当前登录人员的所有的好友的信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Cacheable(cacheNames = "luohuo:friend", key = "'list:'+#uid")
|
||||
public List<Long> getAllFriendIdsByUid(Long uid){
|
||||
public List<Long> getAllFriendIdsByUid(Long uid) {
|
||||
LambdaQueryWrapper<UserFriend> queryWrapper = Wrappers.lambdaQuery();
|
||||
queryWrapper.select(UserFriend::getFriendUid)
|
||||
.eq(UserFriend::getUid, uid);
|
||||
@@ -135,6 +140,7 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
|
||||
/**
|
||||
* 根据房间号+自己的id 定位好友关系
|
||||
*
|
||||
* @param roomId
|
||||
* @param uid
|
||||
* @return
|
||||
@@ -156,8 +162,9 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
|
||||
/**
|
||||
* 当好友关系变更时清除缓存
|
||||
*
|
||||
* @param roomId 房间ID
|
||||
* @param uid 用户ID
|
||||
* @param uid 用户ID
|
||||
*/
|
||||
@CacheEvict(cacheNames = "luohuo:userFriend", key = "'room:'+#roomId+':uid:'+#uid")
|
||||
public void evictFriendCache(Long roomId, Long uid) {
|
||||
@@ -165,6 +172,7 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
|
||||
/**
|
||||
* 查询所有好友房间
|
||||
*
|
||||
* @param roomIdList 房间id
|
||||
*/
|
||||
public List<UserFriend> getAllRoomInfo(List<Long> roomIdList) {
|
||||
|
||||
@@ -9,38 +9,38 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class UserPrivacyDao extends ServiceImpl<UserPrivacyMapper, UserPrivacy> {
|
||||
|
||||
/**
|
||||
* 获取用户隐私设置
|
||||
*/
|
||||
public UserPrivacy getByUid(Long uid) {
|
||||
return lambdaQuery().eq(UserPrivacy::getUid, uid).one();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化用户隐私设置
|
||||
*/
|
||||
@Transactional
|
||||
public boolean initUserPrivacy(Long uid) {
|
||||
UserPrivacy privacy = new UserPrivacy();
|
||||
privacy.setUid(uid);
|
||||
privacy.setIsPrivate(false);
|
||||
privacy.setAllowTempSession(true);
|
||||
privacy.setSearchableByPhone(true);
|
||||
privacy.setSearchableByAccount(true);
|
||||
privacy.setSearchableByUsername(true);
|
||||
privacy.setShowOnlineStatus(true);
|
||||
privacy.setAllowAddFriend(true);
|
||||
privacy.setAllowGroupInvite(true);
|
||||
privacy.setHideProfile(false);
|
||||
return save(privacy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新隐私设置
|
||||
*/
|
||||
@Transactional
|
||||
public boolean updatePrivacy(Long uid, PrivacySettingReq req) {
|
||||
|
||||
/**
|
||||
* 获取用户隐私设置
|
||||
*/
|
||||
public UserPrivacy getByUid(Long uid) {
|
||||
return lambdaQuery().eq(UserPrivacy::getUid, uid).one();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化用户隐私设置
|
||||
*/
|
||||
@Transactional
|
||||
public boolean initUserPrivacy(Long uid) {
|
||||
UserPrivacy privacy = new UserPrivacy();
|
||||
privacy.setUid(uid);
|
||||
privacy.setIsPrivate(false);
|
||||
privacy.setAllowTempSession(true);
|
||||
privacy.setSearchableByPhone(true);
|
||||
privacy.setSearchableByAccount(true);
|
||||
privacy.setSearchableByUsername(true);
|
||||
privacy.setShowOnlineStatus(true);
|
||||
privacy.setAllowAddFriend(true);
|
||||
privacy.setAllowGroupInvite(true);
|
||||
privacy.setHideProfile(false);
|
||||
return save(privacy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新隐私设置
|
||||
*/
|
||||
@Transactional
|
||||
public boolean updatePrivacy(Long uid, PrivacySettingReq req) {
|
||||
return lambdaUpdate()
|
||||
.eq(UserPrivacy::getUid, uid)
|
||||
.set(req.getIsPrivate() != null, UserPrivacy::getIsPrivate, req.getIsPrivate())
|
||||
@@ -53,7 +53,7 @@ public class UserPrivacyDao extends ServiceImpl<UserPrivacyMapper, UserPrivacy>
|
||||
.set(req.getAllowGroupInvite() != null, UserPrivacy::getAllowGroupInvite, req.getAllowGroupInvite())
|
||||
.set(req.getHideProfile() != null, UserPrivacy::getHideProfile, req.getHideProfile())
|
||||
.update();
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean checkAllowTempSession(Long uid) {
|
||||
return baseMapper.checkAllowTempSession(uid);
|
||||
|
||||
@@ -18,8 +18,8 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class UserRoleDao extends ServiceImpl<UserRoleMapper, UserRole> {
|
||||
|
||||
public Set<Long> listByUid(Long uid) {
|
||||
return lambdaQuery()
|
||||
.eq(UserRole::getUid, uid).select(UserRole::getUid).list().stream().map(UserRole::getUid).collect(Collectors.toSet());
|
||||
}
|
||||
public Set<Long> listByUid(Long uid) {
|
||||
return lambdaQuery()
|
||||
.eq(UserRole::getUid, uid).select(UserRole::getUid).list().stream().map(UserRole::getUid).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.List;
|
||||
public class UserStateDao extends ServiceImpl<UserStateMapper, UserState> {
|
||||
|
||||
@Cacheable(cacheNames = "luohuo:user", key = "'state'")
|
||||
public List<UserState> list(){
|
||||
public List<UserState> list() {
|
||||
return lambdaQuery()
|
||||
.list();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.luohuo.flex.im.core.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.luohuo.flex.im.domain.entity.FeedComment;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 朋友圈评论 Mapper 接口
|
||||
*/
|
||||
@Repository
|
||||
public interface FeedCommentMapper extends BaseMapper<FeedComment> {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.luohuo.flex.im.core.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.luohuo.flex.im.domain.entity.FeedLike;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 朋友圈点赞 Mapper 接口
|
||||
*/
|
||||
@Repository
|
||||
public interface FeedLikeMapper extends BaseMapper<FeedLike> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.luohuo.flex.im.core.user.service;
|
||||
|
||||
import com.luohuo.flex.im.domain.vo.req.CursorPageBaseReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedCommentReq;
|
||||
import com.luohuo.flex.im.domain.vo.res.CursorPageBaseResp;
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedCommentVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 朋友圈评论服务
|
||||
*/
|
||||
public interface FeedCommentService {
|
||||
|
||||
/**
|
||||
* 发表评论
|
||||
* @param uid 操作人
|
||||
* @param req 评论请求
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean addComment(Long uid, FeedCommentReq req);
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
* @param uid 操作人
|
||||
* @param commentId 评论ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean delComment(Long uid, Long commentId);
|
||||
|
||||
/**
|
||||
* 分页查询朋友圈评论
|
||||
* @param feedId 朋友圈ID
|
||||
* @param request 分页请求
|
||||
* @return 评论分页列表
|
||||
*/
|
||||
CursorPageBaseResp<FeedCommentVo> getCommentPage(Long feedId, CursorPageBaseReq request);
|
||||
|
||||
/**
|
||||
* 获取朋友圈的评论列表(不分页,用于展示前几条)
|
||||
* @param feedId 朋友圈ID
|
||||
* @param limit 限制数量
|
||||
* @return 评论列表
|
||||
*/
|
||||
List<FeedCommentVo> getCommentList(Long feedId, Integer limit);
|
||||
|
||||
/**
|
||||
* 获取朋友圈的评论数量
|
||||
* @param feedId 朋友圈ID
|
||||
* @return 评论数量
|
||||
*/
|
||||
Integer getCommentCount(Long feedId);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.luohuo.flex.im.core.user.service;
|
||||
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedLikeVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 朋友圈点赞服务
|
||||
*/
|
||||
public interface FeedLikeService {
|
||||
|
||||
/**
|
||||
* 点赞或取消点赞
|
||||
* @param uid 操作人
|
||||
* @param feedId 朋友圈ID
|
||||
* @param actType 操作类型 1-点赞 2-取消点赞
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean setLike(Long uid, Long feedId, Integer actType);
|
||||
|
||||
/**
|
||||
* 获取朋友圈的点赞列表
|
||||
* @param feedId 朋友圈ID
|
||||
* @return 点赞用户列表
|
||||
*/
|
||||
List<FeedLikeVo> getLikeList(Long feedId);
|
||||
|
||||
/**
|
||||
* 获取朋友圈的点赞数量
|
||||
* @param feedId 朋友圈ID
|
||||
* @return 点赞数量
|
||||
*/
|
||||
Integer getLikeCount(Long feedId);
|
||||
|
||||
/**
|
||||
* 判断用户是否已点赞
|
||||
* @param uid 用户ID
|
||||
* @param feedId 朋友圈ID
|
||||
* @return 是否已点赞
|
||||
*/
|
||||
Boolean hasLiked(Long uid, Long feedId);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.luohuo.flex.im.core.user.service;
|
||||
|
||||
import com.luohuo.flex.im.domain.entity.FeedComment;
|
||||
import com.luohuo.flex.im.domain.entity.FeedLike;
|
||||
|
||||
/**
|
||||
* 朋友圈通知服务
|
||||
*/
|
||||
public interface FeedNotifyService {
|
||||
|
||||
/**
|
||||
* 发送点赞通知
|
||||
* 通知逻辑:
|
||||
* 1. 获取朋友圈发布人的所有好友
|
||||
* 2. 过滤出与该朋友圈有互动关系的好友(已点赞或已评论)
|
||||
* 3. 发送 WebSocket 通知给这些好友
|
||||
*
|
||||
* @param feedLike 点赞记录
|
||||
*/
|
||||
void notifyFeedLike(FeedLike feedLike);
|
||||
|
||||
/**
|
||||
* 发送评论通知
|
||||
* 通知逻辑:
|
||||
* 1. 获取朋友圈发布人的所有好友
|
||||
* 2. 过滤出与该朋友圈有互动关系的好友(已点赞或已评论)
|
||||
* 3. 发送 WebSocket 通知给这些好友
|
||||
*
|
||||
* @param feedComment 评论记录
|
||||
*/
|
||||
void notifyFeedComment(FeedComment feedComment);
|
||||
|
||||
/**
|
||||
* 取消点赞通知
|
||||
*
|
||||
* @param feedId 朋友圈ID
|
||||
* @param uid 点赞人UID
|
||||
*/
|
||||
void notifyFeedUnlike(Long feedId, Long uid);
|
||||
}
|
||||
@@ -39,10 +39,11 @@ public interface FeedService {
|
||||
|
||||
/**
|
||||
* 查看朋友圈
|
||||
* @param feedId
|
||||
* @param feedId 朋友圈ID
|
||||
* @param uid 当前用户ID
|
||||
* @return
|
||||
*/
|
||||
FeedVo feedDetail(Long feedId);
|
||||
FeedVo feedDetail(Long feedId, Long uid);
|
||||
|
||||
/**
|
||||
* 获取朋友圈的可见权限
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
package com.luohuo.flex.im.core.user.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.luohuo.basic.cache.redis2.CacheResult;
|
||||
import com.luohuo.basic.cache.repository.CachePlusOps;
|
||||
import com.luohuo.basic.exception.BizException;
|
||||
import com.luohuo.basic.model.cache.CacheHashKey;
|
||||
import com.luohuo.flex.common.cache.common.FeedCommentCacheKeyBuilder;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedCommentDao;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedDao;
|
||||
import com.luohuo.flex.im.core.user.service.FeedCommentService;
|
||||
import com.luohuo.flex.im.core.user.service.FeedNotifyService;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import com.luohuo.flex.im.domain.entity.Feed;
|
||||
import com.luohuo.flex.im.domain.entity.FeedComment;
|
||||
import com.luohuo.flex.im.domain.vo.req.CursorPageBaseReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedCommentReq;
|
||||
import com.luohuo.flex.im.domain.vo.res.CursorPageBaseResp;
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedCommentVo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 朋友圈评论服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FeedCommentServiceImpl implements FeedCommentService {
|
||||
|
||||
private final FeedCommentDao feedCommentDao;
|
||||
private final FeedDao feedDao;
|
||||
private final CachePlusOps cachePlusOps;
|
||||
private final UserSummaryCache userSummaryCache;
|
||||
private final FeedNotifyService feedNotifyService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Boolean addComment(Long uid, FeedCommentReq req) {
|
||||
// 1. 校验朋友圈是否存在
|
||||
Feed feed = feedDao.getById(req.getFeedId());
|
||||
if (Objects.isNull(feed)) {
|
||||
throw new BizException("朋友圈不存在");
|
||||
}
|
||||
|
||||
// 2. 如果是回复评论,校验被回复的评论是否存在
|
||||
if (Objects.nonNull(req.getReplyCommentId())) {
|
||||
FeedComment replyComment = feedCommentDao.getById(req.getReplyCommentId());
|
||||
if (Objects.isNull(replyComment)) {
|
||||
throw new BizException("被回复的评论不存在");
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 创建评论
|
||||
FeedComment feedComment = FeedComment.builder()
|
||||
.feedId(req.getFeedId())
|
||||
.uid(uid)
|
||||
.replyCommentId(req.getReplyCommentId())
|
||||
.replyUid(req.getReplyUid())
|
||||
.content(req.getContent())
|
||||
.build();
|
||||
feedCommentDao.save(feedComment);
|
||||
|
||||
// 4. 发送评论通知
|
||||
feedNotifyService.notifyFeedComment(feedComment);
|
||||
|
||||
// 5. 清除缓存
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
cachePlusOps.del(FeedCommentCacheKeyBuilder.build(req.getFeedId()));
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Boolean delComment(Long uid, Long commentId) {
|
||||
// 1. 查询评论是否存在
|
||||
FeedComment comment = feedCommentDao.getById(commentId);
|
||||
if (Objects.isNull(comment)) {
|
||||
throw new BizException("评论不存在");
|
||||
}
|
||||
|
||||
// 2. 校验权限
|
||||
if (!comment.getUid().equals(uid)) {
|
||||
throw new BizException("无权删除该评论");
|
||||
}
|
||||
|
||||
// 3. 逻辑删除评论
|
||||
feedCommentDao.removeById(commentId);
|
||||
|
||||
// 4. 清除缓存(在事务提交后执行,确保数据库已更新)
|
||||
Long feedId = comment.getFeedId();
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
cachePlusOps.del(FeedCommentCacheKeyBuilder.build(feedId));
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorPageBaseResp<FeedCommentVo> getCommentPage(Long feedId, CursorPageBaseReq request) {
|
||||
// 1. 分页查询评论
|
||||
CursorPageBaseResp<FeedComment> commentPage = feedCommentDao.getCommentPage(feedId, request);
|
||||
|
||||
// 2. 组装用户信息
|
||||
List<FeedCommentVo> commentVoList = buildCommentVoList(commentPage.getList());
|
||||
|
||||
// 3. 返回结果
|
||||
CursorPageBaseResp<FeedCommentVo> result = new CursorPageBaseResp<>();
|
||||
result.setList(commentVoList);
|
||||
result.setCursor(commentPage.getCursor());
|
||||
result.setIsLast(commentPage.getIsLast());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FeedCommentVo> getCommentList(Long feedId, Integer limit) {
|
||||
// 1. 从缓存获取评论列表
|
||||
CacheHashKey hashKey = FeedCommentCacheKeyBuilder.build(feedId);
|
||||
CacheResult<List<FeedComment>> result = cachePlusOps.get(hashKey, t -> feedCommentDao.getCommentListByFeedId(feedId));
|
||||
List<FeedComment> commentList = result.getValue();
|
||||
|
||||
if (CollUtil.isEmpty(commentList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 2. 限制数量
|
||||
if (Objects.nonNull(limit) && commentList.size() > limit) {
|
||||
commentList = commentList.subList(0, limit);
|
||||
}
|
||||
|
||||
// 3. 组装用户信息
|
||||
return buildCommentVoList(commentList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCommentCount(Long feedId) {
|
||||
return feedCommentDao.getCommentCount(feedId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装评论VO列表(包含用户信息)
|
||||
*/
|
||||
private List<FeedCommentVo> buildCommentVoList(List<FeedComment> commentList) {
|
||||
if (CollUtil.isEmpty(commentList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 1. 收集所有需要查询的用户ID
|
||||
Set<Long> uidSet = new HashSet<>();
|
||||
commentList.forEach(comment -> {
|
||||
uidSet.add(comment.getUid());
|
||||
if (Objects.nonNull(comment.getReplyUid())) {
|
||||
uidSet.add(comment.getReplyUid());
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 批量获取用户信息
|
||||
Map<Long, SummeryInfoDTO> userInfoMap = userSummaryCache.getBatch(new ArrayList<>(uidSet));
|
||||
|
||||
// 3. 组装返回结果
|
||||
return commentList.stream()
|
||||
.map(comment -> {
|
||||
FeedCommentVo vo = new FeedCommentVo();
|
||||
BeanUtil.copyProperties(comment, vo);
|
||||
|
||||
// 设置评论人信息
|
||||
SummeryInfoDTO userInfo = userInfoMap.get(comment.getUid());
|
||||
if (Objects.nonNull(userInfo)) {
|
||||
vo.setUserName(userInfo.getName());
|
||||
vo.setUserAvatar(userInfo.getAvatar());
|
||||
}
|
||||
|
||||
// 设置被回复人信息
|
||||
if (Objects.nonNull(comment.getReplyUid())) {
|
||||
SummeryInfoDTO replyUserInfo = userInfoMap.get(comment.getReplyUid());
|
||||
if (Objects.nonNull(replyUserInfo)) {
|
||||
vo.setReplyUserName(replyUserInfo.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return vo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.luohuo.flex.im.core.user.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.luohuo.basic.cache.redis2.CacheResult;
|
||||
import com.luohuo.basic.cache.repository.CachePlusOps;
|
||||
import com.luohuo.basic.exception.BizException;
|
||||
import com.luohuo.basic.model.cache.CacheHashKey;
|
||||
import com.luohuo.flex.common.cache.common.FeedLikeCacheKeyBuilder;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedDao;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedLikeDao;
|
||||
import com.luohuo.flex.im.core.user.service.FeedLikeService;
|
||||
import com.luohuo.flex.im.core.user.service.FeedNotifyService;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import com.luohuo.flex.im.domain.entity.Feed;
|
||||
import com.luohuo.flex.im.domain.entity.FeedLike;
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedLikeVo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 朋友圈点赞服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FeedLikeServiceImpl implements FeedLikeService {
|
||||
|
||||
private final FeedLikeDao feedLikeDao;
|
||||
private final FeedDao feedDao;
|
||||
private final CachePlusOps cachePlusOps;
|
||||
private final UserSummaryCache userSummaryCache;
|
||||
private final FeedNotifyService feedNotifyService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Boolean setLike(Long uid, Long feedId, Integer actType) {
|
||||
// 1. 校验朋友圈是否存在
|
||||
Feed feed = feedDao.getById(feedId);
|
||||
if (Objects.isNull(feed)) {
|
||||
throw new BizException("朋友圈不存在");
|
||||
}
|
||||
|
||||
// 2. 查询是否已经点赞过
|
||||
FeedLike oldLike = feedLikeDao.get(uid, feedId);
|
||||
|
||||
// 3. 根据操作类型处理
|
||||
if (actType == 1) {
|
||||
// 点赞
|
||||
if (Objects.isNull(oldLike)) {
|
||||
// 新增点赞记录
|
||||
FeedLike feedLike = FeedLike.builder()
|
||||
.feedId(feedId)
|
||||
.uid(uid)
|
||||
.build();
|
||||
feedLikeDao.save(feedLike);
|
||||
|
||||
// 发送点赞通知
|
||||
feedNotifyService.notifyFeedLike(feedLike);
|
||||
}
|
||||
// 如果已经点赞过,则不做任何操作(幂等性)
|
||||
} else if (actType == 2) {
|
||||
// 取消点赞
|
||||
if (Objects.isNull(oldLike)) {
|
||||
// 没有点赞记录,直接返回
|
||||
return true;
|
||||
}
|
||||
feedLikeDao.removeById(oldLike.getId());
|
||||
|
||||
// 发送取消点赞通知
|
||||
feedNotifyService.notifyFeedUnlike(feedId, uid);
|
||||
} else {
|
||||
throw new BizException("操作类型错误");
|
||||
}
|
||||
|
||||
// 4. 清除缓存(在事务提交后执行,确保数据库已更新)
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
cachePlusOps.del(FeedLikeCacheKeyBuilder.build(feedId));
|
||||
log.info("✅ 点赞缓存已清除,朋友圈ID: {}", feedId);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FeedLikeVo> getLikeList(Long feedId) {
|
||||
// 1. 从缓存获取点赞用户ID列表
|
||||
CacheHashKey hashKey = FeedLikeCacheKeyBuilder.build(feedId);
|
||||
CacheResult<List<Long>> result = cachePlusOps.get(hashKey, t -> feedLikeDao.getLikeUidList(feedId));
|
||||
List<Long> likeUidList = result.getValue();
|
||||
|
||||
if (CollUtil.isEmpty(likeUidList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 2. 批量获取用户信息
|
||||
Map<Long, SummeryInfoDTO> userInfoMap = userSummaryCache.getBatch(likeUidList);
|
||||
|
||||
// 3. 组装返回结果
|
||||
return likeUidList.stream()
|
||||
.map(uid -> {
|
||||
SummeryInfoDTO userInfo = userInfoMap.get(uid);
|
||||
if (Objects.isNull(userInfo)) {
|
||||
return null;
|
||||
}
|
||||
return FeedLikeVo.builder()
|
||||
.uid(uid)
|
||||
.userName(userInfo.getName())
|
||||
.userAvatar(userInfo.getAvatar())
|
||||
.build();
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getLikeCount(Long feedId) {
|
||||
return feedLikeDao.getLikeCount(feedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean hasLiked(Long uid, Long feedId) {
|
||||
FeedLike feedLike = feedLikeDao.get(uid, feedId);
|
||||
return Objects.nonNull(feedLike);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.luohuo.flex.im.core.user.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedCommentDao;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedDao;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedLikeDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserFriendDao;
|
||||
import com.luohuo.flex.im.core.user.service.FeedNotifyService;
|
||||
import com.luohuo.flex.im.domain.entity.Feed;
|
||||
import com.luohuo.flex.im.domain.entity.FeedComment;
|
||||
import com.luohuo.flex.im.domain.entity.FeedLike;
|
||||
import com.luohuo.flex.model.entity.WSRespTypeEnum;
|
||||
import com.luohuo.flex.model.entity.WsBaseResp;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 朋友圈通知服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FeedNotifyServiceImpl implements FeedNotifyService {
|
||||
|
||||
private final FeedLikeDao feedLikeDao;
|
||||
private final FeedCommentDao feedCommentDao;
|
||||
private final FeedDao feedDao;
|
||||
private final UserFriendDao userFriendDao;
|
||||
private final PushService pushService;
|
||||
|
||||
@Override
|
||||
public void notifyFeedLike(FeedLike feedLike) {
|
||||
try {
|
||||
// 1. 获取跟朋友圈有关系的好友
|
||||
Set<Long> interactiveUidSet = getInteractiveUid(feedLike.getFeedId(), feedLike.getUid());
|
||||
if (interactiveUidSet == null) return;
|
||||
|
||||
// 2. 构建通知消息 - 点赞:uid、feedId(无 comment 字段)
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("uid", feedLike.getUid());
|
||||
data.put("feedId", feedLike.getFeedId());
|
||||
|
||||
sendFeedNotify(data, interactiveUidSet, feedLike.getUid(), "点赞");
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 发送点赞通知失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<Long> getInteractiveUid(Long feedId, Long operatorUid) {
|
||||
// 1. 获取朋友圈信息
|
||||
Feed feed = feedDao.getById(feedId);
|
||||
if (feed == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 获取朋友圈发布人的所有好友
|
||||
List<Long> friendUidList = userFriendDao.getAllFriendIdsByUid(feed.getUid());
|
||||
if (CollUtil.isEmpty(friendUidList)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. 获取与该朋友圈有互动关系的好友(已点赞或已评论)
|
||||
Set<Long> interactiveUidSet = getInteractiveUidSet(feedId, friendUidList);
|
||||
|
||||
// 4. 添加发朋友圈的人
|
||||
interactiveUidSet.add(feed.getUid());
|
||||
|
||||
// 5. 添加操作人自己(这样操作人可以在其他设备上实时看到自己的操作)
|
||||
interactiveUidSet.add(operatorUid);
|
||||
|
||||
if (CollUtil.isEmpty(interactiveUidSet)) {
|
||||
return null;
|
||||
}
|
||||
return interactiveUidSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyFeedComment(FeedComment feedComment) {
|
||||
try {
|
||||
// 1. 获取朋友圈信息
|
||||
Set<Long> interactiveUidSet = getInteractiveUid(feedComment.getFeedId(), feedComment.getUid());
|
||||
if (interactiveUidSet == null) return;
|
||||
|
||||
// 2. 构建通知消息 - 评论:uid、feedId、comment、replyCommentId
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("replyCommentId", feedComment.getReplyCommentId());
|
||||
data.put("uid", feedComment.getUid());
|
||||
data.put("feedId", feedComment.getFeedId());
|
||||
data.put("comment", feedComment.getContent());
|
||||
|
||||
sendFeedNotify(data, interactiveUidSet, feedComment.getUid(), "评论");
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 发送评论通知失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送朋友圈通知(统一处理点赞和评论)
|
||||
* 前端通过判断 comment 字段是否存在来区分点赞还是评论
|
||||
*/
|
||||
private void sendFeedNotify(Map<String, Object> data, Set<Long> interactiveUidSet, Long operatorUid, String type) {
|
||||
WsBaseResp<Map<String, Object>> resp = new WsBaseResp<>();
|
||||
resp.setType(WSRespTypeEnum.FEED_NOTIFY.getType());
|
||||
resp.setData(data);
|
||||
|
||||
pushService.sendPushMsg(resp, new ArrayList<>(interactiveUidSet), operatorUid);
|
||||
|
||||
Long feedId = (Long) data.get("feedId");
|
||||
log.info("✅ {}通知已发送,朋友圈ID: {},操作人: {},通知人数: {}", type, feedId, operatorUid, interactiveUidSet.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyFeedUnlike(Long feedId, Long uid) {
|
||||
try {
|
||||
// 1. 获取跟朋友圈有关系的好友
|
||||
Set<Long> interactiveUidSet = getInteractiveUid(feedId, uid);
|
||||
if (interactiveUidSet == null) return;
|
||||
|
||||
// 2. 构建通知消息 - 取消点赞:uid、feedId、isUnlike=true(无 comment 字段)
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("uid", uid);
|
||||
data.put("feedId", feedId);
|
||||
data.put("isUnlike", true); // 标记为取消点赞
|
||||
|
||||
sendFeedNotify(data, interactiveUidSet, uid, "取消点赞");
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 发送取消点赞通知失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与该朋友圈有互动关系的好友集合
|
||||
* 互动关系包括:已点赞或已评论
|
||||
*/
|
||||
private Set<Long> getInteractiveUidSet(Long feedId, List<Long> friendUidList) {
|
||||
Set<Long> interactiveUidSet = new HashSet<>();
|
||||
|
||||
// 1. 获取所有点赞人
|
||||
List<Long> likeUidList = feedLikeDao.getLikeUidList(feedId);
|
||||
interactiveUidSet.addAll(likeUidList);
|
||||
|
||||
// 2. 获取所有评论人
|
||||
List<FeedComment> commentList = feedCommentDao.getCommentListByFeedId(feedId);
|
||||
Set<Long> commentUidSet = commentList.stream()
|
||||
.map(FeedComment::getUid)
|
||||
.collect(Collectors.toSet());
|
||||
interactiveUidSet.addAll(commentUidSet);
|
||||
|
||||
// 3. 过滤出是好友的用户
|
||||
interactiveUidSet.retainAll(friendUidList);
|
||||
|
||||
return interactiveUidSet;
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,17 @@ import com.luohuo.basic.cache.redis2.CacheResult;
|
||||
import com.luohuo.basic.cache.repository.CachePlusOps;
|
||||
import com.luohuo.basic.exception.BizException;
|
||||
import com.luohuo.basic.model.cache.CacheHashKey;
|
||||
import com.luohuo.flex.common.cache.common.FeedCommentCacheKeyBuilder;
|
||||
import com.luohuo.flex.common.cache.common.FeedLikeCacheKeyBuilder;
|
||||
import com.luohuo.flex.common.cache.common.FeedMediaRelCacheKeyBuilder;
|
||||
import com.luohuo.flex.common.cache.common.FeedTargetRelCacheKeyBuilder;
|
||||
import com.luohuo.flex.im.domain.entity.FeedLike;
|
||||
import com.luohuo.flex.im.domain.vo.req.CursorPageBaseReq;
|
||||
import com.luohuo.flex.im.domain.vo.res.CursorPageBaseResp;
|
||||
import com.luohuo.flex.im.core.chat.service.adapter.MemberAdapter;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedCommentDao;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedDao;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedLikeDao;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedMediaDao;
|
||||
import com.luohuo.flex.im.core.user.dao.FeedTargetDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserFriendDao;
|
||||
@@ -24,18 +29,19 @@ import com.luohuo.flex.im.domain.enums.FeedPermissionEnum;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedParam;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedPermission;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedVo;
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedLikeVo;
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedCommentVo;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import com.luohuo.flex.im.core.user.service.FeedService;
|
||||
import com.luohuo.flex.im.core.user.service.FeedCommentService;
|
||||
import com.luohuo.flex.im.core.user.service.UserTargetRelService;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -53,17 +59,54 @@ public class FeedServiceImpl implements FeedService {
|
||||
private final UserFriendDao userFriendDao;
|
||||
private final CachePlusOps cachePlusOps;
|
||||
private final FeedTargetDao feedTargetDao;
|
||||
private final FeedCommentDao feedCommentDao;
|
||||
private final FeedCommentService feedCommentService;
|
||||
private final FeedLikeDao feedLikeDao;
|
||||
private final UserSummaryCache userSummaryCache;
|
||||
|
||||
/**
|
||||
* @param feedList 朋友圈基础内容
|
||||
* @param currentUid 当前用户ID
|
||||
* @return
|
||||
*/
|
||||
private List<FeedVo> buildFeedResp(List<Feed> feedList) {
|
||||
private List<FeedVo> buildFeedResp(List<Feed> feedList, Long currentUid) {
|
||||
return buildFeedResp(feedList, false, currentUid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param feedList 朋友圈基础内容
|
||||
* @param currentUid 当前用户ID(用于判断是否已点赞)
|
||||
* @return
|
||||
*/
|
||||
private List<FeedVo> buildFeedResp(List<Feed> feedList, boolean isDetail, Long currentUid) {
|
||||
List<FeedVo> feedVos = new ArrayList<>();
|
||||
|
||||
// 批量获取所有发布者的用户信息
|
||||
Set<Long> userIds = feedList.stream().map(Feed::getUid).collect(Collectors.toSet());
|
||||
Map<Long, SummeryInfoDTO> userInfoMap = userSummaryCache.getBatch(new ArrayList<>(userIds));
|
||||
|
||||
// 批量获取当前用户对所有朋友圈的点赞状态
|
||||
Map<Long, Boolean> likeStatusMap = new HashMap<>();
|
||||
if (CollUtil.isNotEmpty(feedList)) {
|
||||
List<Long> feedIds = feedList.stream().map(Feed::getId).collect(Collectors.toList());
|
||||
for (Long feedId : feedIds) {
|
||||
FeedLike like = feedLikeDao.get(currentUid, feedId);
|
||||
likeStatusMap.put(feedId, Objects.nonNull(like));
|
||||
}
|
||||
}
|
||||
|
||||
for (Feed feed : feedList) {
|
||||
FeedVo feedVo = new FeedVo();
|
||||
BeanUtil.copyProperties(feed, feedVo);
|
||||
|
||||
// 添加发布者信息
|
||||
SummeryInfoDTO userInfo = userInfoMap.get(feed.getUid());
|
||||
if (ObjectUtil.isNotNull(userInfo)) {
|
||||
feedVo.setUserName(userInfo.getName());
|
||||
feedVo.setUserAvatar(userInfo.getAvatar());
|
||||
}
|
||||
|
||||
// 添加媒体信息
|
||||
if(!feed.getMediaType().equals(FeedEnum.WORD.getType())){
|
||||
CacheHashKey hashKey = FeedMediaRelCacheKeyBuilder.build(feed.getId());
|
||||
CacheResult<List<FeedMedia>> result = cachePlusOps.get(hashKey, t -> feedMediaDao.getMediaByFeedId(feed.getId()));
|
||||
@@ -72,6 +115,46 @@ public class FeedServiceImpl implements FeedService {
|
||||
feedVo.setUrls(mediaList.stream().sorted(Comparator.comparingInt(FeedMedia::getSort)).map(FeedMedia::getUrl).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
// 添加点赞信息
|
||||
Integer likeCount = feedLikeDao.getLikeCount(feed.getId());
|
||||
feedVo.setLikeCount(likeCount);
|
||||
|
||||
// 设置当前用户是否已点赞
|
||||
feedVo.setHasLiked(likeStatusMap.getOrDefault(feed.getId(), false));
|
||||
|
||||
// 获取点赞列表(返回全部,前端按高度显示)
|
||||
List<Long> likeUidList = feedLikeDao.getLikeUidList(feed.getId());
|
||||
|
||||
if (CollUtil.isNotEmpty(likeUidList)) {
|
||||
Map<Long, SummeryInfoDTO> likeUserInfoMap = userSummaryCache.getBatch(likeUidList);
|
||||
List<FeedLikeVo> likeList = likeUidList.stream()
|
||||
.map(uid -> {
|
||||
SummeryInfoDTO likeUserInfo = likeUserInfoMap.get(uid);
|
||||
if (ObjectUtil.isNull(likeUserInfo)) {
|
||||
return null;
|
||||
}
|
||||
return FeedLikeVo.builder()
|
||||
.uid(uid)
|
||||
.userName(likeUserInfo.getName())
|
||||
.userAvatar(likeUserInfo.getAvatar())
|
||||
.build();
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
feedVo.setLikeList(likeList);
|
||||
}
|
||||
|
||||
// 添加评论数量
|
||||
Integer commentCount = feedCommentDao.getCommentCount(feed.getId());
|
||||
feedVo.setCommentCount(commentCount);
|
||||
|
||||
// 获取评论列表(返回全部,前端按高度显示)
|
||||
List<FeedCommentVo> commentList = feedCommentService.getCommentList(feed.getId(), null);
|
||||
if (CollUtil.isNotEmpty(commentList)) {
|
||||
feedVo.setCommentList(commentList);
|
||||
}
|
||||
|
||||
feedVos.add(feedVo);
|
||||
}
|
||||
return feedVos;
|
||||
@@ -123,7 +206,7 @@ public class FeedServiceImpl implements FeedService {
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// 4. 合并朋友圈内容
|
||||
List<FeedVo> result = buildFeedResp(filteredFeeds);
|
||||
List<FeedVo> result = buildFeedResp(filteredFeeds, uid);
|
||||
return CursorPageBaseResp.init(page, result, page.getTotal());
|
||||
}
|
||||
|
||||
@@ -274,14 +357,18 @@ public class FeedServiceImpl implements FeedService {
|
||||
*/
|
||||
@Transactional
|
||||
public Boolean delFeed(Long feedId){
|
||||
// 1. 首先将朋友圈素材、权限删除
|
||||
// 1. 首先将朋友圈素材、权限、评论、点赞删除
|
||||
feedMediaDao.delMediaByFeedId(feedId);
|
||||
feedTargetDao.delByFeedId(feedId);
|
||||
feedCommentDao.delByFeedId(feedId);
|
||||
feedLikeDao.delByFeedId(feedId);
|
||||
feedDao.removeById(feedId);
|
||||
|
||||
// 2. 清空缓存
|
||||
cachePlusOps.del(FeedTargetRelCacheKeyBuilder.build(feedId));
|
||||
cachePlusOps.del(FeedMediaRelCacheKeyBuilder.build(feedId));
|
||||
cachePlusOps.del(FeedCommentCacheKeyBuilder.build(feedId));
|
||||
cachePlusOps.del(FeedLikeCacheKeyBuilder.build(feedId));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -291,24 +378,19 @@ public class FeedServiceImpl implements FeedService {
|
||||
|
||||
/**
|
||||
* 查看朋友圈
|
||||
* @param feedId
|
||||
* @param feedId 朋友圈ID
|
||||
* @param uid 当前用户ID
|
||||
* @return
|
||||
*/
|
||||
public FeedVo feedDetail(Long feedId) {
|
||||
FeedVo feed = getDetail(feedId);
|
||||
|
||||
if(!feed.getMediaType().equals(FeedEnum.WORD.getType())){
|
||||
// 使用带回调的方式,自动处理缓存读写
|
||||
CacheHashKey hashKey = FeedMediaRelCacheKeyBuilder.build(feedId);
|
||||
CacheResult<List<FeedMedia>> result = cachePlusOps.get(hashKey, t -> feedMediaDao.getMediaByFeedId(feedId));
|
||||
List<FeedMedia> feedMediaList = result.getValue();
|
||||
|
||||
if (CollUtil.isNotEmpty(feedMediaList)){
|
||||
feed.setUrls(feedMediaList.stream().sorted(Comparator.comparingInt(FeedMedia::getSort)).map(FeedMedia::getUrl).collect(Collectors.toList()));
|
||||
}
|
||||
public FeedVo feedDetail(Long feedId, Long uid) {
|
||||
Feed feed = feedDao.getById(feedId);
|
||||
if (ObjectUtil.isNull(feed)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return feed;
|
||||
// 使用 buildFeedResp 方法构建详情页的响应(isDetail=true,显示全部点赞)
|
||||
List<FeedVo> feedVos = buildFeedResp(List.of(feed), true, uid);
|
||||
return feedVos.isEmpty() ? null : feedVos.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,7 +399,7 @@ public class FeedServiceImpl implements FeedService {
|
||||
* @return
|
||||
*/
|
||||
public FeedPermission getFeedPermission(Long uid, Long feedId) {
|
||||
FeedVo feedVo = feedDetail(feedId);
|
||||
FeedVo feedVo = feedDetail(feedId, uid);
|
||||
if(ObjectUtil.isNull(feedVo)){
|
||||
throw new RuntimeException("请选择朋友圈!");
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.luohuo.flex.im.service.tenant.EmailService;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 邮箱服务 TODO 整合实现
|
||||
* 邮箱服务
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
package com.luohuo.flex.im.controller.user;
|
||||
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedCommentPageReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedCommentReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedLikeReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedReq;
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedCommentVo;
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedLikeVo;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import com.luohuo.basic.base.R;
|
||||
import com.luohuo.basic.context.ContextUtil;
|
||||
import com.luohuo.basic.exception.BizException;
|
||||
import com.luohuo.flex.im.domain.vo.req.CursorPageBaseReq;
|
||||
import com.luohuo.flex.im.domain.vo.res.CursorPageBaseResp;
|
||||
import com.luohuo.flex.model.vo.query.OperParam;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedParam;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedPermission;
|
||||
import com.luohuo.flex.im.domain.vo.req.feed.FeedVo;
|
||||
import com.luohuo.flex.im.core.user.service.FeedCommentService;
|
||||
import com.luohuo.flex.im.core.user.service.FeedLikeService;
|
||||
import com.luohuo.flex.im.core.user.service.FeedService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -20,8 +28,12 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
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.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 发布朋友圈、编辑朋友圈、设置谁可见、谁不可见、仅聊天、不看他、不让他看我
|
||||
*/
|
||||
@@ -33,6 +45,12 @@ public class FeedController {
|
||||
@Resource
|
||||
private FeedService feedService;
|
||||
|
||||
@Resource
|
||||
private FeedCommentService feedCommentService;
|
||||
|
||||
@Resource
|
||||
private FeedLikeService feedLikeService;
|
||||
|
||||
@PostMapping("list")
|
||||
@Operation(summary = "朋友圈列表")
|
||||
public R<CursorPageBaseResp<FeedVo>> list(@Valid @RequestBody CursorPageBaseReq request) {
|
||||
@@ -66,6 +84,72 @@ public class FeedController {
|
||||
@GetMapping("detail")
|
||||
@Operation(summary = "用户查看详情")
|
||||
public R<FeedVo> feedDetail(FeedReq feedReq) {
|
||||
return R.success(feedService.feedDetail(feedReq.getFeedId()));
|
||||
return R.success(feedService.feedDetail(feedReq.getFeedId(), ContextUtil.getUid()));
|
||||
}
|
||||
|
||||
// ==================== 评论相关接口 ====================
|
||||
|
||||
@PostMapping("comment/add")
|
||||
@Operation(summary = "发表评论")
|
||||
public R<Boolean> addComment(@Valid @RequestBody FeedCommentReq req) {
|
||||
return R.success(feedCommentService.addComment(ContextUtil.getUid(), req));
|
||||
}
|
||||
|
||||
@PostMapping("comment/delete")
|
||||
@Operation(summary = "删除评论")
|
||||
public R<Boolean> delComment(@RequestParam(value = "commentId", required = false) Long commentId,
|
||||
@RequestBody(required = false) Map<String, Long> body) {
|
||||
// 支持两种方式:URL参数或请求体
|
||||
Long id = commentId != null ? commentId : (body != null ? body.get("commentId") : null);
|
||||
if (id == null) {
|
||||
throw new BizException("缺少必须的[Long]类型的参数[commentId]");
|
||||
}
|
||||
return R.success(feedCommentService.delComment(ContextUtil.getUid(), id));
|
||||
}
|
||||
|
||||
@PostMapping("comment/list")
|
||||
@Operation(summary = "分页查询评论列表")
|
||||
public R<CursorPageBaseResp<FeedCommentVo>> getCommentPage(@Valid @RequestBody FeedCommentPageReq req) {
|
||||
CursorPageBaseReq pageReq = new CursorPageBaseReq();
|
||||
pageReq.setCursor(req.getCursor());
|
||||
pageReq.setPageSize(req.getPageSize());
|
||||
return R.success(feedCommentService.getCommentPage(req.getFeedId(), pageReq));
|
||||
}
|
||||
|
||||
@GetMapping("comment/count")
|
||||
@Operation(summary = "获取评论数量")
|
||||
public R<Integer> getCommentCount(@RequestParam Long feedId) {
|
||||
return R.success(feedCommentService.getCommentCount(feedId));
|
||||
}
|
||||
|
||||
@GetMapping("comment/all")
|
||||
@Operation(summary = "获取所有评论列表(不分页)")
|
||||
public R<List<FeedCommentVo>> getAllComments(@RequestParam Long feedId) {
|
||||
return R.success(feedCommentService.getCommentList(feedId, null));
|
||||
}
|
||||
|
||||
// ==================== 点赞相关接口 ====================
|
||||
@PostMapping("like/toggle")
|
||||
@Operation(summary = "点赞或取消点赞")
|
||||
public R<Boolean> toggleLike(@Valid @RequestBody FeedLikeReq req) {
|
||||
return R.success(feedLikeService.setLike(ContextUtil.getUid(), req.getFeedId(), req.getActType()));
|
||||
}
|
||||
|
||||
@GetMapping("like/list")
|
||||
@Operation(summary = "获取点赞用户列表")
|
||||
public R<List<FeedLikeVo>> getLikeList(@RequestParam Long feedId) {
|
||||
return R.success(feedLikeService.getLikeList(feedId));
|
||||
}
|
||||
|
||||
@GetMapping("like/count")
|
||||
@Operation(summary = "获取点赞数量")
|
||||
public R<Integer> getLikeCount(@RequestParam Long feedId) {
|
||||
return R.success(feedLikeService.getLikeCount(feedId));
|
||||
}
|
||||
|
||||
@GetMapping("like/hasLiked")
|
||||
@Operation(summary = "判断是否已点赞")
|
||||
public R<Boolean> hasLiked(@RequestParam Long feedId) {
|
||||
return R.success(feedLikeService.hasLiked(ContextUtil.getUid(), feedId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.luohuo.flex.im.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.luohuo.basic.base.entity.SuperEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 朋友圈评论表
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("im_feed_comment")
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FeedComment extends SuperEntity<Long> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 朋友圈ID
|
||||
*/
|
||||
@Schema(description = "朋友圈ID")
|
||||
@TableField("feed_id")
|
||||
private Long feedId;
|
||||
|
||||
/**
|
||||
* 评论人uid
|
||||
*/
|
||||
@Schema(description = "评论人uid")
|
||||
@TableField("uid")
|
||||
private Long uid;
|
||||
|
||||
/**
|
||||
* 回复的评论ID(如果是回复评论,则有值;如果是直接评论朋友圈,则为null)
|
||||
*/
|
||||
@Schema(description = "回复的评论ID")
|
||||
@TableField("reply_comment_id")
|
||||
private Long replyCommentId;
|
||||
|
||||
/**
|
||||
* 被回复人的uid(如果是回复评论,则有值)
|
||||
*/
|
||||
@Schema(description = "被回复人的uid")
|
||||
@TableField("reply_uid")
|
||||
private Long replyUid;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
@Schema(description = "评论内容")
|
||||
@TableField("content")
|
||||
private String content;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.luohuo.flex.im.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 朋友圈点赞表
|
||||
*/
|
||||
@Data
|
||||
@TableName("im_feed_like")
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FeedLike implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 朋友圈ID
|
||||
*/
|
||||
@Schema(description = "朋友圈ID")
|
||||
@TableField("feed_id")
|
||||
private Long feedId;
|
||||
|
||||
/**
|
||||
* 点赞人uid
|
||||
*/
|
||||
@Schema(description = "点赞人uid")
|
||||
@TableField("uid")
|
||||
private Long uid;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(description = "创建时间")
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
@Schema(description = "创建者")
|
||||
@TableField("create_by")
|
||||
private Long createBy;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.luohuo.flex.im.domain.vo.req.feed;
|
||||
|
||||
import com.luohuo.flex.im.domain.vo.req.CursorPageBaseReq;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 朋友圈评论分页请求
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FeedCommentPageReq extends CursorPageBaseReq {
|
||||
|
||||
@NotNull(message = "朋友圈ID不能为空")
|
||||
@Schema(description = "朋友圈ID")
|
||||
private Long feedId;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.luohuo.flex.im.domain.vo.req.feed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 朋友圈评论请求
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FeedCommentReq {
|
||||
|
||||
@NotNull(message = "朋友圈ID不能为空")
|
||||
@Schema(description = "朋友圈ID")
|
||||
private Long feedId;
|
||||
|
||||
@Schema(description = "回复的评论ID(如果是回复评论,则有值;如果是直接评论朋友圈,则为null)")
|
||||
private Long replyCommentId;
|
||||
|
||||
@Schema(description = "被回复人的uid(如果是回复评论,则有值)")
|
||||
private Long replyUid;
|
||||
|
||||
@NotNull(message = "评论内容不能为空")
|
||||
@Size(min = 1, max = 500, message = "评论内容必须在1到500个字符之间")
|
||||
@Schema(description = "评论内容")
|
||||
private String content;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.luohuo.flex.im.domain.vo.req.feed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 朋友圈点赞请求
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FeedLikeReq {
|
||||
|
||||
@NotNull(message = "朋友圈ID不能为空")
|
||||
@Schema(description = "朋友圈ID")
|
||||
private Long feedId;
|
||||
|
||||
@NotNull(message = "操作类型不能为空")
|
||||
@Schema(description = "操作类型 1-点赞 2-取消点赞")
|
||||
private Integer actType;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.luohuo.flex.im.domain.vo.req.feed;
|
||||
|
||||
import com.luohuo.flex.im.domain.entity.Feed;
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedLikeVo;
|
||||
import com.luohuo.flex.im.domain.vo.resp.feed.FeedCommentVo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -14,4 +16,25 @@ public class FeedVo extends Feed {
|
||||
|
||||
@Schema(description = "发布的内容的url")
|
||||
private List<String> urls;
|
||||
|
||||
@Schema(description = "点赞数量")
|
||||
private Integer likeCount;
|
||||
|
||||
@Schema(description = "评论数量")
|
||||
private Integer commentCount;
|
||||
|
||||
@Schema(description = "当前用户是否已点赞")
|
||||
private Boolean hasLiked;
|
||||
|
||||
@Schema(description = "点赞用户列表(列表页显示前3个,详情页显示全部)")
|
||||
private List<FeedLikeVo> likeList;
|
||||
|
||||
@Schema(description = "评论列表(列表页显示前3条,详情页显示全部)")
|
||||
private List<FeedCommentVo> commentList;
|
||||
|
||||
@Schema(description = "发布人昵称")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "发布人头像")
|
||||
private String userAvatar;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.luohuo.flex.im.domain.vo.resp.feed;
|
||||
|
||||
import com.luohuo.flex.im.domain.entity.FeedComment;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 朋友圈评论响应VO
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class FeedCommentVo extends FeedComment {
|
||||
|
||||
@Schema(description = "评论人昵称")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "评论人头像")
|
||||
private String userAvatar;
|
||||
|
||||
@Schema(description = "被回复人昵称")
|
||||
private String replyUserName;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.luohuo.flex.im.domain.vo.resp.feed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 朋友圈点赞响应VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FeedLikeVo {
|
||||
|
||||
@Schema(description = "用户ID")
|
||||
private Long uid;
|
||||
|
||||
@Schema(description = "用户昵称")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "用户头像")
|
||||
private String userAvatar;
|
||||
}
|
||||
@@ -264,6 +264,16 @@ public interface CacheKeyTable {
|
||||
*/
|
||||
String FEED_TARGET = "feedTarget";
|
||||
|
||||
/**
|
||||
* 朋友圈评论
|
||||
*/
|
||||
String FEED_COMMENT = "feedComment";
|
||||
|
||||
/**
|
||||
* 朋友圈点赞
|
||||
*/
|
||||
String FEED_LIKE = "feedLike";
|
||||
|
||||
/**
|
||||
* 会话信息
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.luohuo.flex.common.cache.common;
|
||||
|
||||
import com.luohuo.basic.base.entity.SuperEntity;
|
||||
import com.luohuo.basic.model.cache.CacheHashKey;
|
||||
import com.luohuo.basic.model.cache.CacheKeyBuilder;
|
||||
import com.luohuo.flex.common.cache.CacheKeyModular;
|
||||
import com.luohuo.flex.common.cache.CacheKeyTable;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 朋友圈评论缓存 朋友圈ID -> 评论列表
|
||||
* @author 乾乾
|
||||
*/
|
||||
public class FeedCommentCacheKeyBuilder implements CacheKeyBuilder {
|
||||
public static CacheHashKey build(Long feedId) {
|
||||
return new FeedCommentCacheKeyBuilder().hashKey(feedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenant() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTable() {
|
||||
return CacheKeyTable.Chat.FEED_COMMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
return CacheKeyModular.PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModular() {
|
||||
return CacheKeyModular.CHAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getField() {
|
||||
return SuperEntity.ID_FIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueType getValueType() {
|
||||
return ValueType.obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getExpire() {
|
||||
return Duration.ofDays(7L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.luohuo.flex.common.cache.common;
|
||||
|
||||
import com.luohuo.basic.base.entity.SuperEntity;
|
||||
import com.luohuo.basic.model.cache.CacheHashKey;
|
||||
import com.luohuo.basic.model.cache.CacheKeyBuilder;
|
||||
import com.luohuo.flex.common.cache.CacheKeyModular;
|
||||
import com.luohuo.flex.common.cache.CacheKeyTable;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 朋友圈点赞缓存 朋友圈ID -> 点赞用户ID列表
|
||||
* @author 乾乾
|
||||
*/
|
||||
public class FeedLikeCacheKeyBuilder implements CacheKeyBuilder {
|
||||
public static CacheHashKey build(Long feedId) {
|
||||
return new FeedLikeCacheKeyBuilder().hashKey(feedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenant() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTable() {
|
||||
return CacheKeyTable.Chat.FEED_LIKE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
return CacheKeyModular.PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModular() {
|
||||
return CacheKeyModular.CHAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getField() {
|
||||
return SuperEntity.ID_FIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueType getValueType() {
|
||||
return ValueType.obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getExpire() {
|
||||
return Duration.ofDays(7L);
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ public enum WSRespTypeEnum {
|
||||
|
||||
ROOM_GROUP_NOTICE_READ_MSG("roomGroupNoticeReadMsg", "群公告已读", null),
|
||||
FEED_SEND_MSG("feedSendMsg", "朋友圈发布", null),
|
||||
FEED_NOTIFY("feedNotify", "朋友圈通知(点赞/评论)", null),
|
||||
ROOM_NOTIFICATION("roomNotification", "会话消息接收类型改变", null),
|
||||
SHIELD("shield", "你已屏蔽好友的消息", null),
|
||||
UNBLOCK("unblock", "你已解除屏蔽好友的消息", null),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.luohuo.flex.satoken.config;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -8,6 +9,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import com.luohuo.basic.constant.Constants;
|
||||
import com.luohuo.flex.common.properties.IgnoreProperties;
|
||||
import com.luohuo.flex.common.properties.SystemProperties;
|
||||
import com.luohuo.flex.satoken.spring.MySaTokenContextForSpringInJakartaServlet;
|
||||
|
||||
/**
|
||||
* 注册 Sa-Token 框架所需要的 Bean
|
||||
@@ -15,8 +17,19 @@ import com.luohuo.flex.common.properties.SystemProperties;
|
||||
* @since 2024/9/18 14:38
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class MySaTokenContextRegister {
|
||||
|
||||
/**
|
||||
* 注册 SaTokenContext Bean,用于 Spring Boot 3 Jakarta Servlet 环境
|
||||
* 这个 Bean 必须被注册,否则 SaToken 会报"上下文尚未初始化"的错误
|
||||
*/
|
||||
@Bean
|
||||
public SaTokenContext saTokenContext() {
|
||||
log.info("注册 SaTokenContext 实现类:MySaTokenContextForSpringInJakartaServlet");
|
||||
return new MySaTokenContextForSpringInJakartaServlet();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = Constants.PROJECT_PREFIX + ".webmvc", name = "header", havingValue = "true", matchIfMissing = true)
|
||||
public static class InnerConfig {
|
||||
|
||||
@@ -1,76 +1,84 @@
|
||||
///*
|
||||
// * Copyright 2020-2099 sa-token.cc
|
||||
// *
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// */
|
||||
//package com.luohuo.flex.satoken.spring;
|
||||
//
|
||||
//import cn.dev33.satoken.context.SaTokenContext;
|
||||
//import cn.dev33.satoken.context.model.SaRequest;
|
||||
//import cn.dev33.satoken.context.model.SaResponse;
|
||||
//import cn.dev33.satoken.context.model.SaStorage;
|
||||
//import cn.dev33.satoken.servlet.model.SaRequestForServlet;
|
||||
//import cn.dev33.satoken.servlet.model.SaResponseForServlet;
|
||||
//import cn.dev33.satoken.servlet.model.SaStorageForServlet;
|
||||
//import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
//import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil;
|
||||
//
|
||||
///**
|
||||
// * Sa-Token 上下文处理器 [ SpringBoot3 Jakarta Servlet 版 ],在 SpringBoot3 中使用 Sa-Token 时,必须注入此实现类,否则会出现上下文无效异常
|
||||
// *
|
||||
// * @author click33
|
||||
// * @since 1.34.0
|
||||
// */
|
||||
//public class MySaTokenContextForSpringInJakartaServlet implements SaTokenContext {
|
||||
//
|
||||
// /**
|
||||
// * 获取当前请求的 Request 包装对象
|
||||
// */
|
||||
// @Override
|
||||
// public SaRequest getRequest() {
|
||||
// return new MySaRequestForServlet(SpringMVCUtil.getRequest());
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取当前请求的 Response 包装对象
|
||||
// */
|
||||
// @Override
|
||||
// public SaResponse getResponse() {
|
||||
// return new SaResponseForServlet(SpringMVCUtil.getResponse());
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取当前请求的 Storage 包装对象
|
||||
// */
|
||||
// @Override
|
||||
// public SaStorage getStorage() {
|
||||
// return new SaStorageForServlet(SpringMVCUtil.getRequest());
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 判断:指定路由匹配符是否可以匹配成功指定路径
|
||||
// */
|
||||
// @Override
|
||||
// public boolean matchPath(String pattern, String path) {
|
||||
// return SaPathPatternParserUtil.match(pattern, path);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 判断:在本次请求中,此上下文是否可用。
|
||||
// */
|
||||
// @Override
|
||||
// public boolean isValid() {
|
||||
// return SpringMVCUtil.isWeb();
|
||||
// }
|
||||
//
|
||||
//}
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.luohuo.flex.satoken.spring;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.context.model.SaTokenContextModelBox;
|
||||
import cn.dev33.satoken.servlet.model.SaRequestForServlet;
|
||||
import cn.dev33.satoken.servlet.model.SaResponseForServlet;
|
||||
import cn.dev33.satoken.servlet.model.SaStorageForServlet;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器 [ SpringBoot3 Jakarta Servlet 版 ],在 SpringBoot3 中使用 Sa-Token 时,必须注入此实现类,否则会出现上下文无效异常
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.34.0
|
||||
*/
|
||||
public class MySaTokenContextForSpringInJakartaServlet implements SaTokenContext {
|
||||
|
||||
/**
|
||||
* 获取当前请求的 Request 包装对象
|
||||
*/
|
||||
@Override
|
||||
public SaRequest getRequest() {
|
||||
return new SaRequestForServlet(SpringMVCUtil.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的 Response 包装对象
|
||||
*/
|
||||
@Override
|
||||
public SaResponse getResponse() {
|
||||
return new SaResponseForServlet(SpringMVCUtil.getResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的 Storage 包装对象
|
||||
*/
|
||||
@Override
|
||||
public SaStorage getStorage() {
|
||||
return new SaStorageForServlet(SpringMVCUtil.getRequest());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(SaRequest saRequest, SaResponse saResponse, SaStorage saStorage) {
|
||||
// Servlet 环境中,上下文通过 ThreadLocal 自动管理,无需手动设置
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContext() {
|
||||
// Servlet 环境中,上下文通过 ThreadLocal 自动管理,无需手动清除
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:在本次请求中,此上下文是否可用。
|
||||
*/
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return SpringMVCUtil.isWeb();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaTokenContextModelBox getModelBox() {
|
||||
// 返回当前请求的上下文模型盒子
|
||||
return new SaTokenContextModelBox(getRequest(), getResponse(), getStorage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,12 +77,6 @@
|
||||
<dependency>
|
||||
<groupId>com.luohuo.basic</groupId>
|
||||
<artifactId>luohuo-all</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.luohuo.basic</groupId>
|
||||
<artifactId>luohuo-cloud-starter</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
@@ -116,6 +110,11 @@
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -16,11 +16,15 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import com.luohuo.basic.boot.config.BaseConfig;
|
||||
import com.luohuo.basic.constant.Constants;
|
||||
import com.luohuo.basic.log.event.SysLogListener;
|
||||
import cn.dev33.satoken.filter.SaTokenContextFilterForJakartaServlet;
|
||||
import com.luohuo.flex.base.interceptor.AuthenticationSaInterceptor;
|
||||
import com.luohuo.flex.base.interceptor.TokenContextFilter;
|
||||
import com.luohuo.flex.common.properties.IgnoreProperties;
|
||||
import com.luohuo.flex.common.properties.SystemProperties;
|
||||
import com.luohuo.flex.oauth.facade.LogFacade;
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* 基础服务-Web配置
|
||||
@@ -49,6 +53,22 @@ public class BootWebConfiguration extends BaseConfig implements WebMvcConfigurer
|
||||
return new AuthenticationSaInterceptor(ignoreProperties, defResourceFacade);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 SaToken 上下文过滤器,用于初始化 SaToken 上下文
|
||||
* 这个过滤器必须在所有其他过滤器之前执行,以确保 SaToken 上下文被正确初始化
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean saTokenContextFilterForJakartaServlet() {
|
||||
FilterRegistrationBean bean = new FilterRegistrationBean<>(new SaTokenContextFilterForJakartaServlet());
|
||||
// 配置 Filter 拦截的 URL 模式
|
||||
bean.addUrlPatterns("/*");
|
||||
// 设置 Filter 的执行顺序,数值越小越先执行
|
||||
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
bean.setAsyncSupported(true);
|
||||
bean.setDispatcherTypes(EnumSet.of(DispatcherType.ASYNC, DispatcherType.REQUEST));
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/**").addResourceLocations("classpath:/");
|
||||
|
||||
@@ -265,6 +265,8 @@ spring:
|
||||
- classpath:config/${spring.profiles.active}/rocketmq.yml
|
||||
- classpath:config/${spring.profiles.active}/redis.yml
|
||||
- classpath:config/${spring.profiles.active}/mysql.yml
|
||||
- classpath:config/${spring.profiles.active}/base-server.yml
|
||||
- classpath:config/${spring.profiles.active}/ai-server.yml
|
||||
mvc:
|
||||
pathmatch:
|
||||
# 升级springboot2.6.6后临时处理,防止swagger报错
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
spring:
|
||||
autoconfigure:
|
||||
exclude:
|
||||
- org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建
|
||||
- org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建
|
||||
main:
|
||||
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
|
||||
ai:
|
||||
vectorstore: # 向量存储
|
||||
redis:
|
||||
initialize-schema: true
|
||||
index: knowledge_index # Redis 中向量索引的名称:用于存储和检索向量数据的索引标识符,所有相关的向量搜索操作都会基于这个索引进行
|
||||
prefix: "knowledge_segment:" # Redis 中存储向量数据的键名前缀:这个前缀会添加到每个存储在 Redis 中的向量数据键名前,每个 document 都是一个 hash 结构
|
||||
qdrant:
|
||||
initialize-schema: true
|
||||
collection-name: knowledge_segment # Qdrant 中向量集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行
|
||||
host: 127.0.0.1
|
||||
port: 6334
|
||||
milvus:
|
||||
initialize-schema: true
|
||||
database-name: default # Milvus 中数据库的名称
|
||||
collection-name: knowledge_segment # Milvus 中集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行
|
||||
client:
|
||||
host: 127.0.0.1
|
||||
port: 19530
|
||||
qianfan: # 文心一言
|
||||
api-key: x0cuLZ7XsaTCU08vuJWO87Lg
|
||||
secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK
|
||||
zhipuai: # 智谱 AI
|
||||
api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs
|
||||
openai: # OpenAI 官方
|
||||
api-key: sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17
|
||||
base-url: https://api.gptsapi.net
|
||||
azure: # OpenAI 微软
|
||||
openai:
|
||||
endpoint: https://eastusprejade.openai.azure.com
|
||||
api-key: xxx
|
||||
ollama:
|
||||
base-url: http://127.0.0.1:11434
|
||||
chat:
|
||||
model: llama3
|
||||
stabilityai:
|
||||
api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx
|
||||
dashscope: # 通义千问
|
||||
api-key: sk-71800982914041848008480000000000
|
||||
minimax: # Minimax:https://www.minimaxi.com/
|
||||
api-key: xxxx
|
||||
moonshot: # 月之暗灭(KIMI)
|
||||
api-key: sk-abc
|
||||
@@ -0,0 +1,72 @@
|
||||
luohuo:
|
||||
file:
|
||||
storageType: LOCAL # FAST_DFS LOCAL MIN_IO ALI_OSS HUAWEI_OSS QINIU_OSS
|
||||
delFile: false
|
||||
suffix: doc,docx,gif,jpeg,jpg,pdf,png,rar,zip,xls,xlsx,txt,file,bin,egp,egpx,apk,xml,rpx,rpg,html,htm,wps,xbid,db
|
||||
publicBucket:
|
||||
- public
|
||||
local:
|
||||
storagePath: /Users/luohuo/data/projects/uploadfile/file/ # 文件存储路径 ( 某些版本的 window 需要改成 D:\\data\\projects\\uploadfile\\file\\ )
|
||||
urlPrefix: http://127.0.0.1/file/ # 文件访问 (部署nginx后,配置nginx的ip,并配置nginx静态代理storage-path地址的静态资源)
|
||||
innerUrlPrefix: null # 内网的url前缀
|
||||
fastDfs:
|
||||
urlPrefix: https://fastdfs.hula.com/
|
||||
ali:
|
||||
# 请填写自己的阿里云存储配置
|
||||
urlPrefix: "https://luohuo-admin-cloud.oss-cn-beijing.aliyuncs.com/"
|
||||
bucket: "luohuo-admin-cloud"
|
||||
endpoint: "oss-cn-beijing.aliyuncs.com"
|
||||
accessKeyId: "填写你的id"
|
||||
accessKeySecret: "填写你的秘钥"
|
||||
minIo:
|
||||
endpoint: "https://static.luohuo.top/"
|
||||
accessKey: "luohuo"
|
||||
secretKey: "123."
|
||||
bucket: "dev"
|
||||
huawei:
|
||||
uriPrefix: https://123-dev.obs.cn-southwest-2.myhuaweicloud.com/
|
||||
endpoint: "obs.cn-southwest-2.myhuaweicloud.com"
|
||||
accessKey: "123"
|
||||
secretKey: "123"
|
||||
bucket: "123-dev"
|
||||
location: "cn-123-2"
|
||||
qiNiu:
|
||||
domain: cdn.hula
|
||||
useHttps: true
|
||||
zone: "z0"
|
||||
accessKey: "1"
|
||||
secretKey: "2"
|
||||
bucket: "luohuo_admin_cloud"
|
||||
|
||||
#FAST_DFS配置
|
||||
fdfs:
|
||||
soTimeout: 1500
|
||||
connectTimeout: 600
|
||||
thumb-image:
|
||||
width: 150
|
||||
height: 150
|
||||
tracker-list:
|
||||
- 39.108.109.234:22122
|
||||
pool:
|
||||
#从池中借出的对象的最大数目
|
||||
max-total: 153
|
||||
max-wait-millis: 102
|
||||
jmx-name-base: 1
|
||||
jmx-name-prefix: 1
|
||||
# 这里也需要配置
|
||||
spring:
|
||||
mail:
|
||||
# 网易邮箱SMTP服务器
|
||||
host: smtp.163.com
|
||||
# SSL端口
|
||||
port: 465
|
||||
# 发件人邮箱
|
||||
username: nongyehon1g919@163.com
|
||||
# 邮箱SMTP授权码
|
||||
password: YZ7ju1X6W11WSbx2e
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
ssl:
|
||||
enable: true
|
||||
auth: true
|
||||
@@ -1,14 +1,26 @@
|
||||
server:
|
||||
port: 18760
|
||||
# 优雅停机
|
||||
shutdown: GRACEFUL
|
||||
servlet:
|
||||
encoding:
|
||||
enabled: true
|
||||
charset: UTF-8
|
||||
force: true
|
||||
# undertow:
|
||||
# threads:
|
||||
# io: 4 # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
|
||||
# worker: 80 # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
|
||||
# buffer-size: 2048 # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 , 每块buffer的空间大小,越小的空间被利用越充分
|
||||
# direct-buffers: true # 是否分配的直接内存
|
||||
luohuo:
|
||||
version: 2.6.5
|
||||
generator:
|
||||
outputDir: /Users/luohuo/gitlab/luohuo-boot-pro-column
|
||||
frontOutputDir: /Users/luohuo/gitlab/luohuo-web-pro
|
||||
frontSoyOutputDir: /Users/luohuo/gitlab/luohuo-web-pro-soybean
|
||||
frontVben5OutputDir: /Users/luohuo/github/luohuo-web-vben-max/apps/web-antd
|
||||
# 作者
|
||||
author: qianqian
|
||||
# 默认项目
|
||||
projectType: BOOT
|
||||
version: 3.0.0
|
||||
thread:
|
||||
multiplier: 2 # 线程乘数
|
||||
min: 4 # 最小线程数
|
||||
retry:
|
||||
max-count: 3 # 最大重试次数
|
||||
delay-seconds: 3 # 多久时候开始重试
|
||||
# 微信扫码配置
|
||||
wx:
|
||||
mp:
|
||||
@@ -18,21 +30,257 @@ luohuo:
|
||||
secret: 9c215f8d078422805218221144b9095b # 公众号的appsecret
|
||||
token: nongyehong
|
||||
aesKey: 7iNecFLZ8nbtCaMntHYEruSHP1Mk8FmGvrkHwnJzwug # 接口配置里的EncodingAESKey值
|
||||
spring:
|
||||
mail:
|
||||
host: smtp.qq.com # 邮箱SMTP服务器
|
||||
port: 465 # SSL端口
|
||||
username: xxxxxxxxxxxxxxx # 发件人邮箱
|
||||
password: xxxxxxxx # 邮箱SMTP授权码
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
starttls:
|
||||
enable: true
|
||||
ssl:
|
||||
enable: false
|
||||
protocols: TLSv1.2 # 加密协议
|
||||
feign:
|
||||
# xxl-job-admin 的地址
|
||||
job-server: http://127.0.0.1:8767
|
||||
webmvc:
|
||||
undertow: true
|
||||
header: true
|
||||
scan:
|
||||
enabled: true
|
||||
basePackage: com.luohuo
|
||||
ignore:
|
||||
# 是否启用网关的 uri权限鉴权 (设置为false,则后台不校验访问权限)
|
||||
authEnabled: true
|
||||
# 前端校验按钮 是否区分大小写
|
||||
caseSensitive: false
|
||||
# 系统没有配置某个URI时,是否允许访问
|
||||
notConfigUriAllow: false
|
||||
anyone: # 请求中 需要携带Tenant 且 需要携带Token(需要登录),但不需要验证uri权限
|
||||
ALL:
|
||||
- /*/anyone/**
|
||||
- /anyone/**
|
||||
anyUser: # 请求中 需要携带Tenant,但 不需要携带Token(不需要登录) 和 不需要验证uri权限
|
||||
ALL:
|
||||
- /*/anyUser/**
|
||||
- /anyUser/**
|
||||
anyTenant: # 请求中 不需要携带Tenant 且 不需要携带Token(不需要登录) 和 不需要验证uri权限
|
||||
ALL:
|
||||
- /*/anyTenant/**
|
||||
- /anyTenant/**
|
||||
system:
|
||||
# 登录时否验证密码有效性 (常用于开发环境快速登录)
|
||||
verifyPassword: true
|
||||
# 登录时否验证验证码有效性 (常用于开发环境快速登录)
|
||||
verifyCaptcha: true
|
||||
# 默认用户密码
|
||||
defPwd: '123456'
|
||||
# 密码最大输错次数 小于0不限制
|
||||
maxPasswordErrorNum: 10
|
||||
# 密码错误锁定用户时间 除了0表示锁定到今天结束,还支持m、h、d、w、M、y等单位
|
||||
passwordErrorLockUserTime: '0'
|
||||
# 锁定的时间
|
||||
lockUserTime: 15
|
||||
# 缓存Key前缀
|
||||
cachePrefix: luohuo
|
||||
# oauth 服务扫描枚举类的包路径
|
||||
enumPackage: "com.luohuo"
|
||||
# 演示环境启用
|
||||
notAllowWrite: false
|
||||
notAllowWriteList:
|
||||
POST:
|
||||
- /defResource
|
||||
- /defTenantApplicationRel/cancel
|
||||
PUT:
|
||||
- /defResource
|
||||
- /defResource/moveResource
|
||||
DELETE:
|
||||
- /defResource*/**
|
||||
- /defTenant*/**
|
||||
- /defApplication*/**
|
||||
- /defUser*/**
|
||||
- /defParameter*/**
|
||||
- /defDict*/**
|
||||
- /defClient*/**
|
||||
# swagger 文档通用配置, 主要配置了全局参数、版本号信息、联系人信息 详情看: SwaggerProperties
|
||||
swagger:
|
||||
license: Powered By luohuo
|
||||
licenseUrl: https://github.com/luohuo
|
||||
termsOfServiceUrl: https://github.com/luohuo
|
||||
contact: # 联系人信息
|
||||
url: https://github.com/luohuo
|
||||
name: luohuo
|
||||
email: 1046762075@qq.com
|
||||
global-operation-parameters: # 全局参数
|
||||
- name: Token
|
||||
description: 用户信息
|
||||
modelRef: String
|
||||
parameterType: header
|
||||
required: true
|
||||
# 默认值只是方便本地开发时,少填参数,生产环境请禁用swagger或者禁用默认参数
|
||||
defaultValue: "test"
|
||||
- name: Authorization
|
||||
description: 客户端信息
|
||||
modelRef: String
|
||||
parameterType: header
|
||||
required: true
|
||||
defaultValue: "bGFtcF93ZWI6bGFtcF93ZWJfc2VjcmV0"
|
||||
- name: ApplicationId
|
||||
description: 应用ID
|
||||
modelRef: String
|
||||
parameterType: header
|
||||
required: true
|
||||
defaultValue: "1"
|
||||
echo: #详情看: EchoProperties
|
||||
# 是否启用 远程数据 注解AOP注入
|
||||
enabled: true
|
||||
aop-enabled: true
|
||||
basePackages:
|
||||
- com.luohuo.basic
|
||||
- com.luohuo.flex
|
||||
# 字典类型 和 code 的分隔符
|
||||
dictSeparator: '###'
|
||||
# 多个字典code 之间的的分隔符
|
||||
dictItemSeparator: ','
|
||||
# 递归最大深度
|
||||
maxDepth: 3
|
||||
# 本地缓存配置信息 生产慎用
|
||||
guavaCache:
|
||||
enabled: false
|
||||
maximumSize: 1000
|
||||
refreshWriteTime: 2
|
||||
refreshThreadPoolSize: 10
|
||||
log: # 详情看:OptLogProperties
|
||||
# 开启记录操作日志
|
||||
enabled: true
|
||||
# 记录到什么地方 DB:mysql LOGGER:日志文件
|
||||
type: DB
|
||||
xss:
|
||||
# 是否开启 xss 过滤器 详情看:XssProperties
|
||||
enabled: true
|
||||
requestBodyEnabled: false
|
||||
captcha:
|
||||
# 登录界面的验证码配置 详情看:CaptchaProperties
|
||||
type: ARITHMETIC
|
||||
width: 158
|
||||
height: 58
|
||||
len: 2
|
||||
charType: 2
|
||||
async: # 全局线程池配置
|
||||
corePoolSize: 2
|
||||
maxPoolSize: 50
|
||||
queueCapacity: 10000
|
||||
keepAliveSeconds: 300
|
||||
threadNamePrefix: 'luohuo-async-executor-'
|
||||
tenant:
|
||||
enable: true
|
||||
ignoreTables:
|
||||
- secure_invoke_record
|
||||
- worker_node
|
||||
- base_operation_log_ext
|
||||
- base_operation_log
|
||||
|
||||
server:
|
||||
port: 18760
|
||||
|
||||
# knife4j 文档通用配置 详情看: Knife4jProperties
|
||||
knife4j:
|
||||
enable: true
|
||||
setting:
|
||||
language: zh_cn
|
||||
swagger-model-name: 实体类列表
|
||||
# 是否在每个Debug调试栏后显示刷新变量按钮,默认不显示
|
||||
enableReloadCacheParameter: true
|
||||
# 是否开启界面中对某接口的版本控制,如果开启,后端变化后Ui界面会存在小蓝点
|
||||
enableVersion: true
|
||||
# 针对RequestMapping的接口请求类型,在不指定参数类型的情况下,如果不过滤,默认会显示7个类型的接口地址参数,如果开启此配置,默认展示一个Post类型的接口地址
|
||||
enableFilterMultipartApis: false
|
||||
# 是否开启动态参数调试功能
|
||||
enableDynamicParameter: true
|
||||
# 是否显示Footer
|
||||
enableFooter: false
|
||||
enableFooterCustom: true
|
||||
footerCustomContent: Apache License 2.0 | Copyright 2025 [luohuo-cloud](https://github.com/qianqian)
|
||||
|
||||
springdoc:
|
||||
# 默认是false,需要设置为true
|
||||
default-flat-param-object: true
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
||||
tags-sorter: alpha
|
||||
operations-sorter: alpha
|
||||
enabled: ${knife4j.enable}
|
||||
api-docs:
|
||||
enabled: ${knife4j.enable}
|
||||
path: /v3/api-docs
|
||||
|
||||
spring:
|
||||
mvc:
|
||||
pathmatch:
|
||||
# 升级springboot2.6.6后临时处理,防止swagger报错
|
||||
matching-strategy: ANT_PATH_MATCHER
|
||||
lifecycle:
|
||||
# 优雅停机宽限期时间
|
||||
timeout-per-shutdown-phase: 30s
|
||||
servlet:
|
||||
# 上传文件最大值
|
||||
multipart:
|
||||
max-file-size: 512MB
|
||||
max-request-size: 512MB
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
base-path: /actuator
|
||||
exposure:
|
||||
include: '*'
|
||||
endpoint:
|
||||
health:
|
||||
show-details: ALWAYS
|
||||
enabled: true
|
||||
|
||||
feign:
|
||||
httpclient:
|
||||
enabled: false
|
||||
okhttp:
|
||||
enabled: true
|
||||
hystrix:
|
||||
enabled: false
|
||||
sentinel:
|
||||
enabled: true
|
||||
client:
|
||||
config:
|
||||
default:
|
||||
# feign client 调用全局超时时间
|
||||
connectTimeout: 60000
|
||||
readTimeout: 60000
|
||||
loggerLevel: FULL
|
||||
#支持压缩的mime types
|
||||
compression: # 请求压缩
|
||||
request:
|
||||
enabled: true
|
||||
mime-types: text/xml,application/xml,application/json
|
||||
min-request-size: 2048
|
||||
response: # 响应压缩
|
||||
enabled: true
|
||||
|
||||
|
||||
# Sa-Token配置
|
||||
sa-token:
|
||||
# token前缀
|
||||
token-prefix:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: Token
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
active-timeout: 60
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
is-concurrent: false
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: false
|
||||
# token风格
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: false
|
||||
# 开启后,logout() 会自动将 Token 加入黑名单
|
||||
is-blacklist: true
|
||||
# 设备类型标识(关键配置)
|
||||
device:
|
||||
pc: PC
|
||||
mobile: MOBILE
|
||||
|
||||
|
||||
p6spy:
|
||||
filter: true
|
||||
config:
|
||||
# comma separated list of strings to exclude
|
||||
exclude: secure_invoke_record
|
||||
@@ -1,62 +1,47 @@
|
||||
# 相当于 database.yml
|
||||
luohuo:
|
||||
# validation-query 参数对不同数据库的支持参考:https://www.cnblogs.com/BonnieWss/p/9100402.html
|
||||
oracle: &db-oracle
|
||||
db-type: oracle
|
||||
validation-query: SELECT 'x' FROM DUAL
|
||||
filters: stat,wall,slf4j # druid不支持使用p6spy打印日志,所以采用druid 的 slf4j 过滤器来打印可执行日志
|
||||
username: 'luohuo_none'
|
||||
password: 'luohuo_none'
|
||||
driverClassName: oracle.jdbc.driver.OracleDriver
|
||||
url: jdbc:oracle:thin:@172.26.3.67:1521:helowin
|
||||
mysql: &db-mysql
|
||||
filters: stat,wall
|
||||
db-type: mysql
|
||||
validation-query: SELECT 'x'
|
||||
username: 'luohuo_dev'
|
||||
username: 'root'
|
||||
password: '123456'
|
||||
# 生产使用原生驱动,开发使用p6spy驱动打印日志
|
||||
driverClassName: com.p6spy.engine.spy.P6SpyDriver
|
||||
url: jdbc:p6spy:mysql://127.0.0.1:13306/luohuo_dev?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
|
||||
dameng: &db-dameng
|
||||
username: 'SYSDBA'
|
||||
password: 'SYSdba001'
|
||||
driverClassName: dm.jdbc.driver.DmDriver
|
||||
# 主主复制集群的连接 URL,使用服务器IP和端口
|
||||
#以下是样例中的默认端口号和数据库名称,请根据实际情况修改
|
||||
url: jdbc:dm://192.168.1.139:5236/luohuo_ds_c_defaults
|
||||
sqlserver: &db-sqlserver
|
||||
username: 'sa'
|
||||
password: '1234@abcd'
|
||||
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
|
||||
url: jdbc:sqlserver://172.26.3.67:1433;DatabaseName=luohuo_none
|
||||
# driverClassName: com.p6spy.engine.spy.P6SpyDriver
|
||||
# url: jdbc:p6spy:sqlserver://172.26.3.67:1433;DatabaseName=luohuo_none
|
||||
db-type: sqlserver
|
||||
validation-query: SELECT 'x'
|
||||
filters: stat,wall
|
||||
init:
|
||||
separator: GO
|
||||
url: jdbc:p6spy:mysql://192.168.1.37:13306/luohuo_dev?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
|
||||
database: # 字段介绍参考 DatabaseProperties
|
||||
# 4.x 需要写死 column, 其他模式需要使用其他项目,而非改变此参数
|
||||
multiTenantType: NONE
|
||||
# 是否启用数据权限
|
||||
isDataScope: true
|
||||
# 是否启用 sql性能规范插件
|
||||
isBlockAttack: false
|
||||
# 是否启用 sql性能规范插件
|
||||
isIllegalSql: false
|
||||
# 是否启用分布式事务
|
||||
isSeata: false
|
||||
# 生产环境请设置p6spy = false
|
||||
p6spy: true
|
||||
p6spy: false
|
||||
# 单页分页条数限制
|
||||
maxLimit: -1
|
||||
# 溢出总页数后是否进行处理
|
||||
overflow: true
|
||||
# 生成 countSql 优化掉 join, 现在只支持 left join
|
||||
optimizeJoin: true
|
||||
# id生成策略
|
||||
id-type: CACHE
|
||||
hutoolId:
|
||||
id-type: DEFAULT
|
||||
hutool-id:
|
||||
workerId: 0
|
||||
dataCenterId: 0
|
||||
cache-id:
|
||||
time-bits: 31
|
||||
worker-bits: 22
|
||||
seq-bits: 10
|
||||
epochStr: '2020-09-15'
|
||||
default-id:
|
||||
# 时间位分配(默认41)
|
||||
time-bits: 41
|
||||
# 工作节点位分配(默认13)
|
||||
worker-bits: 13
|
||||
# 序列号位分配(默认9)
|
||||
seq-bits: 9
|
||||
# 起始时间
|
||||
epochStr: '2025-02-24'
|
||||
# size扩容参数
|
||||
boost-power: 3
|
||||
# 指定何时向RingBuffer中填充UID, 取值为百分比(0, 100), 默认为50
|
||||
padding-factor: 50
|
||||
|
||||
# mysql 通用配置
|
||||
@@ -70,22 +55,17 @@ spring:
|
||||
enable: true
|
||||
# 从这里开始(druid),中间的这段配置用于 luohuo.database.multiTenantType != DATASOURCE 时
|
||||
<<: *db-mysql
|
||||
# <<: *db-dameng
|
||||
# <<: *db-sqlserver
|
||||
# <<: *db-oracle
|
||||
initialSize: 10
|
||||
minIdle: 10
|
||||
maxActive: 200
|
||||
max-wait: 60000
|
||||
pool-prepared-statements: true
|
||||
max-pool-prepared-statement-per-connection-size: 20
|
||||
validation-query: SELECT 'x'
|
||||
test-on-borrow: false
|
||||
test-on-return: false
|
||||
test-while-idle: true
|
||||
test-while-idle: false
|
||||
time-between-eviction-runs-millis: 60000 #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
min-evictable-idle-time-millis: 300000 #配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
filters: stat,wall
|
||||
filter:
|
||||
wall:
|
||||
enabled: true
|
||||
@@ -101,14 +81,14 @@ spring:
|
||||
# 从这里结束(druid),中间的这段配置用于 luohuo.database.multiTenantType != DATASOURCE 时
|
||||
|
||||
# 以下的2段配置,同时适用于所有模式
|
||||
web-stat-filter: # WebStatFilter配置,说明请参考Druid Wiki,配置_配置WebStatFilter
|
||||
web-stat-filter: # WebStatFilter配置,说明请参考Druid Wiki,配置_配置WebStatFilter
|
||||
enabled: true
|
||||
url-pattern: /*
|
||||
exclusions: "*.js , *.gif ,*.jpg ,*.png ,*.css ,*.ico , /druid/*"
|
||||
session-stat-max-count: 1000
|
||||
profile-enable: true
|
||||
session-stat-enable: false
|
||||
stat-view-servlet: #展示Druid的统计信息,StatViewServlet的用途包括:1.提供监控信息展示的html页面2.提供监控信息的JSON API
|
||||
stat-view-servlet: #展示Druid的统计信息,StatViewServlet的用途包括:1.提供监控信息展示的html页面2.提供监控信息的JSON API
|
||||
enabled: true
|
||||
url-pattern: /druid/* #根据配置中的url-pattern来访问内置监控页面,如果是上面的配置,内置监控页面的首页是/druid/index.html例如:http://127.0.0.1:9000/druid/index.html
|
||||
reset-enable: true #允许清空统计数据
|
||||
@@ -118,7 +98,7 @@ spring:
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations:
|
||||
- classpath*:mapper_**/**/*Mapper.xml
|
||||
- classpath*:mapper**/**/**/*Mapper.xml
|
||||
#实体扫描,多个package用逗号或者分号分隔
|
||||
typeAliasesPackage: com.luohuo.flex.*.entity;com.luohuo.basic.database.mybatis.typehandler
|
||||
typeEnumsPackage: com.luohuo.flex.*.enumeration
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
luohuo:
|
||||
cache:
|
||||
type: REDIS
|
||||
serializer-type: JACK_SON
|
||||
redis:
|
||||
ip: 127.0.0.1
|
||||
ip: 192.168.1.37
|
||||
port: 16379
|
||||
password: 'mq0000'
|
||||
database: 7
|
||||
|
||||
password: luo.123456
|
||||
database: 1
|
||||
spring:
|
||||
cache:
|
||||
type: GENERIC
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# 相当于 rocketmq.yml
|
||||
luohuo:
|
||||
rocketmq:
|
||||
# 若系统中有除了zipkin之外的地方使用了mq ,则一定不能设置成false
|
||||
enabled: true
|
||||
ip: 117.72.82.249
|
||||
ip: 192.168.1.37
|
||||
port: 9876
|
||||
access-key: 'earthearth'
|
||||
secret-key: 'mq000000'
|
||||
|
||||
|
||||
rocketmq:
|
||||
ip: ${luohuo.rocketmq.ip}
|
||||
port: ${luohuo.rocketmq.port}
|
||||
@@ -34,4 +32,4 @@ rocketmq:
|
||||
secret-key: ${luohuo.rocketmq.secret-key} # Secret Key
|
||||
listeners: # 配置某个消费分组,是否监听指定 Topic 。结构为 Map<消费者分组, <Topic, Boolean>> 。默认情况下,不配置表示监听。
|
||||
erbadagang-consumer-group:
|
||||
topic1: false # 关闭 test-consumer-group 对 topic1 的监听消费
|
||||
topic1: false # 关闭 test-consumer-group 对 topic1 的监听消费
|
||||
@@ -28,19 +28,18 @@ public class AckProcessor implements MessageProcessor {
|
||||
private RocketMQTemplate rocketMQTemplate;
|
||||
|
||||
@Override
|
||||
public boolean supports(String payload) {
|
||||
WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public boolean supports(WSBaseReq req) {
|
||||
return WSReqTypeEnum.ACK.eq(req.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(WebSocketSession session, Long uid, String payload) {
|
||||
public void process(WebSocketSession session, Long uid, WSBaseReq payload) {
|
||||
if(ReactiveContextUtil.getTenantId() == null){
|
||||
ReactiveContextUtil.setTenantId(DefValConstants.DEF_TENANT_ID);
|
||||
ReactiveContextUtil.setUid(uid);
|
||||
}
|
||||
|
||||
AckMessageDTO req = JSONUtil.toBean(JSONUtil.toBean(payload, WSBaseReq.class).getData(), AckMessageDTO.class);
|
||||
AckMessageDTO req = JSONUtil.toBean(payload.getData(), AckMessageDTO.class);
|
||||
req.setUid(uid);
|
||||
rocketMQTemplate.send(MqConstant.MSG_PUSH_ACK_TOPIC, MessageBuilder.withPayload(req).build());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.luohuo.flex.ws.websocket.processor;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.luohuo.flex.model.ws.WSBaseReq;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -15,13 +14,12 @@ import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
@Component
|
||||
public class DefaultMessageProcessor implements MessageProcessor {
|
||||
@Override
|
||||
public boolean supports(String payload) {
|
||||
public boolean supports(WSBaseReq req) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(WebSocketSession session, Long uid, String payload) {
|
||||
WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public void process(WebSocketSession session, Long uid, WSBaseReq req) {
|
||||
log.warn("未知消息类型: {}", req.getType());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.luohuo.flex.ws.websocket.processor;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.luohuo.flex.model.ws.WSBaseReq;
|
||||
import com.luohuo.flex.model.enums.WSReqTypeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -16,13 +15,12 @@ import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
@Component
|
||||
public class HeartbeatProcessor implements MessageProcessor {
|
||||
@Override
|
||||
public boolean supports(String payload) {
|
||||
WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public boolean supports(WSBaseReq req) {
|
||||
return WSReqTypeEnum.HEARTBEAT.eq(req.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(WebSocketSession session, Long uid, String payload) {
|
||||
public void process(WebSocketSession session, Long uid, WSBaseReq req) {
|
||||
log.info("收到用户 {} 的心跳", uid);
|
||||
}
|
||||
}
|
||||
@@ -24,15 +24,13 @@
|
||||
// @Resource
|
||||
// private SessionManager sessionManager;
|
||||
// @Override
|
||||
// public boolean supports(String payload) {
|
||||
// WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
// public boolean supports(WSBaseReq req) {
|
||||
// return WSReqTypeEnum.LOGIN.equals(WSReqTypeEnum.of(req.getType()));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void process(WebSocketSession session, Long uid, String payload) {
|
||||
// public void process(WebSocketSession session, Long uid, WSBaseReq req) {
|
||||
// try {
|
||||
// WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
// String qrToken = req.getData().getStr("qrToken");
|
||||
//
|
||||
// // 1. 验证Token有效性
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.luohuo.flex.ws.websocket.processor;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.luohuo.flex.model.ws.WSBaseReq;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
@@ -22,10 +24,11 @@ public class MessageHandlerChain {
|
||||
}
|
||||
|
||||
public void handleMessage(WebSocketSession session, Long uid, String payload) {
|
||||
WSBaseReq bean = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
processors.stream()
|
||||
.filter(p -> {
|
||||
try {
|
||||
return p.supports(payload);
|
||||
return p.supports(bean);
|
||||
} catch (Exception e) {
|
||||
log.error("处理器[{}]支持检查异常", p.getClass().getSimpleName(), e);
|
||||
return false;
|
||||
@@ -34,7 +37,7 @@ public class MessageHandlerChain {
|
||||
.findFirst()
|
||||
.ifPresent(p -> {
|
||||
try {
|
||||
p.process(session, uid, payload);
|
||||
p.process(session, uid, bean);
|
||||
} catch (Exception e) {
|
||||
log.error("处理器[{}]执行失败", p.getClass().getSimpleName(), e);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.luohuo.flex.ws.websocket.processor;
|
||||
|
||||
import com.luohuo.flex.model.ws.WSBaseReq;
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
|
||||
/**
|
||||
@@ -9,15 +10,15 @@ public interface MessageProcessor {
|
||||
/**
|
||||
* 轻量级验证,避免复杂逻辑
|
||||
*
|
||||
* @param payload 消息内容
|
||||
* @param bean 消息内容
|
||||
*/
|
||||
boolean supports(String payload);
|
||||
boolean supports(WSBaseReq bean);
|
||||
|
||||
/**
|
||||
* 业务处理需捕获自身异常
|
||||
* @param session 会话
|
||||
* @param uid 用户id
|
||||
* @param payload 消息内容
|
||||
* @param bean 消息内容
|
||||
*/
|
||||
void process(WebSocketSession session, Long uid, String payload);
|
||||
void process(WebSocketSession session, Long uid, WSBaseReq bean);
|
||||
}
|
||||
@@ -26,14 +26,13 @@ public class ReadProcessor implements MessageProcessor {
|
||||
private RocketMQTemplate rocketMQTemplate;
|
||||
|
||||
@Override
|
||||
public boolean supports(String payload) {
|
||||
WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public boolean supports(WSBaseReq req) {
|
||||
return WSReqTypeEnum.READ.eq(req.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(WebSocketSession session, Long uid, String payload) {
|
||||
ReadMessageDTO req = JSONUtil.toBean(JSONUtil.toBean(payload, WSBaseReq.class).getData(), ReadMessageDTO.class);
|
||||
public void process(WebSocketSession session, Long uid, WSBaseReq payload) {
|
||||
ReadMessageDTO req = JSONUtil.toBean(payload.getData(), ReadMessageDTO.class);
|
||||
req.setUid(uid);
|
||||
rocketMQTemplate.send(MqConstant.MSG_PUSH_READ_TOPIC, MessageBuilder.withPayload(req).build());
|
||||
}
|
||||
|
||||
@@ -35,16 +35,14 @@ public class MediaControlProcessor implements MessageProcessor {
|
||||
private final VideoChatService videoService;
|
||||
|
||||
@Override
|
||||
public boolean supports(String payload) {
|
||||
WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public boolean supports(WSBaseReq req) {
|
||||
return MEDIA_MUTE_AUDIO.eq(req.getType()) ||
|
||||
MEDIA_MUTE_ALL.eq(req.getType()) ||
|
||||
MEDIA_MUTE_VIDEO.eq(req.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(WebSocketSession session, Long uid, String payload) {
|
||||
WSBaseReq baseReq = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public void process(WebSocketSession session, Long uid, WSBaseReq baseReq) {
|
||||
MediaControlVO control = JSONUtil.toBean(baseReq.getData(), MediaControlVO.class);
|
||||
|
||||
// 转发媒体控制指令给房间内其他成员
|
||||
|
||||
@@ -38,16 +38,13 @@ public class QualityMonitorProcessor implements MessageProcessor {
|
||||
private final VideoChatService videoService;
|
||||
private final PushService pushService;
|
||||
|
||||
public boolean supports(String payload) {
|
||||
WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public boolean supports(WSBaseReq req) {
|
||||
return NETWORK_REPORT.eq(req.getType()) ||
|
||||
SCREEN_SHARING.eq(req.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(WebSocketSession session, Long uid, String payload) {
|
||||
WSBaseReq baseReq = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
|
||||
public void process(WebSocketSession session, Long uid, WSBaseReq baseReq) {
|
||||
switch (WSReqTypeEnum.of(baseReq.getType())) {
|
||||
case NETWORK_REPORT -> handleNetworkReport(uid, baseReq);
|
||||
case SCREEN_SHARING -> handleScreenSharing(uid, baseReq);
|
||||
|
||||
@@ -34,17 +34,14 @@ public class RoomAdminProcessor implements MessageProcessor {
|
||||
private final PushService pushService;
|
||||
private final RoomTimeoutService roomTimeoutService;
|
||||
|
||||
public boolean supports(String payload) {
|
||||
WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public boolean supports(WSBaseReq req) {
|
||||
return CLOSE_ROOM.eq(req.getType()) ||
|
||||
KICK_USER.eq(req.getType()) ||
|
||||
MEDIA_MUTE_ALL.eq(req.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(WebSocketSession session, Long uid, String payload) {
|
||||
WSBaseReq baseReq = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
|
||||
public void process(WebSocketSession session, Long uid, WSBaseReq baseReq) {
|
||||
switch (WSReqTypeEnum.of(baseReq.getType())) {
|
||||
case CLOSE_ROOM -> handleCloseRoom(uid, baseReq);
|
||||
case KICK_USER -> handleKickUser(uid, baseReq);
|
||||
|
||||
@@ -53,16 +53,13 @@ public class VideoCallProcessor implements MessageProcessor {
|
||||
private final RoomTimeoutService roomTimeoutService;
|
||||
|
||||
@Override
|
||||
public boolean supports(String payload) {
|
||||
WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public boolean supports(WSBaseReq req) {
|
||||
return VIDEO_CALL_REQUEST.eq(req.getType()) ||
|
||||
VIDEO_CALL_RESPONSE.eq(req.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(WebSocketSession session, Long uid, String payload) {
|
||||
WSBaseReq baseReq = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
|
||||
public void process(WebSocketSession session, Long uid, WSBaseReq baseReq) {
|
||||
switch (WSReqTypeEnum.of(baseReq.getType())) {
|
||||
case VIDEO_CALL_REQUEST -> handleCallRequest(uid, baseReq);
|
||||
case VIDEO_CALL_RESPONSE -> handleCallResponse(uid, baseReq);
|
||||
|
||||
@@ -35,15 +35,13 @@ public class VideoProcessor implements MessageProcessor {
|
||||
private final RoomTimeoutService roomTimeoutService;
|
||||
|
||||
@Override
|
||||
public boolean supports(String payload) {
|
||||
WSBaseReq req = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public boolean supports(WSBaseReq req) {
|
||||
return WEBRTC_SIGNAL.eq(req.getType()) ||
|
||||
VIDEO_HEARTBEAT.eq(req.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(WebSocketSession session, Long uid, String payload) {
|
||||
WSBaseReq baseReq = JSONUtil.toBean(payload, WSBaseReq.class);
|
||||
public void process(WebSocketSession session, Long uid, WSBaseReq baseReq) {
|
||||
|
||||
// 处理不同情况的会话消息
|
||||
switch (WSReqTypeEnum.of(baseReq.getType())) {
|
||||
|
||||
BIN
preview/wx.png
BIN
preview/wx.png
Binary file not shown.
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 141 KiB |
Reference in New Issue
Block a user