diff --git a/CHANGELOG.md b/CHANGELOG.md index 449da82..37ef682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,4 +101,6 @@ ### 1.5.0 * fix: Upgrade send noPersist message ### 1.5.1 - * fix: Add connection ack and return nodeId \ No newline at end of file + * fix: Add connection ack and return nodeId +### 1.5.2 + * fix: Add search channel and message method \ No newline at end of file diff --git a/lib/db/channel.dart b/lib/db/channel.dart index bb171eb..141fe6c 100644 --- a/lib/db/channel.dart +++ b/lib/db/channel.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:collection'; import 'dart:convert'; @@ -110,6 +111,52 @@ class ChannelDB { return list; } + Future> search(String keyword) async { + List list = []; + var sql = + "select t.*,cm.member_name,cm.member_remark from (select ${WKDBConst.tableChannel}.*,max( ${WKDBConst.tableChannelMember}.id) mid from ${WKDBConst.tableChannel}, ${WKDBConst.tableChannelMember} where ${WKDBConst.tableChannel}.channel_id=${WKDBConst.tableChannelMember}.channel_id and ${WKDBConst.tableChannel}.channel_type=${WKDBConst.tableChannelMember}.channel_type and (${WKDBConst.tableChannel}.channel_name like ? or ${WKDBConst.tableChannel}.channel_remark like ? or ${WKDBConst.tableChannelMember}.member_name like ? or ${WKDBConst.tableChannelMember}.member_remark like ?) group by ${WKDBConst.tableChannel}.channel_id,${WKDBConst.tableChannel}.channel_type) t,${WKDBConst.tableChannelMember} cm where t.channel_id=cm.channel_id and t.channel_type=cm.channel_type and t.mid=cm.id"; + List> results = await WKDBHelper.shared + .getDB()! + .rawQuery( + sql, ['%$keyword%', '%$keyword%', '%$keyword%', '%$keyword%']); + for (Map data in results) { + var memberName = WKDBConst.readString(data, 'member_name'); + var memberRemark = WKDBConst.readString(data, 'member_remark'); + var channel = WKDBConst.serializeChannel(data); + var result = WKChannelSearchResult(); + result.channel = channel; + if (memberRemark != '') { + if (memberRemark.toUpperCase() == keyword.toUpperCase()) { + result.containMemberName = memberRemark; + } + } + if (result.containMemberName == '') { + if (memberName != '') { + if (memberName.toUpperCase() == keyword.toUpperCase()) { + result.containMemberName = memberName; + } + } + } + list.add(result); + } + return list; + } + + Future> searchWithChannelTypeAndFollow( + String keyword, int channelType, int follow) async { + List list = []; + var sql = + "select * from ${WKDBConst.tableChannel} where (channel_name LIKE ? or channel_remark LIKE ?) and channel_type=? and follow=?"; + List> results = await WKDBHelper.shared + .getDB()! + .rawQuery(sql, ['%$keyword%', '%$keyword%', channelType, follow]); + for (Map data in results) { + var channel = WKDBConst.serializeChannel(data); + list.add(channel); + } + return list; + } + dynamic getMap(WKChannel channel) { var data = HashMap(); data['channel_id'] = channel.channelID; diff --git a/lib/db/message.dart b/lib/db/message.dart index fe22be4..37f4657 100644 --- a/lib/db/message.dart +++ b/lib/db/message.dart @@ -829,6 +829,147 @@ class MessageDB { whereArgs: [channelId, channelType]); } + Future> search(String keyword) async { + List list = []; + var sql = + "select distinct c.*, count(*) message_count, case count(*) WHEN 1 then m.client_seq else ''END client_seq, CASE count(*) WHEN 1 THEN m.searchable_word else '' end searchable_word from ${WKDBConst.tableChannel} c LEFT JOIN ${WKDBConst.tableMessage} m ON m.channel_id = c.channel_id and m.channel_type = c.channel_type WHERE m.is_deleted=0 and searchable_word LIKE ? GROUP BY c.channel_id, c.channel_type ORDER BY m.created_at DESC limit 100"; + List> results = + await WKDBHelper.shared.getDB()!.rawQuery(sql, ['%$keyword%']); + for (Map data in results) { + var channel = WKDBConst.serializeChannel(data); + var message = WKMessageSearchResult(); + message.channel = channel; + message.messageCount = WKDBConst.readInt(data, 'message_count'); + message.searchableWord = WKDBConst.readString(data, 'searchable_word'); + list.add(message); + } + return list; + } + + Future> searchWithChannel( + String keyword, String channelId, int channelType) async { + List list = []; + var sql = + "select * from (select $messageCols,$extraCols from ${WKDBConst.tableMessage} left join ${WKDBConst.tableMessageExtra} on ${WKDBConst.tableMessage}.message_id= ${WKDBConst.tableMessageExtra}.message_id where ${WKDBConst.tableMessage}.searchable_word like ? and ${WKDBConst.tableMessage}.channel_id=? and ${WKDBConst.tableMessage}.channel_type=?) where is_deleted=0 and revoke=0"; + List> results = await WKDBHelper.shared + .getDB()! + .rawQuery(sql, ['%$keyword%', channelId, channelType]); + List fromUIDs = []; + WKChannel? channel = + await WKIM.shared.channelManager.getChannel(channelId, channelType); + + for (Map data in results) { + var msg = WKDBConst.serializeWKMsg(data); + if (channel != null) { + msg.setChannelInfo(channel); + } + if (msg.fromUID != '') { + fromUIDs.add(msg.fromUID); + } + list.add(msg); + } + if (fromUIDs.isNotEmpty) { + List uniqueList = fromUIDs.toSet().toList(); + List wkChannels = await ChannelDB.shared + .queryWithChannelIdsAndChannelType( + uniqueList, WKChannelType.personal); + if (wkChannels.isNotEmpty) { + for (WKChannel channel in wkChannels) { + for (WKMsg msg in list) { + if (msg.fromUID == channel.channelID) { + msg.setFrom(channel); + break; + } + } + } + } + + if (channelType == WKChannelType.group) { + List members = await ChannelMemberDB.shared + .queryMemberWithUIDs(channelId, channelType, uniqueList); + if (members.isNotEmpty) { + for (WKChannelMember member in members) { + for (WKMsg msg in list) { + if (msg.fromUID == member.memberUID) { + msg.setMemberOfFrom(member); + break; + } + } + } + } + } + } + return list; + } + + Future> searchMsgWithChannelAndContentTypes( + String channelID, + int channelType, + int oldestOrderSeq, + int limit, + List contentTypes) async { + var sql = ""; + List list = []; + List arguments = []; + if (oldestOrderSeq <= 0) { + arguments = [channelID, channelType, contentTypes]; + sql = + "select * from (select $messageCols,$extraCols from ${WKDBConst.tableMessage} left join ${WKDBConst.tableMessageExtra} on ${WKDBConst.tableMessage}.message_id=${WKDBConst.tableMessageExtra}.message_id where ${WKDBConst.tableMessage}.channel_id=? and ${WKDBConst.tableMessage}.channel_type=? and ${WKDBConst.tableMessage}.type<>0 and ${WKDBConst.tableMessage}.type<>99 and ${WKDBConst.tableMessage}.type in (${WKDBConst.getPlaceholders(contentTypes.length)})) where is_deleted=0 and revoke=0 order by order_seq desc limit 0,$limit"; + } else { + arguments = [channelID, channelType, oldestOrderSeq, contentTypes]; + sql = + "select * from (select $messageCols,$extraCols from ${WKDBConst.tableMessage} left join ${WKDBConst.tableMessageExtra} on ${WKDBConst.tableMessage}.message_id=${WKDBConst.tableMessageExtra}.message_id where ${WKDBConst.tableMessage}.channel_id=? and ${WKDBConst.tableMessage}.channel_type=? and ${WKDBConst.tableMessage}.order_seq0 and ${WKDBConst.tableMessage}.type<>99 and ${WKDBConst.tableMessage}.type in (${WKDBConst.getPlaceholders(contentTypes.length)})) where is_deleted=0 and revoke=0 order by order_seq desc limit 0,$limit"; + } + List> results = + await WKDBHelper.shared.getDB()!.rawQuery(sql, arguments); + List fromUIDs = []; + WKChannel? channel = + await WKIM.shared.channelManager.getChannel(channelID, channelType); + + for (Map data in results) { + var msg = WKDBConst.serializeWKMsg(data); + if (channel != null) { + msg.setChannelInfo(channel); + } + if (msg.fromUID != '') { + fromUIDs.add(msg.fromUID); + } + list.add(msg); + } + if (fromUIDs.isNotEmpty) { + List uniqueList = fromUIDs.toSet().toList(); + List wkChannels = await ChannelDB.shared + .queryWithChannelIdsAndChannelType( + uniqueList, WKChannelType.personal); + if (wkChannels.isNotEmpty) { + for (WKChannel channel in wkChannels) { + for (WKMsg msg in list) { + if (msg.fromUID == channel.channelID) { + msg.setFrom(channel); + break; + } + } + } + } + + if (channelType == WKChannelType.group) { + List members = await ChannelMemberDB.shared + .queryMemberWithUIDs(channelID, channelType, uniqueList); + if (members.isNotEmpty) { + for (WKChannelMember member in members) { + for (WKMsg msg in list) { + if (msg.fromUID == member.memberUID) { + msg.setMemberOfFrom(member); + break; + } + } + } + } + } + } + return list; + } + dynamic getMap(WKMsg msg) { var map = {}; map['message_id'] = msg.messageID; diff --git a/lib/entity/channel.dart b/lib/entity/channel.dart index 9713204..ab7f4c5 100644 --- a/lib/entity/channel.dart +++ b/lib/entity/channel.dart @@ -52,3 +52,9 @@ class WKChannel { int parentChannelType = 0; WKChannel(this.channelID, this.channelType); } + +class WKChannelSearchResult { + WKChannel? channel; + // 包含的成员名称 + String containMemberName = ''; +} diff --git a/lib/entity/msg.dart b/lib/entity/msg.dart index 099da46..c178ebe 100644 --- a/lib/entity/msg.dart +++ b/lib/entity/msg.dart @@ -302,3 +302,11 @@ class WKSendOptions { int expire = 0; String topicID = ""; } + +class WKMessageSearchResult { + WKChannel? channel; + //包含关键字的信息 + String searchableWord = ""; + //条数 + int messageCount = 0; +} diff --git a/lib/manager/channel_manager.dart b/lib/manager/channel_manager.dart index 9d81e9d..b76f0c8 100644 --- a/lib/manager/channel_manager.dart +++ b/lib/manager/channel_manager.dart @@ -52,6 +52,18 @@ class WKChannelManager { ChannelDB.shared.insertOrUpdateList(list); } + // 全局搜索 + Future> search(String keyword) { + return ChannelDB.shared.search(keyword); + } + + // 搜索已关注channel(好友) + Future> searchWithChannelTypeAndFollow( + String searchKey, int channelType, int follow) { + return ChannelDB.shared + .searchWithChannelTypeAndFollow(searchKey, channelType, follow); + } + addOrUpdateChannel(WKChannel channel) { _updateChannel(channel); _setRefresh(channel); diff --git a/lib/manager/message_manager.dart b/lib/manager/message_manager.dart index 0d70ea7..a32b697 100644 --- a/lib/manager/message_manager.dart +++ b/lib/manager/message_manager.dart @@ -134,6 +134,40 @@ class WKMessageManager { } } + // 全局搜索 + Future> search(String keyword) { + return MessageDB.shared.search(keyword); + } + + /* + * 搜索某个频道到消息 + * + * @param searchKey 关键字 + * @param channelID 频道ID + * @param channelType 频道类型 + * @return List + */ + Future> searchWithChannel( + String keyword, String channelID, int channelType) { + return MessageDB.shared.searchWithChannel(keyword, channelID, channelType); + } + + /* + * 查询某个频道的固定类型消息 + * + * @param channelID 频道ID + * @param channelType 频道列席 + * @param oldestOrderSeq 最后一次消息大orderSeq + * @param limit 每次获取数量 + * @param contentTypes 消息内容类型 + * @return List + */ + Future> searchMsgWithChannelAndContentTypes(String channelID, + int channelType, int oldestOrderSeq, int limit, List contentTypes) { + return MessageDB.shared.searchMsgWithChannelAndContentTypes( + channelID, channelType, oldestOrderSeq, limit, contentTypes); + } + Future getWithClientMsgNo(String clientMsgNo) { return MessageDB.shared.queryWithClientMsgNo(clientMsgNo); } diff --git a/pubspec.yaml b/pubspec.yaml index eba59a1..af6fbd3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ description: wukong IM flutter sdk # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.5.1 +version: 1.5.2 homepage: https://github.com/WuKongIM/WuKongIMFlutterSDK environment: