Compare commits

...
3 Commits
Author SHA1 Message Date
heavyrian2012 de89b44b21 release note and version 2026-05-10 15:12:17 +08:00
heavyrian2012 44b18a66c0 优化批量添加频道订阅功能 2026-05-10 14:55:55 +08:00
heavyrian2012 42281c3ad3 添加部分频道相关server api 2026-05-10 14:30:59 +08:00
25 changed files with 1000 additions and 43 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
<relativePath>../</relativePath>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>1.4.6</version>
<version>1.4.7</version>
</parent>
<artifactId>moquette-broker</artifactId>
@@ -0,0 +1,41 @@
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputSubscribeChannelBatch;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.hazelcast.util.StringUtil;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import java.util.concurrent.Executor;
@Route(APIPath.Batch_Subscribe_Channel)
@HttpMethod("POST")
public class ChannelSubscriberBatchAction extends AdminAction {
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputSubscribeChannelBatch input = getRequestBody(request.getNettyRequest(), InputSubscribeChannelBatch.class);
if (input != null && !StringUtil.isNullOrEmpty(input.getChannelId()) && input.getUserIds() != null && !input.getUserIds().isEmpty() ) {
ErrorCode errorCode = messagesStore.batchListenChannel(input.getChannelId(), input.getUserIds(), input.getSubscribe()>0);
sendResponse(response, errorCode, null);
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(gson.toJson(result));
}
}
return true;
}
}
@@ -0,0 +1,65 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.PojoGroupInfo;
import cn.wildfirechat.pojos.PojoGroupInfoList;
import cn.wildfirechat.proto.WFCMessage;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import java.util.ArrayList;
import java.util.List;
@Route(APIPath.Group_Batch_Info)
@HttpMethod("POST")
public class GetBatchGroupInfoAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
List<String> inputGetGroup = getRequestBody(request.getNettyRequest(), ArrayList.class);
if (inputGetGroup != null && !inputGetGroup.isEmpty()) {
List<WFCMessage.UserRequest> requests = new ArrayList<>();
for (String s : inputGetGroup) {
requests.add(WFCMessage.UserRequest.newBuilder().setUid(s).build());
}
List<WFCMessage.GroupInfo> groupInfoDataList = messagesStore.getGroupInfos(requests);
PojoGroupInfoList pojoGroupInfoList = new PojoGroupInfoList();
pojoGroupInfoList.groupInfoList = new ArrayList<>();
for (WFCMessage.GroupInfo groupInfo : groupInfoDataList) {
pojoGroupInfoList.groupInfoList.add(PojoGroupInfo.fromProto(groupInfo));
}
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.ok(pojoGroupInfoList);
response.setContent(gson.toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(gson.toJson(result));
}
}
return true;
}
}
@@ -0,0 +1,57 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputCountOffset;
import cn.wildfirechat.pojos.OutputChannelInfoList;
import cn.wildfirechat.pojos.OutputGetChannelInfo;
import cn.wildfirechat.proto.WFCMessage;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.util.internal.StringUtil;
import java.util.ArrayList;
import java.util.List;
@Route(APIPath.List_Channel_Info)
@HttpMethod("POST")
public class GetChannelListAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputCountOffset inputCreateChannel = getRequestBody(request.getNettyRequest(), InputCountOffset.class);
if (inputCreateChannel != null
&& inputCreateChannel.count > 0 && inputCreateChannel.offset >= 0) {
int count = messagesStore.getChannelTotalCount(false);
List<WFCMessage.ChannelInfo> list = messagesStore.getChannelInfoList(inputCreateChannel.count, inputCreateChannel.offset, false);
OutputChannelInfoList outputChannelInfoList = new OutputChannelInfoList();
outputChannelInfoList.total = count;
outputChannelInfoList.list = new ArrayList<>();
for (WFCMessage.ChannelInfo channelData : list) {
outputChannelInfoList.list.add(OutputGetChannelInfo.fromPbInfo(channelData));
}
sendResponse(response, ErrorCode.ERROR_CODE_SUCCESS, outputChannelInfoList);
} else {
sendResponse(response, ErrorCode.INVALID_PARAMETER, null);
}
}
return true;
}
}
@@ -46,25 +46,7 @@ public class GetGroupInfoAction extends AdminAction {
if (groupInfo == null) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
PojoGroupInfo pojoGroupInfo = new PojoGroupInfo();
pojoGroupInfo.setExtra(groupInfo.getExtra());
pojoGroupInfo.setName(groupInfo.getName());
pojoGroupInfo.setOwner(groupInfo.getOwner());
pojoGroupInfo.setPortrait(groupInfo.getPortrait());
pojoGroupInfo.setTarget_id(groupInfo.getTargetId());
pojoGroupInfo.setType(groupInfo.getType());
pojoGroupInfo.setMember_count(groupInfo.getMemberCount());
pojoGroupInfo.setMute(groupInfo.getMute());
pojoGroupInfo.setJoin_type(groupInfo.getJoinType());
pojoGroupInfo.setPrivate_chat(groupInfo.getPrivateChat());
pojoGroupInfo.setSearchable(groupInfo.getSearchable());
pojoGroupInfo.setMax_member_count(groupInfo.getMemberCount());
pojoGroupInfo.setHistory_message(groupInfo.getHistoryMessage());
pojoGroupInfo.setSuper_group(groupInfo.getSuperGroup()>0);
pojoGroupInfo.setDeleted(groupInfo.getDeleted()>0);
pojoGroupInfo.setUpdate_dt(groupInfo.getUpdateDt());
pojoGroupInfo.setMember_update_dt(groupInfo.getMemberUpdateDt());
result = RestResult.ok(pojoGroupInfo);
result = RestResult.ok(PojoGroupInfo.fromProto(groupInfo));
}
setResponseContent(result, response);
} else {
@@ -0,0 +1,49 @@
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputStringCountOffset;
import cn.wildfirechat.pojos.InputSubscribeChannel;
import cn.wildfirechat.pojos.OutputBooleanValue;
import cn.wildfirechat.pojos.PojoTotalList;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import java.util.List;
@Route(APIPath.List_Channel_Subscriber)
@HttpMethod("POST")
public class ListChannelSubscriberAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputStringCountOffset inputSubscribeChannel = getRequestBody(request.getNettyRequest(), InputStringCountOffset.class);
response.setStatus(HttpResponseStatus.OK);
if (inputSubscribeChannel != null
&& !StringUtil.isNullOrEmpty(inputSubscribeChannel.target)
&& inputSubscribeChannel.count > 0) {
int count = messagesStore.getChannelListenerCount(inputSubscribeChannel.target);
List<String> list = messagesStore.getChannelListenerList(inputSubscribeChannel.target, inputSubscribeChannel.count, inputSubscribeChannel.offset);
RestResult result = RestResult.ok(new PojoTotalList(count, list));
response.setContent(gson.toJson(result));
} else {
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(gson.toJson(result));
}
}
return true;
}
}
@@ -0,0 +1,53 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputChatroomMute;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
@Route(APIPath.Chatroom_MuteAll)
@HttpMethod("POST")
public class SetChatroomMuteAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputChatroomMute input = getRequestBody(request.getNettyRequest(), InputChatroomMute.class);
if (input != null
&& !StringUtil.isNullOrEmpty(input.getChatroomId())) {
ErrorCode code = messagesStore.setChatroomState(input.getChatroomId(), input.getStatus());
response.setStatus(HttpResponseStatus.OK);
response.setContent(gson.toJson(RestResult.resultOf(code)));
sendResponse(response, ErrorCode.ERROR_CODE_SUCCESS, null);
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(gson.toJson(result));
}
}
return true;
}
}
@@ -0,0 +1,57 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.UserSettingPojo;
import cn.wildfirechat.proto.WFCMessage;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
@Route(APIPath.User_Get_Setting)
@HttpMethod("POST")
public class UserSettingGetAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
UserSettingPojo input = getRequestBody(request.getNettyRequest(), UserSettingPojo.class);
if (input != null && (!StringUtil.isNullOrEmpty(input.getUserId()))) {
WFCMessage.UserSettingEntry entry = messagesStore.getUserSetting(input.getUserId(), input.getScope(), input.getKey());
response.setStatus(HttpResponseStatus.OK);
RestResult result;
if (entry == null) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
input.setValue(entry.getValue());
result = RestResult.ok(input);
}
response.setContent(gson.toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(gson.toJson(result));
}
}
return true;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.UserSettingPojo;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import static win.liyufan.im.IMTopic.PutUserSettingTopic;
import static win.liyufan.im.UserSettingScope.kUserSettingMyChannels;
@Route(APIPath.User_Put_Setting)
@HttpMethod("POST")
public class UserSettingPutAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
UserSettingPojo userSettingPojo = getRequestBody(request.getNettyRequest(), UserSettingPojo.class);
if (userSettingPojo != null && !StringUtil.isNullOrEmpty(userSettingPojo.getUserId())) {
WFCMessage.ModifyUserSettingReq modifyUserSettingReq = WFCMessage.ModifyUserSettingReq.newBuilder().setScope(userSettingPojo.getScope()).setKey(userSettingPojo.getKey()).setValue(userSettingPojo.getValue()).build();
sendApiMessage(response, userSettingPojo.getUserId(), PutUserSettingTopic, modifyUserSettingReq.toByteArray(), result -> {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
return new Result(errorCode);
});
return false;
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(gson.toJson(result));
}
}
return true;
}
}
@@ -3917,7 +3917,6 @@ public class DatabaseStore {
return null;
}
void persistChannelListener(final String groupId, final List<String> memberList) {
mScheduler.execute(()->{
Connection connection = null;
@@ -4182,6 +4181,279 @@ public class DatabaseStore {
}
}
void batchInsertChannelListener(String channelId, List<String> listeners, boolean listen) throws SQLException {
LOG.info("updateChannelListener {}", channelId);
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
int size = 0;
String sql = listen
? "insert into t_channel_listener (_cid, _mid, _dt) values(?, ?, ?)"
: "delete from t_channel_listener where _cid=? and _mid=?";
statement = connection.prepareStatement(sql);
for (String listener : listeners) {
int index = 1;
statement.setString(index++, channelId);
statement.setString(index++, listener);
if (listen) {
statement.setLong(index++, System.currentTimeMillis());
}
statement.addBatch();
if(++size%100 == 0) {
statement.executeBatch();
statement.clearBatch();
}
}
if(size%100 != 0) {
statement.executeBatch();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e, RDBS_Exception);
throw e;
} finally {
DBUtil.closeDB(connection, statement);
}
}
public List<String> findMissingMids(String cid, List<String> midList, boolean contain) {
if (midList == null || midList.isEmpty()) {
return new ArrayList<>();
}
String tempTable = "temp_check_mids_" + System.currentTimeMillis();
Connection conn = null;
try {
conn = DBUtil.getConnection();
// Step 1: 创建临时表
try (Statement st = conn.createStatement()) {
st.execute("CREATE TEMPORARY TABLE " + tempTable +
" (_mid VARCHAR(64) NOT NULL PRIMARY KEY)");
}
// Step 2: 批量插入
String insertSql = "INSERT IGNORE INTO " + tempTable + " (_mid) VALUES (?)";
try (PreparedStatement ps = conn.prepareStatement(insertSql)) {
int count = 0;
for (String mid : midList) {
ps.setString(1, mid);
ps.addBatch();
if (++count % 1000 == 0) {
ps.executeBatch();
ps.clearBatch();
}
}
if (count % 1000 != 0) {
ps.executeBatch();
}
}
// Step 3: 查询
String querySql = "SELECT t._mid FROM " + tempTable + " t " +
"WHERE " + (contain ? "" : " NOT ") + " EXISTS (" +
" SELECT 1 FROM t_channel_listener c " +
" WHERE c._cid = ? AND c._mid = t._mid" +
")";
List<String> result = new ArrayList<>();
try (PreparedStatement ps = conn.prepareStatement(querySql)) {
ps.setString(1, cid);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
result.add(rs.getString(1));
}
}
}
return result;
} catch (SQLException e) {
throw new RuntimeException("查询缺失mid失败", e);
} finally {
// 显式清理临时表
if (conn != null) {
try (Statement st = conn.createStatement()) {
st.execute("DROP TEMPORARY TABLE IF EXISTS " + tempTable);
} catch (SQLException ignored) {
// 清理失败不影响主流程
} finally {
try { conn.close(); } catch (SQLException ignored) {}
}
}
}
}
List<WFCMessage.ChannelInfo> getChannelInfoList(int count, int offset, boolean withDeleted) {
List<WFCMessage.ChannelInfo> out = new ArrayList<>();
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
try {
connection = DBUtil.getConnection();
String sql = "select _cid, _name" +
", _portrait" +
", _owner" +
", _status" +
", _desc" +
", _extra" +
", _dt from t_channel";
if(!withDeleted) {
sql += " where _status <> 64 ";
}
sql += " order by _dt desc ";
sql += " limit " + count;
if (offset > 0) {
sql += " offset " + offset;
}
statement = connection.prepareStatement(sql);
rs = statement.executeQuery();
while (rs.next()) {
WFCMessage.ChannelInfo.Builder builder = WFCMessage.ChannelInfo.newBuilder();
int index = 1;
String value = rs.getString(index++);
value = (value == null ? "" : value);
builder.setTargetId(value);
value = rs.getString(index++);
value = (value == null ? "" : value);
builder.setName(value);
value = rs.getString(index++);
value = (value == null ? "" : value);
builder.setPortrait(value);
value = rs.getString(index++);
value = (value == null ? "" : value);
builder.setOwner(value);
int status = rs.getInt(index++);
builder.setStatus(status);
value = rs.getString(index++);
value = (value == null ? "" : value);
builder.setDesc(value);
value = rs.getString(index++);
value = (value == null ? "" : value);
builder.setExtra(value);
long longValue = rs.getLong(index++);
builder.setUpdateDt(longValue);
out.add(builder.build());
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e, RDBS_Exception);
} finally {
DBUtil.closeDB(connection, statement, rs);
}
return out;
}
int getChannelTotalCount(boolean withDeleted) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
try {
connection = DBUtil.getConnection();
String sql = "select count(*) from t_channel";
if(!withDeleted) {
sql += " where _status <> 64";
}
statement = connection.prepareStatement(sql);
rs = statement.executeQuery();
if (rs.next()) {
return rs.getInt(1);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e, RDBS_Exception);
} finally {
DBUtil.closeDB(connection, statement, rs);
}
return 0;
}
public int getChannelListenerCount(String channelId) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
try {
connection = DBUtil.getConnection();
String sql = "select count(*) from t_channel_listener where _cid = ?";
statement = connection.prepareStatement(sql);
int index = 1;
statement.setString(index++, channelId);
rs = statement.executeQuery();
if (rs.next()) {
return rs.getInt(1);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e, RDBS_Exception);
} finally {
DBUtil.closeDB(connection, statement, rs);
}
return 0;
}
public List<String> getChannelListenerList(String channelId, int count, int offset) {
List<String> out = new ArrayList<>();
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
try {
connection = DBUtil.getConnection();
String sql = "select _mid from t_channel_listener where _cid = ?";
if(count > 0) {
sql += " limit " + count;
}
if(offset > 0) {
sql += " offset " + offset;
}
statement = connection.prepareStatement(sql);
int index = 1;
statement.setString(index++, channelId);
rs = statement.executeQuery();
while (rs.next()) {
String value = rs.getString(1);
if (!StringUtil.isNullOrEmpty(value)) {
out.add(value);
}
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Utility.printExecption(LOG, e, RDBS_Exception);
} finally {
DBUtil.closeDB(connection, statement, rs);
}
return out;
}
public Set<String> getSensitiveWord() {
Connection connection = null;
PreparedStatement statement = null;
@@ -48,6 +48,7 @@ import win.liyufan.im.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -78,6 +79,7 @@ import static io.moquette.BrokerConstants.*;
import static io.moquette.server.Constants.MAX_CHATROOM_MESSAGE_QUEUE;
import static io.moquette.server.Constants.MAX_MESSAGE_QUEUE;
import static cn.wildfirechat.pojos.MyInfoType.*;
import static win.liyufan.im.IMTopic.PutUserSettingTopic;
import static win.liyufan.im.UserSettingScope.*;
public class MemoryMessagesStore implements IMessagesStore {
@@ -1341,6 +1343,20 @@ public class MemoryMessagesStore implements IMessagesStore {
return false;
}
@Override
public ErrorCode setChatroomState(String chatroomId, int status) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
IMap<String, WFCMessage.ChatroomInfo> chatroomInfoMap = hzInstance.getMap(CHATROOMS);
WFCMessage.ChatroomInfo info = chatroomInfoMap.get(chatroomId);
if (info == null) {
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
info = info.toBuilder().setState(status).build();
chatroomInfoMap.set(chatroomId, info);
return ErrorCode.ERROR_CODE_SUCCESS;
}
@Override
public long insertChatroomMessages(String target, int line, long messageId) {
// messageId是全局的,messageSeq是跟个人相关的,理论上messageId的增长数度远远大于seq。
@@ -2325,6 +2341,19 @@ public class MemoryMessagesStore implements IMessagesStore {
return out;
}
@Override
public List<WFCMessage.GroupInfo> getGroupInfos(List<WFCMessage.UserRequest> requests) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
IMap<String, WFCMessage.GroupInfo> mIMap = hzInstance.getMap(GROUPS_MAP);
ArrayList<WFCMessage.GroupInfo> out = new ArrayList<>();
for (WFCMessage.UserRequest request : requests) {
WFCMessage.GroupInfo groupInfo = mIMap.get(request.getUid());
out.add(groupInfo);
}
return out;
}
@Override
public WFCMessage.GroupInfo getGroupInfo(String groupId) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
@@ -4925,6 +4954,56 @@ public class MemoryMessagesStore implements IMessagesStore {
return getChannelListener(channelId);
}
@Override
public List<WFCMessage.ChannelInfo> getChannelInfoList(int count, int offset, boolean withDeleted) {
return databaseStore.getChannelInfoList(count, offset, withDeleted);
}
@Override
public int getChannelTotalCount(boolean withDeleted) {
return databaseStore.getChannelTotalCount(withDeleted);
}
@Override
public ErrorCode batchListenChannel(String channelId, List<String> userIds, boolean listen) {
IMap<String, WFCMessage.ChannelInfo> mIMap = m_Server.getHazelcastInstance().getMap(CHANNELS);
WFCMessage.ChannelInfo channelData = mIMap.get(channelId);
if(channelData == null || (channelData.getStatus() & Channel_State_Mask_Deleted) > 0) {
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
List<String> missedIds = databaseStore.findMissingMids(channelId, userIds, !listen);
try {
databaseStore.batchInsertChannelListener(channelId, missedIds, listen);
} catch (SQLException e) {
throw new RuntimeException(e);
}
new Thread(() -> {
for (String missedId : missedIds) {
WFCMessage.ModifyUserSettingReq modifyUserSettingReq = WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingListenedChannels).setKey(channelId).setValue(listen? "1" : "0").build();
m_Server.onApiMessage(missedId, null, modifyUserSettingReq.toByteArray(), 0, missedId, PutUserSettingTopic, ProtoConstants.RequestSourceType.Request_From_User);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
return ErrorCode.ERROR_CODE_SUCCESS;
}
@Override
public int getChannelListenerCount(String channelId) {
return databaseStore.getChannelListenerCount(channelId);
}
@Override
public List<String> getChannelListenerList(String channelId, int count, int offset) {
return databaseStore.getChannelListenerList(channelId, count, offset);
}
@Override
public int getOnlineUserCount() {
return m_Server.getProcessor().getConnectionDescriptors().getActiveConnectionsNo();
@@ -113,6 +113,7 @@ public interface IMessagesStore {
ErrorCode modifyGroupMemberAlias(String operator, String groupId, String alias, String memberId, boolean isAdmin);
ErrorCode modifyGroupMemberExtra(String operator, String groupId, String extra, String memberId, boolean isAdmin);
List<WFCMessage.GroupInfo> getGroupInfos(List<WFCMessage.UserRequest> requests, String fromUser, boolean isAdmin);
List<WFCMessage.GroupInfo> getGroupInfos(List<WFCMessage.UserRequest> requests);
WFCMessage.GroupInfo getGroupInfo(String groupId);
Set<String> getUserGroupIds(String userId);
Set<String> getUserGroupIds(String userId, List<Integer> types);
@@ -170,6 +171,7 @@ public interface IMessagesStore {
int getChatroomMemberCount(String chatroomId);
Collection<String> getChatroomMemberClient(String userId);
boolean checkUserClientInChatroom(String user, String clientId, String chatroomId);
ErrorCode setChatroomState(String chatroomId, int status);
long insertChatroomMessages(String target, int line, long messageId);
Collection<UserClientEntry> getChatroomMembers(String chatroomId);
@@ -232,6 +234,12 @@ public interface IMessagesStore {
boolean canSendMessageInChannel(String user, String channelId);
boolean checkUserInChannel(String user, String channelId);
Collection<String> getChannelSubscriber(String channelId);
List<WFCMessage.ChannelInfo> getChannelInfoList(int count, int offset, boolean withDeleted);
int getChannelTotalCount(boolean withDeleted);
ErrorCode batchListenChannel(String channelId, List<String> userIds, boolean listen);
int getChannelListenerCount(String channelId);
List<String> getChannelListenerList(String channelId, int count, int offset);
int getOnlineUserCount();
GetOnlineUserResult getOnlineUsers(int offset, int count);
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>1.4.6</version>
<version>1.4.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -120,8 +120,11 @@ public interface APIPath {
String Create_Channel = "/admin/channel/create";
String Destroy_Channel = "/admin/channel/destroy";
String Get_Channel_Info = "/admin/channel/get";
String List_Channel_Info = "/admin/channel/list";
String Subscribe_Channel = "/admin/channel/subscribe";
String Batch_Subscribe_Channel = "/admin/channel/batch_subscribe";
String Check_User_Subscribe_Channel = "/admin/channel/is_subscribed";
String List_Channel_Subscriber = "/admin/channel/list_subscriber";
String Get_System_Setting = "/admin/system/get_setting";
String Put_System_Setting = "/admin/system/put_setting";
String GET_CUSTOMER = "/admin/customer";
@@ -0,0 +1,16 @@
package cn.wildfirechat.pojos;
public class InputStringCountOffset {
public String target;
public int count;
public int offset;
public InputStringCountOffset() {
}
public InputStringCountOffset(String target, int count, int offset) {
this.target = target;
this.count = count;
this.offset = offset;
}
}
@@ -0,0 +1,50 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
import java.util.List;
public class InputSubscribeChannelBatch {
private String channelId;
private List<String> userIds;
private int subscribe; //1,订阅;0,取消订阅
public InputSubscribeChannelBatch() {
}
public InputSubscribeChannelBatch(String channelId, List<String> userIds, int subscribe) {
this.channelId = channelId;
this.userIds = userIds;
this.subscribe = subscribe;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public List<String> getUserIds() {
return userIds;
}
public void setUserIds(List<String> userIds) {
this.userIds = userIds;
}
public int getSubscribe() {
return subscribe;
}
public void setSubscribe(int subscribe) {
this.subscribe = subscribe;
}
}
@@ -0,0 +1,21 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <heavyrain.lee@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
import cn.wildfirechat.proto.WFCMessage;
import java.util.ArrayList;
import java.util.List;
public class OutputChannelInfoList {
public List<OutputGetChannelInfo> list;
public int total;
}
@@ -8,6 +8,8 @@
package cn.wildfirechat.pojos;
import cn.wildfirechat.proto.WFCMessage;
/**
* 群组信息类
* <p>
@@ -175,4 +177,26 @@ public class PojoGroupInfo {
public void setMember_update_dt(long member_update_dt) {
this.member_update_dt = member_update_dt;
}
static public PojoGroupInfo fromProto(WFCMessage.GroupInfo groupInfo) {
PojoGroupInfo pojoGroupInfo = new PojoGroupInfo();
pojoGroupInfo.setExtra(groupInfo.getExtra());
pojoGroupInfo.setName(groupInfo.getName());
pojoGroupInfo.setOwner(groupInfo.getOwner());
pojoGroupInfo.setPortrait(groupInfo.getPortrait());
pojoGroupInfo.setTarget_id(groupInfo.getTargetId());
pojoGroupInfo.setType(groupInfo.getType());
pojoGroupInfo.setMember_count(groupInfo.getMemberCount());
pojoGroupInfo.setMute(groupInfo.getMute());
pojoGroupInfo.setJoin_type(groupInfo.getJoinType());
pojoGroupInfo.setPrivate_chat(groupInfo.getPrivateChat());
pojoGroupInfo.setSearchable(groupInfo.getSearchable());
pojoGroupInfo.setMax_member_count(groupInfo.getMemberCount());
pojoGroupInfo.setHistory_message(groupInfo.getHistoryMessage());
pojoGroupInfo.setSuper_group(groupInfo.getSuperGroup()>0);
pojoGroupInfo.setDeleted(groupInfo.getDeleted()>0);
pojoGroupInfo.setUpdate_dt(groupInfo.getUpdateDt());
pojoGroupInfo.setMember_update_dt(groupInfo.getMemberUpdateDt());
return pojoGroupInfo;
}
}
@@ -0,0 +1,16 @@
package cn.wildfirechat.pojos;
import java.util.List;
public class PojoTotalList {
public int total;
public List<String> list;
public PojoTotalList() {
}
public PojoTotalList(int total, List<String> list) {
this.total = total;
this.list = list;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
<!--<relativePath>../pom.xml</relativePath>-->
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>1.4.6</version>
<version>1.4.7</version>
</parent>
<artifactId>distribution</artifactId>
+1 -1
View File
@@ -12,7 +12,7 @@
<artifactId>wildfirechat-parent</artifactId>
<packaging>pom</packaging>
<version>1.4.6</version>
<version>1.4.7</version>
<name>Wildfire Chat</name>
<description>Wildfire Chat Server</description>
<inceptionYear>2019</inceptionYear>
+9
View File
@@ -12,6 +12,15 @@
11. *** 1.3.8版本添加了server api发送消息的限制,限制消息体最大内容不能超过64KB,如果升级请注意业务系统发送消息大小。***
# 更新记录
--------------
Release note 1.4.7:
1. 添加获取所有机器人的Server API接口。
2. 添加获取某个电话号码的用户列表的Server API接口。
3. 机器人发送消息distribute为false时不重新分发。
4. 解决机器人和频道API请求错误时result不为空的问题。
5. 机器人添加根据邮箱获取用户信息和批量获取用户信息的接口。
6. 添加批量订阅/取消订阅的频道接口,添加批量获取订阅用户的接口。
--------------
Release note 1.4.6:
1. 添加网盘存储类型。
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>1.4.6</version>
<version>1.4.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -2,9 +2,12 @@ package cn.wildfirechat.sdk;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.pojos.*;
import cn.wildfirechat.pojos.mesh.PojoStringList;
import cn.wildfirechat.sdk.model.IMResult;
import cn.wildfirechat.sdk.utilities.AdminHttpUtils;
import java.util.List;
/**
* 频道管理类
* <p>
@@ -53,6 +56,21 @@ public class ChannelAdmin {
return AdminHttpUtils.httpJsonPost(path, inputChannelId, OutputGetChannelInfo.class);
}
/**
* 获取频道信息列表
* @param count 数量
* @param offset offset
* @return 频道信息列表
* @throws Exception 请求失败时抛出异常
*/
public static IMResult<OutputChannelInfoList> getChannelInfoList(int count, int offset) throws Exception {
String path = APIPath.List_Channel_Info;
InputCountOffset inputCountOffset = new InputCountOffset();
inputCountOffset.count = count;
inputCountOffset.offset = offset;
return AdminHttpUtils.httpJsonPost(path, inputCountOffset, OutputChannelInfoList.class);
}
/**
* 订阅频道
* @param channelId 频道ID
@@ -66,6 +84,19 @@ public class ChannelAdmin {
return AdminHttpUtils.httpJsonPost(path, input, Void.class);
}
/**
* 批量用户订阅频道
* @param channelId 频道ID
* @param userIds 用户ID列表
* @return 订阅结果
* @throws Exception 请求失败时抛出异常
*/
public static IMResult<Void> batchSubscribeChannel(String channelId, List<String> userIds) throws Exception {
String path = APIPath.Batch_Subscribe_Channel;
InputSubscribeChannelBatch input = new InputSubscribeChannelBatch(channelId, userIds, 1);
return AdminHttpUtils.httpJsonPost(path, input, Void.class);
}
/**
* 取消订阅频道
* @param channelId 频道ID
@@ -79,6 +110,19 @@ public class ChannelAdmin {
return AdminHttpUtils.httpJsonPost(path, input, Void.class);
}
/**
* 批量用户取消订阅频道
* @param channelId 频道ID
* @param userIds 用户ID列表
* @return 取消订阅结果
* @throws Exception 请求失败时抛出异常
*/
public static IMResult<Void> batchUnsubscribeChannel(String channelId, List<String> userIds) throws Exception {
String path = APIPath.Batch_Subscribe_Channel;
InputSubscribeChannelBatch input = new InputSubscribeChannelBatch(channelId, userIds, 0);
return AdminHttpUtils.httpJsonPost(path, input, Void.class);
}
/**
* 检查用户是否订阅了频道
* @param userId 用户ID
@@ -91,4 +135,18 @@ public class ChannelAdmin {
InputSubscribeChannel input = new InputSubscribeChannel(channelId, userId, 0);
return AdminHttpUtils.httpJsonPost(path, input, OutputBooleanValue.class);
}
/**
* 获取频道订阅用户列表
* @param channelId 频道ID
* @param count
* @param offset
* @return 订阅用户id列表
* @throws Exception 请求失败时抛出异常
*/
public static IMResult<PojoTotalList> getChannelSubscribers(String channelId, int count, int offset) throws Exception {
String path = APIPath.List_Channel_Subscriber;
InputStringCountOffset input = new InputStringCountOffset(channelId, count, offset);
return AdminHttpUtils.httpJsonPost(path, input, PojoTotalList.class);
}
}
+55 -18
View File
@@ -270,7 +270,7 @@ public class Main {
System.out.println("get user info success");
} else {
System.out.println("get user info by mobile failure");
System.exit(-1);
// System.exit(-1);
}
} else {
System.out.println("get user info by mobile failure");
@@ -1022,7 +1022,7 @@ public class Main {
//验证群组已被解散
resultGetGroupInfo = GroupAdmin.getGroupInfo(groupInfo.getTarget_id());
if (resultGetGroupInfo != null && resultGetGroupInfo.getErrorCode() == ErrorCode.ERROR_CODE_NOT_EXIST) {
if (resultGetGroupInfo != null && (resultGetGroupInfo.getErrorCode() == ErrorCode.ERROR_CODE_NOT_EXIST || resultGetGroupInfo.result.isDeleted())) {
System.out.println("group dismissed verified");
} else {
System.out.println("group dismiss verify failure");
@@ -1187,14 +1187,15 @@ public class Main {
System.out.println("failure");
}
//测试删除多播消息
IMResult<Void> deleteMultiCastResult = MessageAdmin.deleteMultiCastMessage("user1", resultMulticastMessage.result.getMessageUid(), multicastReceivers);
if (deleteMultiCastResult != null && deleteMultiCastResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("delete multicast message success");
} else {
System.out.println("delete multicast message failure");
if (commercialServer) {
//测试删除多播消息
IMResult<Void> deleteMultiCastResult = MessageAdmin.deleteMultiCastMessage("user1", resultMulticastMessage.result.getMessageUid(), multicastReceivers);
if (deleteMultiCastResult != null && deleteMultiCastResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("delete multicast message success");
} else {
System.out.println("delete multicast message failure");
}
}
}
//***********************************************
@@ -1591,6 +1592,40 @@ public class Main {
System.exit(-1);
}
IMResult<OutputChannelInfoList> channelInfoListIMResult = ChannelAdmin.getChannelInfoList(10, 0);
if(channelInfoListIMResult != null && channelInfoListIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("get channel list success");
} else {
System.out.println("get channel list failure");
System.exit(-1);
}
voidIMResult = ChannelAdmin.batchSubscribeChannel(inputCreateChannel.getTargetId(), Arrays.asList("user1", "user2", "user3"));
if(voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("batch subscribe channel success");
} else {
System.out.println("batch subscribe channel failure");
System.exit(-1);
}
IMResult<PojoTotalList> totalListIMResult = ChannelAdmin.getChannelSubscribers(inputCreateChannel.getTargetId(), 10, 1);
if(totalListIMResult != null && totalListIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("get channel subscribe list success");
} else {
System.out.println("get channel subscribe list failure");
System.exit(-1);
}
voidIMResult = ChannelAdmin.batchUnsubscribeChannel(inputCreateChannel.getTargetId(), Arrays.asList("user1", "user2", "user3"));
if(voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("batch unsubscribe channel success");
} else {
System.out.println("batch unsubscribe channel failure");
System.exit(-1);
}
voidIMResult = ChannelAdmin.destroyChannel(inputCreateChannel.getTargetId());
if(voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("success");
@@ -1659,7 +1694,7 @@ public class Main {
//测试用户设置功能
IMResult<Void> setUserSettingResult = GeneralAdmin.setUserSetting("user1", 1, "test_key", "test_value");
IMResult<Void> setUserSettingResult = GeneralAdmin.setUserSetting("user1", 3003, "test_key", "test_value");
if (setUserSettingResult != null && setUserSettingResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("set user setting success");
} else {
@@ -1667,7 +1702,7 @@ public class Main {
System.exit(-1);
}
IMResult<UserSettingPojo> getUserSettingResult = GeneralAdmin.getUserSetting("user1", 1, "test_key");
IMResult<UserSettingPojo> getUserSettingResult = GeneralAdmin.getUserSetting("user1", 3003, "test_key");
if (getUserSettingResult != null && getUserSettingResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS && "test_value".equals(getUserSettingResult.getResult().getValue())) {
System.out.println("get user setting success");
} else {
@@ -2691,13 +2726,15 @@ public class Main {
System.exit(-1);
}
//测试重新发布消息
IMResult<Void> republishResult = channelServiceApi.republishMessage(resultSendMessage.getResult().getMessageUid(), Arrays.asList("userId2", "userId3"));
if (republishResult != null && republishResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("republish message success");
} else {
System.out.println("republish message failure");
System.exit(-1);
if(commercialServer) {
//测试重新发布消息
IMResult<Void> republishResult = channelServiceApi.republishMessage(resultSendMessage.getResult().getMessageUid(), Arrays.asList("userId2", "userId3"));
if (republishResult != null && republishResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("republish message success");
} else {
System.out.println("republish message failure");
System.exit(-1);
}
}
ArticleContent articleContent = new ArticleContent("article1", "https://media.wfcoss.cn/channel-assets/20220816/2dd76540daa9444dae44e942aa1c2bbc.png", "这是一个测试文章", "测试一下文章的功能", "https://mp.weixin.qq.com/s/W6tanLbALd3qqZM8r3MTgA", true);