Compare commits

..

12 Commits
main ... dev

14 changed files with 590 additions and 59 deletions

View File

@ -10,15 +10,28 @@
<listener value="com.zchd.base.AppListener"/>
<listener value="net.tccn.ZhubListener"/>
<source name="int_cache" value="org.redkalex.cache.redis.MyRedisCacheSource">
<node addr="47.106.237.198" password="hello123!" port="6064" db="0"/>
<!-- single -->
<source name="redis" value="com.zchd.base.RedissionCacheSourcex" password="hello#123!">
<node addr="redis://47.106.237.198:6379" db="6"/>
</source>
<source name="str_cache" value="org.redkalex.cache.redis.MyRedisCacheSource">
<node addr="47.106.237.198" password="hello123!" port="6064" db="0"/>
<!-- cluster -->
<!--
<source name="redis" value="com.zchd.base.RedissionCacheSourcex" type="cluster">
<node addr="redis://127.0.0.1:7000"/>
<node addr="redis://127.0.0.1:7001"/>
<node addr="redis://127.0.0.1:7002"/>
</source>
<source name="long_cache" value="org.redkalex.cache.redis.MyRedisCacheSource">
<node addr="47.106.237.198" password="hello123!" port="6064" db="0"/>
-->
<!-- sentinel -->
<!--
<source name="redis" value="com.zchd.base.RedissionCacheSourcex" type="sentinel" master="mymaster">
<node addr="redis://127.0.0.1:26379"/>
<node addr="redis://127.0.0.1:26380"/>
<node addr="redis://127.0.0.1:26381"/>
</source>
-->
</resources>
<server protocol="HTTP" port="8091" maxbody="2m">

View File

@ -6,7 +6,9 @@ javax.level=INFO
com.sun.level=INFO
sun.level=INFO
jdk.level=INFO
java.util.logging.FileHandler.level=FINE
io.netty.level = INFO
org.postgresql.level = INFO
java.util.logging.FileHandler.level=INFO
#10M
java.util.logging.FileHandler.limit=10485760
java.util.logging.FileHandler.count=10000

View File

@ -6,6 +6,9 @@
<persistence-unit name="z_im">
<shared-cache-mode>ALL</shared-cache-mode>
<properties>
<!-- mysql -->
<!--<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://47.106.237.198:3306/z_im?currentSchema=public"/>-->
<!-- postgresql -->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://47.106.237.198:3306/z_im"/>
<property name="javax.persistence.jdbc.user" value="u_im"/>
<property name="javax.persistence.jdbc.password" value="u_im@2024"/>

83
docs/z_im-mysql.sql Normal file
View File

@ -0,0 +1,83 @@
CREATE TABLE `appinfo` (
`appid` varchar(64) NOT NULL DEFAULT '' COMMENT '[平台标识]',
`appname` varchar(64) NOT NULL DEFAULT '' COMMENT '[平台名称]',
`appsecret` varchar(64) NOT NULL DEFAULT '' COMMENT '[校验码]',
`createtime` bigint NOT NULL DEFAULT '0' COMMENT '[创建时间]',
`updatetime` bigint NOT NULL DEFAULT '0' COMMENT '[修改时间]',
`status` smallint NOT NULL DEFAULT '10' COMMENT '[状态]10正常, 40停用80删除',
`checkfriend` smallint NOT NULL DEFAULT '0' COMMENT '[是否检查好友关系1是0否]',
`checkblack` smallint NOT NULL DEFAULT '0' COMMENT '[是否检查拉黑关系1是0否]',
`machineretract` smallint NOT NULL DEFAULT '0' COMMENT '[是否需要机审撤回处理1是0否]',
`hasnext` smallint NOT NULL DEFAULT '0' COMMENT '[是否有下一步流程1是0否]',
PRIMARY KEY (`appid`)
) ENGINE=InnoDB COMMENT='平台信息表';
CREATE TABLE `channelmessage` (
`messageid` varchar(64) NOT NULL DEFAULT '' COMMENT '[消息ID]',
`content` text NOT NULL COMMENT '[消息内容]',
`appid` varchar(8) NOT NULL DEFAULT '' COMMENT '[平台ID]',
`channeltype` varchar(15) NOT NULL DEFAULT '' COMMENT '[平台频道类型]',
`channelvalue` varchar(64) NOT NULL DEFAULT '' COMMENT '[平台频道ID]',
`senduserid` int NOT NULL DEFAULT '0' COMMENT '[发送人]',
`createtime` bigint NOT NULL DEFAULT '0' COMMENT '[创建时间]',
`status` smallint NOT NULL DEFAULT '10' COMMENT '[状态]80删除',
`type` varchar(255) NOT NULL DEFAULT '' COMMENT '[消息类型]',
`title` varchar(255) NOT NULL DEFAULT '' COMMENT '[消息标题]',
`attachment` varchar(2048) NOT NULL DEFAULT '' COMMENT '[消息附加信息]',
PRIMARY KEY (`messageid`) USING BTREE
) ENGINE=InnoDB COMMENT='频道内容表';
CREATE TABLE `channeluser` (
`cuid` varchar(64) NOT NULL DEFAULT '' COMMENT '[用户组ID]',
`appid` varchar(64) NOT NULL DEFAULT '' COMMENT '[平台ID]',
`channeltype` varchar(15) NOT NULL DEFAULT '' COMMENT '[平台频道类型]',
`channelvalue` varchar(64) NOT NULL DEFAULT '' COMMENT '[平台频道ID]',
`userid` int NOT NULL DEFAULT '0' COMMENT '[订阅用户]',
`createtime` bigint NOT NULL DEFAULT '0' COMMENT '[创建时间]',
`lastaccepttime` bigint NOT NULL DEFAULT '0' COMMENT '[最后接收时间]',
`status` smallint NOT NULL DEFAULT '10' COMMENT '[状态]10正常42禁言',
`identitytype` smallint NOT NULL DEFAULT '10' COMMENT '[用户身份0游客10成员20管理员30群主]',
PRIMARY KEY (`cuid`)
) ENGINE=InnoDB COMMENT='用户组信息表';
CREATE TABLE `friendmessage` (
`messageid` varchar(64) NOT NULL DEFAULT '' COMMENT '[私聊ID]发送人-时间戳',
`messagetype` varchar(32) NOT NULL DEFAULT '' COMMENT '[消息类型]',
`content` text NOT NULL COMMENT '[私聊内容]',
`senduserid` int NOT NULL DEFAULT '0' COMMENT '[发送人]',
`receiveuserid` int NOT NULL DEFAULT '0' COMMENT '[接收人]',
`createtime` bigint NOT NULL DEFAULT '0' COMMENT '[创建时间]',
`status` smallint NOT NULL DEFAULT '20' COMMENT '[状态]10已发送20未发送21仅在线发送',
`type` varchar(255) NOT NULL DEFAULT '' COMMENT '[消息类型]',
`title` varchar(255) NOT NULL DEFAULT '' COMMENT '[消息标题]',
`attachment` varchar(2048) NOT NULL DEFAULT '' COMMENT '[消息附加信息]',
`newField` varchar(255) NOT NULL DEFAULT '' COMMENT '[新字段描述]',
PRIMARY KEY (`messageid`)
) ENGINE=InnoDB COMMENT='私聊内容表';
CREATE TABLE `imuser` (
`userid` int NOT NULL COMMENT '[用户标识]',
`guserid` varchar(64) NOT NULL DEFAULT '' COMMENT '[平台自用用户标识]',
`appid` varchar(8) NOT NULL DEFAULT '' COMMENT '[关联平台标识]',
`gender` smallint NOT NULL DEFAULT '0' COMMENT '[性别]',
`nickname` varchar(32) NOT NULL DEFAULT '' COMMENT '[昵称]',
`face` varchar(128) NOT NULL DEFAULT '' COMMENT '[头像]',
`createtime` bigint NOT NULL DEFAULT '0' COMMENT '[创建时间]',
`imtoken` varchar(125) DEFAULT '' COMMENT '[用户认证]',
`updatetime` bigint NOT NULL DEFAULT '0' COMMENT '[修改时间]',
`lastaccepttime` bigint NOT NULL DEFAULT '0' COMMENT '[接收时间]',
`status` smallint NOT NULL DEFAULT '10' COMMENT '[状态]10正常40停用80删除',
PRIMARY KEY (`userid`)
) ENGINE=InnoDB COMMENT='IM用户';
--
INSERT INTO z_im.appinfo (appid, appname, appsecret, createtime, updatetime, status, checkfriend, checkblack, machineretract, hasnext) VALUES
('krlq2lp4', '应用A-开发服', '108319232efb4880bcb4f3323829692c', 0, 0, 10, 0, 0, 0, 0),
('xrkqglth', '应用A-测试服', '108319232efb4880bcb4f3323829692d', 0, 0, 10, 0, 0, 0, 0);
INSERT INTO imuser (userid, guserid, appid, gender, nickname, face, createtime, imtoken, updatetime, lastaccepttime, status) VALUES
(10001, '1784509137732849665', 'xrkqglth', 0, '', '', 1721373487374, 'c75ab6feda394d8c8b28371cda45ec12', 1748780429, 1748780429, 10),
(10002, '1784421470808907778', 'xrkqglth', 0, '', '', 1721377122233, '69ca1dcbebc645b78f84a9e88287382d', 1748780432, 1748780432, 10);

136
docs/z_im-postgresql.sql Normal file
View File

@ -0,0 +1,136 @@
CREATE TABLE appinfo (
appid VARCHAR(64) NOT NULL DEFAULT '',
appname VARCHAR(64) NOT NULL DEFAULT '',
appsecret VARCHAR(64) NOT NULL DEFAULT '',
createtime BIGINT NOT NULL DEFAULT 0,
updatetime BIGINT NOT NULL DEFAULT 0,
status SMALLINT NOT NULL DEFAULT 10,
checkfriend SMALLINT NOT NULL DEFAULT 0,
checkblack SMALLINT NOT NULL DEFAULT 0,
machineretract SMALLINT NOT NULL DEFAULT 0,
hasnext SMALLINT NOT NULL DEFAULT 0,
PRIMARY KEY (appid)
);
COMMENT ON TABLE appinfo IS '平台信息表';
COMMENT ON COLUMN appinfo.appid IS '[平台标识]';
COMMENT ON COLUMN appinfo.appname IS '[平台名称]';
COMMENT ON COLUMN appinfo.appsecret IS '[校验码]';
COMMENT ON COLUMN appinfo.createtime IS '[创建时间]';
COMMENT ON COLUMN appinfo.updatetime IS '[修改时间]';
COMMENT ON COLUMN appinfo.status IS '[状态]10正常, 40停用80删除';
COMMENT ON COLUMN appinfo.checkfriend IS '[是否检查好友关系1是0否]';
COMMENT ON COLUMN appinfo.checkblack IS '[是否检查拉黑关系1是0否]';
COMMENT ON COLUMN appinfo.machineretract IS '[是否需要机审撤回处理1是0否]';
COMMENT ON COLUMN appinfo.hasnext IS '[是否有下一步流程1是0否]';
CREATE TABLE channelmessage (
messageid VARCHAR(64) NOT NULL DEFAULT '',
content TEXT NOT NULL,
appid VARCHAR(8) NOT NULL DEFAULT '',
channeltype VARCHAR(15) NOT NULL DEFAULT '',
channelvalue VARCHAR(64) NOT NULL DEFAULT '',
senduserid INT NOT NULL DEFAULT 0,
createtime BIGINT NOT NULL DEFAULT 0,
status SMALLINT NOT NULL DEFAULT 10,
type VARCHAR(255) NOT NULL DEFAULT '',
title VARCHAR(255) NOT NULL DEFAULT '',
attachment VARCHAR(2048) NOT NULL DEFAULT '',
PRIMARY KEY (messageid)
);
COMMENT ON TABLE channelmessage IS '频道内容表';
COMMENT ON COLUMN channelmessage.messageid IS '[消息ID]';
COMMENT ON COLUMN channelmessage.content IS '[消息内容]';
COMMENT ON COLUMN channelmessage.appid IS '[平台ID]';
COMMENT ON COLUMN channelmessage.channeltype IS '[平台频道类型]';
COMMENT ON COLUMN channelmessage.channelvalue IS '[平台频道ID]';
COMMENT ON COLUMN channelmessage.senduserid IS '[发送人]';
COMMENT ON COLUMN channelmessage.createtime IS '[创建时间]';
COMMENT ON COLUMN channelmessage.status IS '[状态]80删除';
COMMENT ON COLUMN channelmessage.type IS '[消息类型]';
COMMENT ON COLUMN channelmessage.title IS '[消息标题]';
COMMENT ON COLUMN channelmessage.attachment IS '[消息附加信息]';
CREATE TABLE channeluser (
cuid VARCHAR(64) NOT NULL DEFAULT '',
appid VARCHAR(64) NOT NULL DEFAULT '',
channeltype VARCHAR(15) NOT NULL DEFAULT '',
channelvalue VARCHAR(64) NOT NULL DEFAULT '',
userid INT NOT NULL DEFAULT 0,
createtime BIGINT NOT NULL DEFAULT 0,
lastaccepttime BIGINT NOT NULL DEFAULT 0,
status SMALLINT NOT NULL DEFAULT 10,
identitytype SMALLINT NOT NULL DEFAULT 10,
PRIMARY KEY (cuid)
);
COMMENT ON TABLE channeluser IS '用户组信息表';
COMMENT ON COLUMN channeluser.cuid IS '[用户组ID]';
COMMENT ON COLUMN channeluser.appid IS '[平台ID]';
COMMENT ON COLUMN channeluser.channeltype IS '[平台频道类型]';
COMMENT ON COLUMN channeluser.channelvalue IS '[平台频道ID]';
COMMENT ON COLUMN channeluser.userid IS '[订阅用户]';
COMMENT ON COLUMN channeluser.createtime IS '[创建时间]';
COMMENT ON COLUMN channeluser.lastaccepttime IS '[最后接收时间]';
COMMENT ON COLUMN channeluser.status IS '[状态]10正常42禁言';
COMMENT ON COLUMN channeluser.identitytype IS '[用户身份0游客10成员20管理员30群主]';
CREATE TABLE friendmessage (
messageid VARCHAR(64) NOT NULL DEFAULT '',
messagetype VARCHAR(32) NOT NULL DEFAULT '',
content TEXT NOT NULL,
senduserid INT NOT NULL DEFAULT 0,
receiveuserid INT NOT NULL DEFAULT 0,
createtime BIGINT NOT NULL DEFAULT 0,
status SMALLINT NOT NULL DEFAULT 20,
type VARCHAR(255) NOT NULL DEFAULT '',
title VARCHAR(255) NOT NULL DEFAULT '',
attachment VARCHAR(2048) NOT NULL DEFAULT '',
PRIMARY KEY (messageid)
);
COMMENT ON TABLE friendmessage IS '私聊内容表';
COMMENT ON COLUMN friendmessage.messageid IS '[私聊ID]发送人-时间戳';
COMMENT ON COLUMN friendmessage.messagetype IS '[消息类型]';
COMMENT ON COLUMN friendmessage.content IS '[私聊内容]';
COMMENT ON COLUMN friendmessage.senduserid IS '[发送人]';
COMMENT ON COLUMN friendmessage.receiveuserid IS '[接收人]';
COMMENT ON COLUMN friendmessage.createtime IS '[创建时间]';
COMMENT ON COLUMN friendmessage.status IS '[状态]10已发送20未发送21仅在线发送';
COMMENT ON COLUMN friendmessage.type IS '[消息类型]';
COMMENT ON COLUMN friendmessage.title IS '[消息标题]';
COMMENT ON COLUMN friendmessage.attachment IS '[消息附加信息]';
CREATE TABLE imuser (
userid INT NOT NULL,
guserid VARCHAR(64) NOT NULL DEFAULT '',
appid VARCHAR(8) NOT NULL DEFAULT '',
gender SMALLINT NOT NULL DEFAULT 0,
nickname VARCHAR(32) NOT NULL DEFAULT '',
face VARCHAR(128) NOT NULL DEFAULT '',
createtime BIGINT NOT NULL DEFAULT 0,
imtoken VARCHAR(125) DEFAULT '',
updatetime BIGINT NOT NULL DEFAULT 0,
lastaccepttime BIGINT NOT NULL DEFAULT 0,
status SMALLINT NOT NULL DEFAULT 10,
PRIMARY KEY (userid)
);
COMMENT ON TABLE imuser IS 'IM用户';
COMMENT ON COLUMN imuser.userid IS '[用户标识]';
COMMENT ON COLUMN imuser.guserid IS '[平台自用用户标识]';
COMMENT ON COLUMN imuser.appid IS '[关联平台标识]';
COMMENT ON COLUMN imuser.gender IS '[性别]';
COMMENT ON COLUMN imuser.nickname IS '[昵称]';
COMMENT ON COLUMN imuser.face IS '[头像]';
COMMENT ON COLUMN imuser.createtime IS '[创建时间]';
COMMENT ON COLUMN imuser.imtoken IS '[用户认证]';
COMMENT ON COLUMN imuser.updatetime IS '[修改时间]';
COMMENT ON COLUMN imuser.lastaccepttime IS '[接收时间]';
COMMENT ON COLUMN imuser.status IS '[状态]10正常40停用80删除';
--
INSERT INTO z_im.appinfo (appid, appname, appsecret, createtime, updatetime, status, checkfriend, checkblack, machineretract, hasnext) VALUES
('krlq2lp4', '应用A-开发服', '108319232efb4880bcb4f3323829692c', 0, 0, 10, 0, 0, 0, 0),
('xrkqglth', '应用A-测试服', '108319232efb4880bcb4f3323829692d', 0, 0, 10, 0, 0, 0, 0);
INSERT INTO imuser (userid, guserid, appid, gender, nickname, face, createtime, imtoken, updatetime, lastaccepttime, status) VALUES
(10001, '1784509137732849665', 'xrkqglth', 0, '', '', 1721373487374, 'c75ab6feda394d8c8b28371cda45ec12', 1748780429, 1748780429, 10),
(10002, '1784421470808907778', 'xrkqglth', 0, '', '', 1721377122233, '69ca1dcbebc645b78f84a9e88287382d', 1748780432, 1748780432, 10);

27
pom.xml
View File

@ -14,6 +14,7 @@
</properties>
<repositories>
<!-- https://nexus.1216.top/repo/ -->
<repository>
<id>maven-release</id>
<name>maven-nexus</name>
@ -25,19 +26,43 @@
<dependency>
<groupId>org.redkale</groupId>
<artifactId>redkale</artifactId>
<version>2.2.250620</version>
</dependency>
<dependency>
<groupId>org.redkalex</groupId>
<artifactId>redkale-plugins</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.47.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
<version>9.3.0</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.5</version>
</dependency>
<dependency>
<groupId>net.tccn</groupId>
<artifactId>zhub-client-redkale</artifactId>
<version>x.22.0</version>
<exclusions>
<exclusion>
<artifactId>redkale</artifactId>
<groupId>org.redkale</groupId>
</exclusion>
</exclusions>
</dependency>
<!--<dependency>

View File

@ -5,8 +5,8 @@ import org.redkale.convert.json.JsonConvert;
import org.redkale.service.AbstractService;
import org.redkale.service.RetResult;
import org.redkale.source.DataSource;
import org.redkale.util.AnyValue;
import org.redkale.util.Sheet;
import org.redkalex.cache.redis.MyRedisCacheSource;
import javax.annotation.Resource;
import java.io.File;
@ -24,14 +24,13 @@ public class BaseService extends AbstractService {
@Resource(name = "zhub")
protected ZHubClient zhub;
@Resource(name = "int_cache")
protected MyRedisCacheSource<Integer> intCache;
/*@Resource(name = "redis")
protected RedissionCacheSourcex<Integer> intCache;
@Resource(name = "long_cache")
protected MyRedisCacheSource<Long> longCache;
@Resource(name = "str_cache")
protected MyRedisCacheSource<String> strCache;
@Resource(name = "redis")
protected RedissionCacheSourcex<Long> longCache;*/
@Resource(name = "redis")
protected RedissionCacheSourcex redisCache;
@Resource(name = "z_im")
protected DataSource zimSource;
@ -42,6 +41,21 @@ public class BaseService extends AbstractService {
@Resource(name = "APP_NAME")
protected String APP_NAME = "";
@Override
public void init(AnyValue config) {
/*PoolSource<Connection> source = ((DataJdbcSource) zimSource).getReadPoolSource();
// postgresql 设置当前schema
if ("postgresql".equals(source.getDbtype())) {
String url = source.getUrl();
String schema = getCurrentSchema(url);
if (Utils.isEmpty(schema)) {
schema = "public";
}
((DataJdbcSource) zimSource).directExecute(String.format("SET search_path TO %s;", schema));
}*/
}
protected RetResult retError(String info) {
return new RetResult<>(100, info);
}
@ -49,4 +63,21 @@ public class BaseService extends AbstractService {
protected RetResult retError(int code, String info) {
return new RetResult<>(code, info);
}
/**
* 获取 postgresql url当前schema
*
* @param url
* @return
*/
/*protected String getCurrentSchema(String url) {
String[] arr = url.split("currentSchema=");
if (arr.length > 1) {
String[] arr2 = arr[1].split("&");
if (arr2.length > 0) {
return arr2[0];
}
}
return "";
}*/
}

View File

@ -0,0 +1,99 @@
package com.zchd.base;
import org.redisson.Redisson;
import org.redisson.config.BaseConfig;
import org.redisson.config.Config;
import org.redkale.service.Local;
import org.redkale.source.CacheSource;
import org.redkale.util.AnyValue;
import org.redkale.util.AutoLoad;
import org.redkale.util.ResourceType;
import org.redkalex.cache.RedisCacheSource;
import org.redkalex.cache.RedissionCacheSource;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@Local
@AutoLoad(false)
@ResourceType(CacheSource.class)
public class RedissionCacheSourcex<V> extends RedissionCacheSource<V> {
private final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@Override
public void init(AnyValue conf) {
if (this.convert == null) this.convert = this.defaultConvert;
if (conf == null) conf = new AnyValue.DefaultAnyValue();
final List<String> addresses = new ArrayList<>();
Config config = new Config();
AnyValue[] nodes = conf.getAnyValues("node");
String type = conf.getOrDefault("type", "");
String masterName = conf.getOrDefault("master", "");
BaseConfig baseConfig = null;
for (AnyValue node : nodes) {
String addr = node.getValue("addr");
addresses.add(addr);
String db0 = node.getValue("db", "").trim();
if (!db0.isEmpty()) this.db = Integer.valueOf(db0);
if (nodes.length == 1) {
baseConfig = config.useSingleServer();
config.useSingleServer().setAddress(addr);
config.useSingleServer().setDatabase(this.db);
} else if ("masterslave".equalsIgnoreCase(type)) { //主从
baseConfig = config.useMasterSlaveServers();
if (node.get("master") != null) {
config.useMasterSlaveServers().setMasterAddress(addr);
} else {
config.useMasterSlaveServers().addSlaveAddress(addr);
}
config.useMasterSlaveServers().setDatabase(this.db);
} else if ("cluster".equalsIgnoreCase(type)) { //集群
baseConfig = config.useClusterServers();
config.useClusterServers().addNodeAddress(addr);
} else if ("replicated".equalsIgnoreCase(type)) { //
baseConfig = config.useReplicatedServers();
config.useReplicatedServers().addNodeAddress(addr);
config.useReplicatedServers().setDatabase(this.db);
} else if ("sentinel".equalsIgnoreCase(type)) { //
baseConfig = config.useSentinelServers();
config.useSentinelServers().addSentinelAddress(addr);
config.useSentinelServers().setDatabase(this.db);
config.useSentinelServers().setMasterName(masterName);
}
}
if (baseConfig != null) {
String username = conf.getValue("username", "").trim();
String password = conf.getValue("password", "").trim();
String retryAttempts = conf.getValue("retryAttempts", "").trim();
String retryInterval = conf.getValue("retryInterval", "").trim();
if (!username.isEmpty()) baseConfig.setUsername(username);
if (!password.isEmpty()) baseConfig.setPassword(password);
if (!retryAttempts.isEmpty()) baseConfig.setRetryAttempts(Integer.parseInt(retryAttempts));
if (!retryInterval.isEmpty()) baseConfig.setRetryInterval(Integer.parseInt(retryInterval));
}
this.redisson = Redisson.create(config);
this.nodeAddrs = addresses;
if (logger.isLoggable(Level.FINE)) logger.log(Level.FINE, RedisCacheSource.class.getSimpleName() + ": addrs=" + addresses + ", db=" + db);
}
public boolean getBit(String key, int offset) {
return redisson.getBitSet(key).get(offset);
}
public void setBit(String key, int offset, boolean bool) {
redisson.getBitSet(key).set(offset, bool);
}
public <T extends Object> V getHm(String key, T field) {
return (V) redisson.getMap(key).get(field);
}
public void hset(String key, String field, Object value) {
hset(key, field, value.getClass(), value);
}
}

View File

@ -9,16 +9,17 @@ import com.zchd.zim.entity.ChannelUser;
import com.zchd.zim.entity.ImUser;
import net.tccn.zhub.Lock;
import net.tccn.zhub.RpcResult;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.RestMapping;
import org.redkale.net.http.RestService;
import org.redkale.service.RetResult;
import org.redkale.source.*;
import org.redkale.source.ColumnValue;
import org.redkale.source.FilterBean;
import org.redkale.source.FilterNode;
import org.redkale.source.Flipper;
import org.redkale.util.AnyValue;
import org.redkale.util.Comment;
import org.redkale.util.TypeToken;
import org.redkale.util.Utility;
import org.redkalex.cache.redis.MyRedisCacheSource;
import javax.annotation.Resource;
import java.io.Serializable;
@ -30,15 +31,16 @@ import java.util.concurrent.CompletableFuture;
public class ImAccountService extends BaseService {
@Resource(name = "str_cache")
protected MyRedisCacheSource<String> strCache;
/*@Resource(name = "redis")
protected RedissionCacheSourcex<String> strCache;*/
@Resource
protected ImMessageMonitor messageMonitor;
@Resource
protected ImAccountService accountService;
/*@Resource
protected ImAccountService accountService;*/
@Override
public void init(AnyValue config) {
super.init(config);
CompletableFuture.runAsync(() -> {
List<Serializable> list = zimSource.queryColumnList("userid", ImUser.class, new Flipper(1, "userid DESC"), (FilterBean) null);
@ -46,7 +48,8 @@ public class ImAccountService extends BaseService {
if (!list.isEmpty()) {
userid = Kv.toAs(list.get(0), int.class);
}
intCache.set("im:max-userid", userid);
// intCache.set("im:max-userid", userid);
redisCache.setLongAsync("im:max-userid", userid);
});
// 游戏用户注册
@ -93,10 +96,10 @@ public class ImAccountService extends BaseService {
SubscribeBean bean = r.getValue();
String imtoken = bean.getImtoken();
ImUser user = accountService.currentImUser(imtoken);
ImUser user = currentImUser(imtoken);
int userid = user.getUserid();
zimSource.updateColumn(ImUser.class, userid, ColumnValue.create("status", ImUser.STATUS_FREEZE_ACTIVE));
intCache.setBit("im:banned-talk", userid, true);
redisCache.setBit("im:banned-talk", userid, true);
return r.render();
});
@ -106,10 +109,10 @@ public class ImAccountService extends BaseService {
SubscribeBean bean = r.getValue();
String imtoken = bean.getImtoken();
ImUser user = accountService.currentImUser(imtoken);
ImUser user = currentImUser(imtoken);
int userid = user.getUserid();
zimSource.updateColumn(ImUser.class, userid, ColumnValue.create("status", 10));
intCache.setBit("im:banned-talk", userid, false);
redisCache.setBit("im:banned-talk", userid, false);
return r.render();
});
}
@ -117,16 +120,19 @@ public class ImAccountService extends BaseService {
@Comment("获取当前用户ID")
public ImUser currentImUser(String token) {
ImUser user = strCache.hget("im:user-token", token, new TypeToken<ImUser>() {
}.getType());
ImUser user = (ImUser) redisCache.hget("im:user-token", token, ImUser.class);
if (user == null) {
user = zimSource.find(ImUser.class, FilterNode.create("imtoken", token));
strCache.hset("im:user-token", token, JsonConvert.root(), user);
// strCache.hset("im:user-token", token, JsonConvert.root(), user);
redisCache.hset("im:user-token", token, user);
}
return user;
}
public ImUser getImUser(ImBean bean) {
return zimSource.find(ImUser.class, FilterNode.create("guserid", bean.getGuserid()).and("appid", bean.getAppid()));
}
/*@Comment("获取当前用户ID")
public int currentImUserId(String token) {
return currentImUser(token).getUserid();
@ -151,17 +157,20 @@ public class ImAccountService extends BaseService {
if (user == null) {
user = ImUser.buildImUser(bean.getGuserid(), bean.getAppid());
int userid = (int) intCache.incr("im:max-userid");
int userid = (int) redisCache.incr("im:max-userid");
user.setUserid(userid);
user.setImtoken(Utility.uuid()); // 生成IM-TOKEN
zimSource.insert(user);
// intCache.set("im:account-user-token:" + user.getImtoken(), userid);
strCache.hset("im:user-token", user.getImtoken(), JsonConvert.root(), user);
} else {
user.setImtoken(Utility.uuid()); // 生成IM-TOKEN
zimSource.updateColumn(ImUser.class, user.getUserid(), ColumnValue.create("imtoken", user.getImtoken()));
}
//strCache.hset("im:user-token", user.getImtoken(), JsonConvert.root(), user);
redisCache.hset("im:user-token", user.getImtoken(), user);
return RetResult.success(Kv.of("token", user.getImtoken()));
}
@ -190,7 +199,7 @@ public class ImAccountService extends BaseService {
*/
@RestMapping(name = "subscribe", auth = false, comment = "给用户订阅频道信息")
public RetResult subscribe(SubscribeBean bean) {
ImUser user = accountService.currentImUser(bean.getImtoken());
ImUser user = currentImUser(bean.getImtoken());
int userid = user.getUserid();
Lock lock = zhub.lock("im:channel-create:" + userid, 500);
try {
@ -220,7 +229,7 @@ public class ImAccountService extends BaseService {
*/
@RestMapping(name = "unsubscribe", auth = false, comment = "取消频道订阅")
public RetResult unsubscribe(SubscribeBean bean) {
ImUser user = accountService.currentImUser(bean.getImtoken());
ImUser user = currentImUser(bean.getImtoken());
int userid = user.getUserid();

View File

@ -3,6 +3,9 @@ package com.zchd.zim;
import com.zchd.base.BaseService;
import com.zchd.base.util.Kv;
import com.zchd.base.util.QueueTask;
import com.zchd.base.util.Utils;
import com.zchd.zim.bean.ImBean;
import com.zchd.zim.bean.MessageBean;
import com.zchd.zim.entity.ChannelMessage;
import com.zchd.zim.entity.ChannelUser;
import com.zchd.zim.entity.FriendMessage;
@ -11,12 +14,14 @@ import net.tccn.timer.Timers;
import net.tccn.zhub.ZHubClient;
import org.redkale.net.http.RestService;
import org.redkale.net.http.WebSocketNode;
import org.redkale.service.RetResult;
import org.redkale.source.ColumnValue;
import org.redkale.source.DataSource;
import org.redkale.source.FilterNode;
import org.redkale.source.Flipper;
import org.redkale.util.AnyValue;
import org.redkale.util.Comment;
import org.redkale.util.TypeToken;
import javax.annotation.Resource;
import javax.persistence.Transient;
@ -39,11 +44,52 @@ public class ImChatService extends BaseService {
@Transient //消息存储
protected QueueTask<Runnable> dbQueue = new QueueTask<>(1);
@Resource
private ImAccountService accountService;
@Override
public void init(AnyValue config) {
msgQueue.init(logger, Runnable::run);
dbQueue.init(logger, Runnable::run);
zhub.rpcSubscribe("im:send-message", new TypeToken<MessageBean>() {
}, r -> {
MessageBean bean = r.getValue();
/*// 当指定点对点消息类型的时候消息类型必须明确
if (!Utils.isEmpty(bean.getGuserid()) && Utils.isEmpty(bean.getMessagetype())) {
return r.retError("发送消息类型未明确");
}*/
if (Utils.isEmpty(bean.getGuserid()) && bean.getReceivechannel() == null) {
return r.retError("发送目标未明确");
}
// 发送点对点消息
if (!Utils.isEmpty(bean.getGuserid())) {
ImBean imBean = bean.buildImBean();
ImUser imUser = accountService.getImUser(imBean);
// 接收人未注册 IM 信息为其注册信息
if (imUser == null) {
RetResult<Kv> result = accountService.register(imBean);
if (!result.isSuccess()) {
return r.retError(result.getRetinfo());
}
imUser = accountService.getImUser(imBean);
}
FriendMessage message = bean.buildFriendMessage(0, imUser.getUserid());
sendFriendMessage(message);
}
// 发送群组消息
if (bean.getReceivechannel() != null) {
ChannelMessage message = bean.buildChannelMessage(0); // 发送者0系统发送
sendChannelMessage(message);
}
return r.render();
});
}
@Comment("发送私聊消息")
@ -56,7 +102,8 @@ public class ImChatService extends BaseService {
// intCache.incrHm("im:heartbeat:" + uid, "messagecount", -1);
// 更新状态失败 200ms 后重试当数据未写入的时候更新失败
Timers.tryDelay(() -> {
FilterNode node = FilterNode.create("senduserid", bean.getSenduserid()).and("receiveuserid", uid);
// FilterNode node = FilterNode.create("senduserid", bean.getSenduserid()).and("receiveuserid", uid);
FilterNode node = FilterNode.create("messageid", bean.getMessageid());
int updateColumn = zimSource.updateColumn(FriendMessage.class, node, ColumnValue.create("status", 10));
return updateColumn != 0;
@ -81,7 +128,7 @@ public class ImChatService extends BaseService {
// 拉取频道离线消息
List<ChannelUser> userGroups = userChannels(userid);
for (ChannelUser userGroup : userGroups) {
Long lastAcceptTime = longCache.getHm("im:channel:" + userGroup.buildChannelid(), userGroup.getUserid());
Long lastAcceptTime = redisCache.hgetLong("im:channel:" + userGroup.buildChannelid(), userGroup.getUserid() + "", 0);
if (lastAcceptTime == null || lastAcceptTime == 0) {
lastAcceptTime = userGroup.getCreatetime();
}
@ -93,7 +140,8 @@ public class ImChatService extends BaseService {
.and("senduserid", NOTEQUAL, userid)
.and("createtime", GREATERTHAN, lastAcceptTime)
.and("createtime", LESSTHAN, System.currentTimeMillis())
.and("status", NOTEQUAL, 80);
.and("status", NOTEQUAL, 80)
.and("status", NOTEQUAL, 21);
try {
List<ChannelMessage> messages = zimSource.queryList(ChannelMessage.class, new Flipper(50, "createtime desc"), node1);
@ -110,7 +158,7 @@ public class ImChatService extends BaseService {
}
} finally {
longCache.setHm("im:channel:" + userGroup.buildChannelid(), userGroup.getUserid(), lastAcceptTime);
redisCache.hsetLong("im:channel:" + userGroup.buildChannelid(), userGroup.getUserid() + "", lastAcceptTime);
// 变更最后拉取时间
/*FilterNode node = FilterNode.create("appid", userGroup.getAppid())
.and("channeltype", userGroup.getChanneltype())
@ -129,7 +177,7 @@ public class ImChatService extends BaseService {
@Comment("用户IM是否在线")
public boolean userOnline(int userid) {
return intCache.getBit("im:user", userid);
return redisCache.getBit("im:user", userid);
}
@Comment("用户在线直接发送, 如果不在线先记录到数据库")
@ -153,20 +201,26 @@ public class ImChatService extends BaseService {
zhub.broadcast("im:channel:" + message.buildChannelid(), message);
}
private String getGuserid(int userid) {
protected String getGuserid(int userid) {
if (userid == 0) {
return "0";
}
ImUser user = zimSource.find(ImUser.class, userid);
return user.getGuserid();
}
public int getUserid(String appid, String guserid) {
ImUser user = zimSource.find(ImUser.class, FilterNode.create("appid", appid).and("guserid", guserid));
if (user == null) {
return 0;
}
return user.getUserid();
}
public Kv buildFriendMessageDeail(FriendMessage message) {
Kv detail = Kv.toKv(message, "content", "messageid", "sendtime");
detail.set("sendguserid", getGuserid(message.getSenduserid()));
Kv data = Kv.of("detail", detail).set("type", "friend-text");
Kv data = Kv.of("detail", detail).set("type", message.getMessagetype());
return data;
}

View File

@ -1,11 +1,13 @@
package com.zchd.zim;
import com.zchd.base.RedissionCacheSourcex;
import com.zchd.base.info.SwearWordService;
import com.zchd.base.util.Kv;
import com.zchd.base.util.Utils;
import com.zchd.zim.entity.ChannelMessage;
import com.zchd.zim.entity.FriendMessage;
import com.zchd.zim.entity.ImUser;
import net.tccn.zhub.ZHubClient;
import org.redkale.net.http.HttpRequest;
import org.redkale.net.http.RestOnMessage;
import org.redkale.net.http.RestWebSocket;
@ -13,7 +15,6 @@ import org.redkale.net.http.WebSocket;
import org.redkale.service.RetResult;
import org.redkale.source.DataSource;
import org.redkale.util.Comment;
import org.redkalex.cache.redis.MyRedisCacheSource;
import javax.annotation.Resource;
import java.util.Map;
@ -27,9 +28,11 @@ public class ImChatWebSocket extends WebSocket {
@Resource(name = "z_im")
protected DataSource zimSource;
@Resource(name = "redis")
protected RedissionCacheSourcex redisCache;
@Resource(name = "int_cache")
protected MyRedisCacheSource<Integer> intCache;
@Resource(name = "zhub")
protected ZHubClient zhub;
@Resource
protected ImAccountService accountService;
@ -74,7 +77,9 @@ public class ImChatWebSocket extends WebSocket {
return CompletableFuture.runAsync(() -> {
final int userid = (int) getUserid();
getLogger().info("im:state-change:" + userid + "---ws connected---");
intCache.setBit("im:user", userid, true);
redisCache.setBit("im:user", userid, true);
// 推送上线状态
zhub.publish("im:online:" + getAttribute("appid"), getAttribute("guserid"));
// 上线开启订阅
messageMonitor.online(userid);
// 拉取离线消息
@ -95,10 +100,14 @@ public class ImChatWebSocket extends WebSocket {
String mck = extmap.get("mck");
int targetguserid = chatService.getUserid(appid, guserid);
if (targetguserid == 0) {
sendTip("用户不存在", mck, 4001);
return;
}
FriendMessage message = FriendMessage.buildFriendMessage(bean.get("content"), userid, targetguserid);
// 禁言检查
if (intCache.getBit("im:banned-talk", userid)) {
if (redisCache.getBit("im:banned-talk", userid)) {
sendTip("发送失败,你已被禁言", mck, 3002);
return;
}
@ -133,7 +142,7 @@ public class ImChatWebSocket extends WebSocket {
String mck = extmap.get("mck");
// 禁言检查
if (intCache.getBit("im:banned-talk", userid)) {
if (redisCache.getBit("im:banned-talk", userid)) {
sendTip("发送失败,你已被禁言", mck, 3002);
return;
}
@ -166,7 +175,9 @@ public class ImChatWebSocket extends WebSocket {
public CompletableFuture onClose(int code, String reason) {
final int userid = (int) getUserid();
getLogger().info("im:state-change:" + userid + "---close---" + code + "---" + reason);
intCache.setBit("im:user:", userid, false);
redisCache.setBit("im:user", userid, false);
// 推送离线状态
zhub.publish("im:offline:" + getAttribute("appid"), getAttribute("guserid"));
// 取消用户订阅
messageMonitor.offline(userid);
return null;

View File

@ -13,7 +13,6 @@ import org.redkale.util.AnyValue;
import org.redkale.util.Comment;
import org.redkale.util.TypeToken;
import org.redkale.util.Utility;
import org.redkalex.cache.redis.MyRedisCacheSource;
import javax.annotation.Resource;
import java.util.BitSet;
@ -32,11 +31,6 @@ public class ImMessageMonitor extends BaseService {
@Resource(name = "im_chat")
protected WebSocketNode wsnode;
@Resource(name = "int_cache")
protected MyRedisCacheSource<Integer> intCache;
@Resource(name = "long_cache")
protected MyRedisCacheSource<Long> longCache;
protected BitSet bitSet = new BitSet(); // IM用户连接当前实例记录
protected ConcurrentHashMap<String, Set<Integer>> channelSubscribe = new ConcurrentHashMap<>();
@ -122,7 +116,7 @@ public class ImMessageMonitor extends BaseService {
}
// todo并发场景下会影响拉取离线消息
longCache.setHm("im:channel:" + x.buildChannelid(), uid, System.currentTimeMillis());
redisCache.hset("im:channel:" + x.buildChannelid(), uid + "", System.currentTimeMillis());
});
});
});

View File

@ -0,0 +1,68 @@
package com.zchd.zim.bean;
import com.zchd.base.util.Utils;
import com.zchd.zim.entity.AppInfo;
import com.zchd.zim.entity.ChannelMessage;
import com.zchd.zim.entity.FriendMessage;
import lombok.Getter;
import lombok.Setter;
import org.redkale.util.Comment;
import javax.persistence.Column;
@Setter
@Getter
public class MessageBean {
@Column(comment = "[消息内容]")
private String content = "";
@Column(comment = "[平台信息]{appid, appsecret}")
private AppInfo appinfo;
@Column(comment = "[消息类型]用于点对点模式下的消息类别标注")
private String messagetype = "";
@Column(comment = "[接收人]")
private String guserid;
@Comment("[接收群组]{channeltype, channelvalue}")
private SubscribeBean receivechannel;
@Column(comment = "[仅在线接收]0否、1是")
private short onlinereceive = 0;
public FriendMessage buildFriendMessage(int senduserid, int userid) {
FriendMessage message = new FriendMessage();
message.setContent(content);
message.setMessagetype(messagetype);
message.setSenduserid(senduserid);
message.setReceiveuserid(userid);
message.setCreatetime(System.currentTimeMillis());
message.setStatus(onlinereceive == 1 ? (short) 21 : 20);
message.setMessageid(Utils.fmt36(senduserid) + "-" + Utils.fmt36(message.getCreatetime()));
return message;
}
public ChannelMessage buildChannelMessage(int senduserid) {
ChannelMessage message = new ChannelMessage();
message.setContent(content);
message.setAppid(appinfo.getAppid());
message.setSenduserid(senduserid);
message.setChanneltype(receivechannel.getChanneltype());
message.setChannelvalue(receivechannel.getChannelvalue());
message.setCreatetime(System.currentTimeMillis());
message.setStatus(onlinereceive == 1 ? (short) 21 : 20);
message.setMessageid(Utils.fmt36(message.getSenduserid()) + "-" + Utils.fmt36(message.getCreatetime()));
return message;
}
public ImBean buildImBean() {
ImBean bean = new ImBean();
bean.setAppid(appinfo.getAppid());
bean.setAppsecret(appinfo.getAppsecret());
bean.setGuserid(guserid);
return bean;
}
}

View File

@ -17,6 +17,9 @@ public class FriendMessage {
@Column(comment = "[私聊ID]发送人-时间戳")
private String messageid = "";
@Column(comment = "[消息类型]")
private String messagetype = "friend-text";
@Column(comment = "[私聊内容]")
private String content = "";
@ -29,7 +32,7 @@ public class FriendMessage {
@Column(comment = "[创建时间]")
private long createtime;
@Column(comment = "[状态]10已发送20未发送")
@Column(comment = "[状态]10已发送20未发送21仅在线发送")
private short status;
public static FriendMessage buildFriendMessage(String content, int senduserid, int receiveuserid) {