fix: 注册后推送群成员信息事件、登录时更新imUser的物理地址并返回给userInfo、调整个人信息修改
1
luohuo-cloud/install/docker/.env
Normal file
@@ -0,0 +1 @@
|
||||
NACOS_VERSION=v3.0.2
|
||||
97
luohuo-cloud/install/docker/docker-compose.yml
Normal file
@@ -0,0 +1,97 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
nacos:
|
||||
image: nacos/nacos-server:${NACOS_VERSION}
|
||||
container_name: nacos-standalone-mysql
|
||||
env_file:
|
||||
- ./env/nacos-standalone-mysql.env
|
||||
volumes:
|
||||
- ./nacos/standalone-logs/:/home/nacos/logs
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "8848:8848"
|
||||
- "9848:9848"
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
restart: always
|
||||
mysql:
|
||||
container_name: mysql
|
||||
restart: always
|
||||
image: mysql:8.0.30
|
||||
env_file:
|
||||
- ./env/mysql.env
|
||||
volumes:
|
||||
- ./mysql/data:/var/lib/mysql
|
||||
ports:
|
||||
- "13306:3306"
|
||||
healthcheck:
|
||||
test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
redis:
|
||||
image: redis:latest
|
||||
container_name: redis
|
||||
restart: always
|
||||
ports:
|
||||
- '16379:6379'
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
|
||||
- ./redis/logs:/logs
|
||||
#配置文件启动
|
||||
command: redis-server /usr/local/etc/redis/redis.conf --requirepass luo123456
|
||||
# rocketmq
|
||||
rocketmq-namesrv:
|
||||
image: apache/rocketmq:5.3.2
|
||||
container_name: rocketmq-namesrv
|
||||
ports:
|
||||
- 9876:9876
|
||||
command: sh mqnamesrv
|
||||
volumes:
|
||||
- ./rocketmq/namesrv/logs:/home/rocketmq/logs
|
||||
- ./rocketmq/namesrv/store:/home/rocketmq/store
|
||||
rocketmq-broker:
|
||||
image: apache/rocketmq:5.3.2
|
||||
container_name: rocketmq-broker
|
||||
ports:
|
||||
- 10909:10909
|
||||
- 10911:10911
|
||||
- 10912:10912
|
||||
environment:
|
||||
- NAMESRV_ADDR=rocketmq-namesrv:9876
|
||||
volumes:
|
||||
- ./rocketmq/broker/logs:/home/rocketmq/logs
|
||||
- ./rocketmq/broker/store:/home/rocketmq/store
|
||||
- ./rocketmq/broker/conf/broker.conf:/home/rocketmq/rocketmq-5.3.2/conf/broker.conf
|
||||
- ./rocketmq/broker/conf/plain_acl.yml:/home/rocketmq/rocketmq-5.3.2/conf/plain_acl.yml
|
||||
depends_on:
|
||||
- rocketmq-namesrv
|
||||
command: sh mqbroker -c /home/rocketmq/rocketmq-5.3.2/conf/broker.conf
|
||||
rocketmq-proxy:
|
||||
image: apache/rocketmq:5.3.2
|
||||
container_name: rocketmq-proxy
|
||||
depends_on:
|
||||
- rocketmq-broker
|
||||
- rocketmq-namesrv
|
||||
ports:
|
||||
- 8082:8080
|
||||
- 8081:8081
|
||||
restart: on-failure
|
||||
environment:
|
||||
- NAMESRV_ADDR=rocketmq-namesrv:9876
|
||||
command: sh mqproxy
|
||||
|
||||
jenkins:
|
||||
image: jenkins/jenkins:lts-jdk21
|
||||
container_name: jenkins
|
||||
user: root
|
||||
restart: always
|
||||
ports:
|
||||
- "20000:8080" # Web UI 端口
|
||||
- "50000:50000" # Agent 连接端口
|
||||
volumes:
|
||||
- /home/jenkins/work:/var/jenkins_home # 数据持久化
|
||||
- /var/run/docker.sock:/var/run/docker.sock # 宿主机 Docker 控制
|
||||
- /usr/bin/docker:/usr/bin/docker # 宿主机 Docker 命令
|
||||
12
luohuo-cloud/install/docker/env/mysql.env
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# mysql root 密码
|
||||
MYSQL_ROOT_PASSWORD=123456
|
||||
# 创建nacos 账号和密码
|
||||
MYSQL_DATABASE=nacos
|
||||
MYSQL_USER=nacos
|
||||
MYSQL_PASSWORD=n2SWa3j45a6AdmZE
|
||||
LANG=C.UTF-8
|
||||
# 允许远程访问
|
||||
MYSQL_ROOT_HOST=%
|
||||
# 支持utf8mb4字符集
|
||||
MYSQL_CHARSET=utf8mb4
|
||||
MYSQL_COLLATION=utf8mb4_unicode_ci
|
||||
15
luohuo-cloud/install/docker/env/nacos-standalone-mysql.env
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# 这里要用mysql 的容器名称
|
||||
PREFER_HOST_MODE=mysql
|
||||
MODE=standalone
|
||||
SPRING_DATASOURCE_PLATFORM=mysql
|
||||
MYSQL_SERVICE_HOST=mysql
|
||||
MYSQL_SERVICE_DB_NAME=nacos
|
||||
# 端口配置docker 内部的端口,不用配置宿主机的端口,如果配置宿主机的端口,那么PREFER_HOST_MODE 需要配置host.docker.internal
|
||||
MYSQL_SERVICE_PORT=3306
|
||||
MYSQL_SERVICE_USER=nacos
|
||||
# 密码必须要和mysql中创建的密码一致
|
||||
MYSQL_SERVICE_PASSWORD=n2SWa3j45a6AdmZE
|
||||
MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
NACOS_AUTH_IDENTITY_KEY=2222
|
||||
NACOS_AUTH_IDENTITY_VALUE=2xxx
|
||||
NACOS_AUTH_TOKEN=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=
|
||||
6
luohuo-cloud/install/docker/env/redis.env
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
REDIS_PASSWORD="luo123456" # 16位复杂度与mysql业务密码同级
|
||||
REDIS_DATABASES=0 # 默认数据库数量
|
||||
REDIS_MAXMEMORY=2gb # 内存限制
|
||||
REDIS_APPENDONLY=yes # 启用AOF持久化
|
||||
REDIS_PROTECTED_MODE=yes # 保护模式
|
||||
REDIS_RENAME_COMMANDS="FLUSHDB:_,FLUSHALL:_,CONFIG:_" # 禁用危险命令
|
||||
0
luohuo-cloud/install/docker/redis/redis.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
## 📝 修改环境配置
|
||||
|
||||
上传目录下的docker文件夹到服务器/home/docker下面, 需要修改env文件夹rocketmq文件夹里面的配置, 特别是[broker.conf](docker/rocketmq/broker/conf/broker.conf)里面brokerIP1的值
|
||||
|
||||
|
||||
## 🛠️ 启动命令
|
||||
- **仔细阅读**: docker-compose.yml 的内容(redis的密码也在这里面设置的)、./env 文件夹下面的内容,里面包含了账号密码等信息
|
||||
- **打开目录**: 当前文件夹下输入 cmd 回车
|
||||
- **执行命令**: docker-compose up -d
|
||||
- **导入nacos数据库**: [mysql-schema.sql](../mysql-schema.sql)
|
||||
- **导入nacos命名空间数据**: [nacos_config_export_20250816090745.zip](../../nacos/nacos_config_export_20250816090745.zip)
|
||||
105
luohuo-cloud/install/docker/rocketmq/broker/conf/broker.conf
Normal file
@@ -0,0 +1,105 @@
|
||||
#所属集群名字
|
||||
brokerClusterName=DefaultCluster
|
||||
|
||||
# 启用Proxy模式(可选,默认NONE)
|
||||
proxyMode=NONE
|
||||
|
||||
# 启用Controller模式(集群部署需配置)
|
||||
enableControllerMode=false
|
||||
|
||||
#broker名字,注意此处不同的配置文件填写的不一样,如果在broker-a.properties使用:broker-a,
|
||||
#在broker-b.properties使用:broker-b
|
||||
brokerName=broker-a
|
||||
|
||||
#0 表示Master,>0 表示Slave
|
||||
brokerId=0
|
||||
|
||||
#nameServer地址,分号分割
|
||||
#namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876
|
||||
namesrvAddr=rocketmq-namesrv:9876
|
||||
|
||||
#启动IP,如果 docker 报 com.alibaba.rocketmq.remoting.exception.RemotingConnectException: connect to <192.168.0.120:10909> failed
|
||||
# 解决方式1 加上一句producer.setVipChannelEnabled(false);,解决方式2 brokerIP1 设置宿主机IP,不要使用docker 内部IP
|
||||
brokerIP1=rocketmq-broker
|
||||
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
|
||||
defaultTopicQueueNums=4
|
||||
|
||||
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭 !!!这里仔细看是false,false,false
|
||||
autoCreateTopicEnable=true
|
||||
enableAutoCreateSystemTopic=true
|
||||
# 必须设置为true(与proxy协调)
|
||||
enableAutoCreateSubscriptionGroup=true
|
||||
|
||||
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
|
||||
autoCreateSubscriptionGroup=true
|
||||
|
||||
#Broker 对外服务的监听端口
|
||||
listenPort=10911
|
||||
|
||||
#此参数控制是否开启密码,不开启可设置false
|
||||
aclEnable=false
|
||||
|
||||
#删除文件时间点,默认凌晨4点
|
||||
deleteWhen=04
|
||||
|
||||
#文件保留时间,默认48小时
|
||||
fileReservedTime=120
|
||||
|
||||
#commitLog每个文件的大小默认1G
|
||||
mappedFileSizeCommitLog=1073741824
|
||||
|
||||
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
|
||||
mappedFileSizeConsumeQueue=300000
|
||||
|
||||
#destroyMapedFileIntervalForcibly=120000
|
||||
#redeleteHangedFileInterval=120000
|
||||
#检测物理文件磁盘空间
|
||||
diskMaxUsedSpaceRatio=88
|
||||
#存储路径
|
||||
storePathRootDir=/home/rocketmq/store
|
||||
#commitLog 存储路径
|
||||
storePathCommitLog=/home/rocketmq/store/commitlog
|
||||
#消费队列存储
|
||||
storePathConsumeQueue=/home/rocketmq/store/consumequeue
|
||||
#消息索引存储路径
|
||||
storePathIndex=/home/rocketmq/store/index
|
||||
#checkpoint 文件存储路径
|
||||
storeCheckpoint=/home/rocketmq/store/checkpoint
|
||||
#abort 文件存储路径
|
||||
abortFile=/home/rocketmq/store/abort
|
||||
#限制的消息大小
|
||||
maxMessageSize=65536
|
||||
|
||||
# 延迟消息配置
|
||||
#timerWheelEnable=false
|
||||
#enableScheduleMessage=false
|
||||
|
||||
# 确保此目录存在且可写
|
||||
#timerStorePath=/home/rocketmq/store/timerwheel
|
||||
#timerFlushIntervalMs=1000
|
||||
#timerPrecisionMs=1000
|
||||
#scheduleMessageServiceThreadPoolNums=4
|
||||
|
||||
#flushCommitLogLeastPages=4
|
||||
#flushConsumeQueueLeastPages=2
|
||||
#flushCommitLogThoroughInterval=10000
|
||||
#flushConsumeQueueThoroughInterval=60000
|
||||
|
||||
#Broker 的角色
|
||||
#- ASYNC_MASTER 异步复制Master
|
||||
#- SYNC_MASTER 同步双写Master
|
||||
#- SLAVE
|
||||
brokerRole=ASYNC_MASTER
|
||||
|
||||
#刷盘方式
|
||||
#- ASYNC_FLUSH 异步刷盘 SYNC_FLUSH 同步刷盘
|
||||
flushDiskType=ASYNC_FLUSH
|
||||
|
||||
# 堆外内存限制(需与 JVM 参数一致)
|
||||
maxDirectMemorySize=1g
|
||||
|
||||
#发消息线程池数量
|
||||
#sendMessageThreadPoolNums=128
|
||||
#拉消息线程池数量
|
||||
#pullMessageThreadPoolNums=128
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
globalWhiteRemoteAddresses:
|
||||
# - 47.100.93.*
|
||||
# - 156.254.120.*
|
||||
|
||||
accounts:
|
||||
- accessKey: RocketMQ
|
||||
secretKey: mq000000
|
||||
whiteRemoteAddress:
|
||||
admin: true
|
||||
defaultTopicPerm: PUB|SUB
|
||||
defaultGroupPerm: PUB|SUB
|
||||
topicPerms:
|
||||
- topicA=DENY
|
||||
- topicB=PUB|SUB
|
||||
- topicC=SUB
|
||||
groupPerms:
|
||||
- groupA=DENY
|
||||
- groupB=PUB|SUB
|
||||
- groupC=SUB
|
||||
|
||||
- accessKey: earthearth
|
||||
secretKey: mq000000
|
||||
whiteRemoteAddress:
|
||||
admin: true
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"rocketMQClusterName": "DefaultCluster"
|
||||
}
|
||||
BIN
luohuo-cloud/install/image/img.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
luohuo-cloud/install/image/img_1.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
luohuo-cloud/install/image/img_10.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
luohuo-cloud/install/image/img_11.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
luohuo-cloud/install/image/img_12.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
luohuo-cloud/install/image/img_2.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
luohuo-cloud/install/image/img_3.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
luohuo-cloud/install/image/img_4.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
luohuo-cloud/install/image/img_5.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
luohuo-cloud/install/image/img_6.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
luohuo-cloud/install/image/img_7.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
luohuo-cloud/install/image/img_8.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
luohuo-cloud/install/image/img_9.png
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
luohuo-cloud/install/image/jenkins密码.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
luohuo-cloud/install/image/环境部署效果.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
44
luohuo-cloud/install/init_pg_db.sh
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
set -eo pipefail
|
||||
|
||||
# Check if a custom parameter has been set, otherwise use default values
|
||||
DB_PORT="${DB_PORT:=5432}"
|
||||
SUPERUSER="${SUPERUSER:=postgres}"
|
||||
SUPERUSER_PWD="${SUPERUSER_PWD:=postgres}"
|
||||
|
||||
# Allow to skip Docker if a dockerized Postgres database is already running
|
||||
if [[ -z "${SKIP_DOCKER}" ]]
|
||||
then
|
||||
# if a postgres container is running, print instructions to kill it and exit
|
||||
RUNNING_POSTGRES_CONTAINER=$(docker ps --filter 'name=postgres' --format '{{.ID}}')
|
||||
if [[ -n $RUNNING_POSTGRES_CONTAINER ]]; then
|
||||
echo >&2 "there is a postgres container already running, kill it with"
|
||||
echo >&2 " docker kill ${RUNNING_POSTGRES_CONTAINER}"
|
||||
exit 1
|
||||
fi
|
||||
CONTAINER_NAME="postgres"
|
||||
# Launch postgres using Docker
|
||||
docker run \
|
||||
--env POSTGRES_USER=${SUPERUSER} \
|
||||
--env POSTGRES_PASSWORD=${SUPERUSER_PWD} \
|
||||
--health-cmd="pg_isready -U ${SUPERUSER} || exit 1" \
|
||||
--health-interval=1s \
|
||||
--health-timeout=5s \
|
||||
--health-retries=5 \
|
||||
--publish "${DB_PORT}":5432 \
|
||||
--detach \
|
||||
--name "${CONTAINER_NAME}" \
|
||||
postgres -N 1000
|
||||
# ^ Increased maximum number of connections for testing purposes
|
||||
|
||||
until [ \
|
||||
"$(docker inspect -f "{{.State.Health.Status}}" ${CONTAINER_NAME})" == \
|
||||
"healthy" \
|
||||
]; do
|
||||
>&2 echo "Postgres is still unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
>&2 echo "Postgres is up and running on port ${DB_PORT} - running migrations now!"
|
||||
179
luohuo-cloud/install/mysql-schema.sql
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = config_info */
|
||||
/******************************************/
|
||||
CREATE TABLE `config_info` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
|
||||
`group_id` varchar(128) DEFAULT NULL COMMENT 'group_id',
|
||||
`content` longtext NOT NULL COMMENT 'content',
|
||||
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
`src_user` text COMMENT 'source user',
|
||||
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
|
||||
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
|
||||
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
|
||||
`c_desc` varchar(256) DEFAULT NULL COMMENT 'configuration description',
|
||||
`c_use` varchar(64) DEFAULT NULL COMMENT 'configuration usage',
|
||||
`effect` varchar(64) DEFAULT NULL COMMENT '配置生效的描述',
|
||||
`type` varchar(64) DEFAULT NULL COMMENT '配置的类型',
|
||||
`c_schema` text COMMENT '配置的模式',
|
||||
`encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = config_info since 2.5.0 */
|
||||
/******************************************/
|
||||
CREATE TABLE `config_info_gray` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
|
||||
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
|
||||
`content` longtext NOT NULL COMMENT 'content',
|
||||
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
|
||||
`src_user` text COMMENT 'src_user',
|
||||
`src_ip` varchar(100) DEFAULT NULL COMMENT 'src_ip',
|
||||
`gmt_create` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_create',
|
||||
`gmt_modified` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_modified',
|
||||
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
|
||||
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
|
||||
`gray_name` varchar(128) NOT NULL COMMENT 'gray_name',
|
||||
`gray_rule` text NOT NULL COMMENT 'gray_rule',
|
||||
`encrypted_data_key` varchar(256) NOT NULL DEFAULT '' COMMENT 'encrypted_data_key',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_configinfogray_datagrouptenantgray` (`data_id`,`group_id`,`tenant_id`,`gray_name`),
|
||||
KEY `idx_dataid_gmt_modified` (`data_id`,`gmt_modified`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='config_info_gray';
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = config_tags_relation */
|
||||
/******************************************/
|
||||
CREATE TABLE `config_tags_relation` (
|
||||
`id` bigint(20) NOT NULL COMMENT 'id',
|
||||
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
|
||||
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
|
||||
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
|
||||
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
|
||||
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
|
||||
`nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识',
|
||||
PRIMARY KEY (`nid`),
|
||||
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
|
||||
KEY `idx_tenant_id` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = group_capacity */
|
||||
/******************************************/
|
||||
CREATE TABLE `group_capacity` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
|
||||
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
|
||||
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
|
||||
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
|
||||
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
|
||||
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
|
||||
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_group_id` (`group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = his_config_info */
|
||||
/******************************************/
|
||||
CREATE TABLE `his_config_info` (
|
||||
`id` bigint(20) unsigned NOT NULL COMMENT 'id',
|
||||
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识',
|
||||
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
|
||||
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
|
||||
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
|
||||
`content` longtext NOT NULL COMMENT 'content',
|
||||
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
`src_user` text COMMENT 'source user',
|
||||
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
|
||||
`op_type` char(10) DEFAULT NULL COMMENT 'operation type',
|
||||
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
|
||||
`encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥',
|
||||
`publish_type` varchar(50) DEFAULT 'formal' COMMENT 'publish type gray or formal',
|
||||
`gray_name` varchar(50) DEFAULT NULL COMMENT 'gray name',
|
||||
`ext_info` longtext DEFAULT NULL COMMENT 'ext info',
|
||||
PRIMARY KEY (`nid`),
|
||||
KEY `idx_gmt_create` (`gmt_create`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`),
|
||||
KEY `idx_did` (`data_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
|
||||
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = tenant_capacity */
|
||||
/******************************************/
|
||||
CREATE TABLE `tenant_capacity` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
|
||||
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
|
||||
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
|
||||
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
|
||||
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
|
||||
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
|
||||
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tenant_id` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
|
||||
|
||||
|
||||
CREATE TABLE `tenant_info` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`kp` varchar(128) NOT NULL COMMENT 'kp',
|
||||
`tenant_id` varchar(128) default '' COMMENT 'tenant_id',
|
||||
`tenant_name` varchar(128) default '' COMMENT 'tenant_name',
|
||||
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
|
||||
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
|
||||
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
|
||||
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
|
||||
KEY `idx_tenant_id` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
|
||||
|
||||
CREATE TABLE `users` (
|
||||
`username` varchar(50) NOT NULL PRIMARY KEY COMMENT 'username',
|
||||
`password` varchar(500) NOT NULL COMMENT 'password',
|
||||
`enabled` boolean NOT NULL COMMENT 'enabled'
|
||||
);
|
||||
|
||||
CREATE TABLE `roles` (
|
||||
`username` varchar(50) NOT NULL COMMENT 'username',
|
||||
`role` varchar(50) NOT NULL COMMENT 'role',
|
||||
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
|
||||
);
|
||||
|
||||
CREATE TABLE `permissions` (
|
||||
`role` varchar(50) NOT NULL COMMENT 'role',
|
||||
`resource` varchar(128) NOT NULL COMMENT 'resource',
|
||||
`action` varchar(8) NOT NULL COMMENT 'action',
|
||||
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
|
||||
);
|
||||
|
||||
51
luohuo-cloud/install/rocketmqtest/docker-compose.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
services:
|
||||
rocketmq-namesrv:
|
||||
image: apache/rocketmq:5.3.2
|
||||
container_name: rocketmq-namesrv-test
|
||||
networks:
|
||||
- rocketmq-net
|
||||
ports:
|
||||
- 9886:9876
|
||||
command: sh mqnamesrv
|
||||
volumes:
|
||||
- ./rocketmq/namesrv/logs:/home/rocketmq/logs
|
||||
- ./rocketmq/namesrv/store:/home/rocketmq/store
|
||||
|
||||
rocketmq-broker:
|
||||
image: apache/rocketmq:5.3.2
|
||||
container_name: rocketmq-broker-test
|
||||
networks:
|
||||
- rocketmq-net
|
||||
ports:
|
||||
- 11909:10909
|
||||
- 11911:10911
|
||||
- 11912:10912
|
||||
environment:
|
||||
- NAMESRV_ADDR=rocketmq-namesrv:9876
|
||||
volumes:
|
||||
- ./rocketmq/broker/logs:/home/rocketmq/logs
|
||||
- ./rocketmq/broker/store:/home/rocketmq/store
|
||||
- ./rocketmq/broker/conf/broker.conf:/home/rocketmq/rocketmq-5.3.2/conf/broker.conf
|
||||
- ./rocketmq/broker/conf/plain_acl.yml:/home/rocketmq/rocketmq-5.3.2/conf/plain_acl.yml
|
||||
depends_on:
|
||||
- rocketmq-namesrv
|
||||
command: sh mqbroker -c /home/rocketmq/rocketmq-5.3.2/conf/broker.conf
|
||||
rocketmq-proxy:
|
||||
image: apache/rocketmq:5.3.2
|
||||
container_name: rocketmq-proxy-test
|
||||
networks:
|
||||
- rocketmq-net
|
||||
depends_on:
|
||||
- rocketmq-broker
|
||||
- rocketmq-namesrv
|
||||
ports:
|
||||
- 8182:8080
|
||||
- 8181:8081
|
||||
restart: on-failure
|
||||
environment:
|
||||
- NAMESRV_ADDR=rocketmq-namesrv:9876
|
||||
command: sh mqproxy
|
||||
|
||||
networks:
|
||||
rocketmq-net:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,104 @@
|
||||
#所属集群名字
|
||||
brokerClusterName=DefaultCluster
|
||||
|
||||
# 启用Proxy模式(可选,默认NONE)
|
||||
proxyMode=NONE
|
||||
|
||||
# 启用Controller模式(集群部署需配置)
|
||||
enableControllerMode=false
|
||||
|
||||
#broker名字,注意此处不同的配置文件填写的不一样,如果在broker-a.properties使用:broker-a,
|
||||
#在broker-b.properties使用:broker-b
|
||||
brokerName=broker-a
|
||||
|
||||
#0 表示Master,>0 表示Slave
|
||||
brokerId=0
|
||||
|
||||
#nameServer地址,分号分割
|
||||
#namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876
|
||||
namesrvAddr=rocketmq-namesrv:9876
|
||||
|
||||
#启动IP,如果 docker 报 com.alibaba.rocketmq.remoting.exception.RemotingConnectException: connect to <192.168.0.120:10909> failed
|
||||
# 解决方式1 加上一句producer.setVipChannelEnabled(false);,解决方式2 brokerIP1 设置宿主机IP,不要使用docker 内部IP
|
||||
brokerIP1=192.168.1.37
|
||||
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
|
||||
defaultTopicQueueNums=4
|
||||
|
||||
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭 !!!这里仔细看是false,false,false
|
||||
autoCreateTopicEnable=true
|
||||
enableAutoCreateSystemTopic=true
|
||||
# 必须设置为true(与proxy协调)
|
||||
enableAutoCreateSubscriptionGroup=true
|
||||
|
||||
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
|
||||
autoCreateSubscriptionGroup=true
|
||||
|
||||
#Broker 对外服务的监听端口
|
||||
listenPort=10911
|
||||
|
||||
#此参数控制是否开启密码,不开启可设置false
|
||||
aclEnable=false
|
||||
|
||||
#删除文件时间点,默认凌晨4点
|
||||
deleteWhen=04
|
||||
|
||||
#文件保留时间,默认48小时
|
||||
fileReservedTime=120
|
||||
|
||||
#commitLog每个文件的大小默认1G
|
||||
mappedFileSizeCommitLog=1073741824
|
||||
|
||||
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
|
||||
mappedFileSizeConsumeQueue=300000
|
||||
|
||||
#destroyMapedFileIntervalForcibly=120000
|
||||
#redeleteHangedFileInterval=120000
|
||||
#检测物理文件磁盘空间
|
||||
diskMaxUsedSpaceRatio=88
|
||||
#存储路径
|
||||
storePathRootDir=/home/rocketmq/store
|
||||
#commitLog 存储路径
|
||||
storePathCommitLog=/home/rocketmq/store/commitlog
|
||||
#消费队列存储
|
||||
storePathConsumeQueue=/home/rocketmq/store/consumequeue
|
||||
#消息索引存储路径
|
||||
storePathIndex=/home/rocketmq/store/index
|
||||
#checkpoint 文件存储路径
|
||||
storeCheckpoint=/home/rocketmq/store/checkpoint
|
||||
#abort 文件存储路径
|
||||
abortFile=/home/rocketmq/store/abort
|
||||
#限制的消息大小
|
||||
maxMessageSize=65536
|
||||
|
||||
# 延迟消息配置
|
||||
#timerWheelEnable=false
|
||||
#enableScheduleMessage=false
|
||||
|
||||
# 确保此目录存在且可写
|
||||
#timerStorePath=/home/rocketmq/store/timerwheel
|
||||
#timerFlushIntervalMs=1000
|
||||
#timerPrecisionMs=1000
|
||||
#scheduleMessageServiceThreadPoolNums=4
|
||||
|
||||
#flushCommitLogLeastPages=4
|
||||
#flushConsumeQueueLeastPages=2
|
||||
#flushCommitLogThoroughInterval=10000
|
||||
#flushConsumeQueueThoroughInterval=60000
|
||||
|
||||
#Broker 的角色
|
||||
#- ASYNC_MASTER 异步复制Master
|
||||
#- SYNC_MASTER 同步双写Master
|
||||
#- SLAVE
|
||||
brokerRole=ASYNC_MASTER
|
||||
|
||||
#刷盘方式
|
||||
#- ASYNC_FLUSH 异步刷盘 SYNC_FLUSH 同步刷盘
|
||||
flushDiskType=ASYNC_FLUSH
|
||||
|
||||
# 堆外内存限制(需与 JVM 参数一致)
|
||||
maxDirectMemorySize=1g
|
||||
|
||||
#发消息线程池数量
|
||||
#sendMessageThreadPoolNums=128
|
||||
#拉消息线程池数量
|
||||
#pullMessageThreadPoolNums=128
|
||||
@@ -0,0 +1,24 @@
|
||||
globalWhiteRemoteAddresses:
|
||||
# - 47.100.93.*
|
||||
# - 156.254.120.*
|
||||
|
||||
accounts:
|
||||
- accessKey: RocketMQ
|
||||
secretKey: mq000000
|
||||
whiteRemoteAddress:
|
||||
admin: true
|
||||
defaultTopicPerm: PUB|SUB
|
||||
defaultGroupPerm: PUB|SUB
|
||||
topicPerms:
|
||||
- topicA=DENY
|
||||
- topicB=PUB|SUB
|
||||
- topicC=SUB
|
||||
groupPerms:
|
||||
- groupA=DENY
|
||||
- groupB=PUB|SUB
|
||||
- groupC=SUB
|
||||
|
||||
- accessKey: earthearth
|
||||
secretKey: mq000000
|
||||
whiteRemoteAddress:
|
||||
admin: true
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"rocketMQClusterName": "DefaultCluster"
|
||||
}
|
||||
95
luohuo-cloud/install/服务端部署文档.md
Normal file
@@ -0,0 +1,95 @@
|
||||
## 📝 修改环境配置
|
||||
|
||||
上传目录下的docker文件夹到服务器/home/docker下面, 需要修改env文件夹rocketmq文件夹里面的配置, 特别是[broker.conf](docker/rocketmq/broker/conf/broker.conf)里面brokerIP1的值
|
||||
|
||||
|
||||
## 🛠️ 启动命令
|
||||
- **提高权限**: sudo chmod -R 777 /home/docker/rocketmq
|
||||
- **开放端口**: 先去你购物服务器的平台里面的安全组里面放行mysql、redis、rocketmq部署的端口,再去1panel或宝塔下面放行
|
||||
- **执行命令**: cd /home/docker & docker-compose up -d
|
||||
- **导入mysql数据库**: install文件夹下面有一个nacos[mysql-schema.sql](mysql-schema.sql)的sql文件,需要导入到数据库里面,不然nacos启动不了
|
||||
- **重试nacos**: 如果还不行就删了nacos的容器,重新执行 docker-compose up -d
|
||||
|
||||
## 🖼️ 效果预览
|
||||

|
||||
|
||||
## 🛠️ 安装项目环境
|
||||
|
||||
- **开放端口**: 安全组里面放行jenkins部署的端口,再去1panel或宝塔下面放行, 我这里配置的是20000
|
||||
- **安装JDK21**: https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz [安装在宿主机]
|
||||
- **安装git、maven**: 我机器自带的git、https://maven.apache.org/download.cgi 下载然后上传解压即可 [安装在宿主机]
|
||||
|
||||
|
||||
## 🛠️ jenkins 环境配置
|
||||
- **查看密码**: docker logs -f jenkins | grep "initialAdminPassword"
|
||||

|
||||
- **进入jenkins**: http://ip:20000,输入上一步查看得到的密码,配置各项基础信息
|
||||
- **安装插件**: 自行选择默认推荐的就行,ant、Gradle等一些没用的插件可以取消勾选
|
||||
- **下载必选jenkins插件**: http://ip:20000/manage/pluginManager/
|
||||
```
|
||||
Git Parameter
|
||||
Git plugin
|
||||
Maven Integration
|
||||
gitee
|
||||
Publish Over SSH
|
||||
```
|
||||
- **系统全局配置**: http://ip:20000/manage/configure 配置环境变量
|
||||
```
|
||||
PATH
|
||||
/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/var/jenkins_home/maven/bin:/root/bin
|
||||
```
|
||||

|
||||
- **系统全局配置**: http://ip:20000/manage/configure 配置项目地址; 添加凭证时必须选giteeAPI, util、cloud两个项目地址都需要配进去,点击测试链接按钮必须返回成功
|
||||

|
||||

|
||||
|
||||
- **系统全局配置**: http://ip:20000/manage/configureTools/ 配置jenkins内部maven; 项目环境中下载的maven解压到 /home/jenkins/work 下面
|
||||

|
||||

|
||||
|
||||
|
||||
## 🛠️ 创建util、cloud项目流水线
|
||||
|
||||
- **创建util**: 输入名称、选择流水线
|
||||

|
||||
- **配置util**: 根据步骤,仔细对比这上面的内容
|
||||

|
||||
- **创建账号凭证**: 这一步创建的账号是username!!!,注意看是你登录gitee的账号密码。不是apikey!
|
||||

|
||||
|
||||
- **创建cloud**: 输入名称、选择流水线
|
||||

|
||||
- **配置cloud**: 根据步骤,仔细对比这上面的内容
|
||||

|
||||
|
||||
## 🛠️ 编译 util、cloud
|
||||
|
||||
- **install util**: 因为cloud依赖 util, 所以先安装util到maven本地仓库
|
||||

|
||||
|
||||
- **install cloud**: 必须等util模块install完成才行,这样cloud模块就可以通过maven本地仓库找到util依赖
|
||||

|
||||
|
||||
## 🛠️ 运行项目
|
||||
- **开放端口**: 10911 [rocketMQ通信用的]
|
||||
- **授权目录**:
|
||||
```
|
||||
chmod 777 /home/docker/rocketmq/broker/logs
|
||||
chmod 777 /home/docker/rocketmq/broker/store
|
||||
chmod 777 /home/docker/rocketmq/namesrv/logs
|
||||
chmod 777 /home/docker/rocketmq/namesrv/store
|
||||
```
|
||||
- **导入数据库**: luohuo_im_01、luohuo_dev
|
||||
- **执行命令**: /bin/bash /home/jenkins/work/workspace/luohuo-cloud/src/main/bin/all-start.sh
|
||||
|
||||

|
||||
|
||||
|
||||
- **注意事项**: 消息推送采用CLUSTERING模式,单个mq只能被一个cloud项目连接,多个cloud可能会导致消息接收不到; 若需要多次连接必须docker再开一个rocketmq的test版本单独连接,直接copy[rocketmqtest](rocketmqtest)目录 docker-compose up -d即可
|
||||
|
||||
|
||||
## 🛠️ 本地视频语音电话
|
||||
- **浏览器输入地址**: chrome://flags/
|
||||
- **搜索关键字**: Insecure origins treated as secure
|
||||
- **开放端口和ip**: http://192.168.1.37:6130,http://192.168.1.24:6130,http://192.168.1.26:6130
|
||||
```
|
||||
@@ -56,41 +56,11 @@ public class RedisKey {
|
||||
*/
|
||||
public static final String ROOM_FRIEND_FORMAT = "room_friend:id_%d";
|
||||
|
||||
/**
|
||||
* 群成员信息
|
||||
*/
|
||||
public static final String GROUP_MEMBER_INFO_FORMAT = "group_member_info:groupId_%d";
|
||||
|
||||
/**
|
||||
* 群公告
|
||||
*/
|
||||
public static final String GROUP_ANNOUNCEMENTS_FORMAT = "groupInfo:announcements_%d";
|
||||
|
||||
/**
|
||||
* 用户token存放 格式:终端:uid
|
||||
*/
|
||||
public static final String USER_TOKEN_UID_FORMAT = "userToken:%s:uid_%d";
|
||||
|
||||
/**
|
||||
* 用户token存放 格式:终端:uid:uuid
|
||||
*/
|
||||
public static final String USER_TOKEN_FORMAT = "userToken:%s:uid_%d:%s";
|
||||
|
||||
/**
|
||||
* 用户refreshToken存放
|
||||
*/
|
||||
public static final String USER_REFRESH_TOKEN_UID_FORMAT = "userRefreshToken:%s:uid_%d";
|
||||
|
||||
/**
|
||||
* 用户refreshToken存放
|
||||
*/
|
||||
public static final String USER_REFRESH_TOKEN_FORMAT = "userRefreshToken:%s:uid_%d:%s";
|
||||
|
||||
/**
|
||||
* 用户的信息更新时间
|
||||
*/
|
||||
public static final String USER_MODIFY_FORMAT = "userModify:uid_%d";
|
||||
|
||||
/**
|
||||
* 用户的信息汇总
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.luohuo.flex.im.common.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 邀请进群事件
|
||||
*/
|
||||
@Getter
|
||||
public class GroupInviteMemberEvent extends ApplicationEvent {
|
||||
|
||||
// 变动的成员
|
||||
private final List<Long> memberList;
|
||||
private final Long roomId;
|
||||
// 消息接收人
|
||||
private final Long uid;
|
||||
|
||||
public GroupInviteMemberEvent(Object source, Long roomId, List<Long> memberList, Long uid) {
|
||||
super(source);
|
||||
this.memberList = memberList;
|
||||
this.roomId = roomId;
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.luohuo.flex.im.common.event.listener;
|
||||
|
||||
import com.luohuo.flex.im.common.event.GroupInviteMemberEvent;
|
||||
import com.luohuo.flex.im.core.chat.service.ChatService;
|
||||
import com.luohuo.flex.im.core.chat.service.adapter.RoomAdapter;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.domain.vo.request.ChatMessageReq;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.event.TransactionPhase;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.luohuo.flex.im.common.config.ThreadPoolConfig.LUOHUO_EXECUTOR;
|
||||
|
||||
/**
|
||||
* 邀请群成员监听器
|
||||
*
|
||||
* @author 乾乾
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class GroupInviteMemberListener {
|
||||
|
||||
private ChatService chatService;
|
||||
private UserCache userCache;
|
||||
|
||||
/**
|
||||
* 触发邀请群员的消息
|
||||
* @param event
|
||||
*/
|
||||
@Async(LUOHUO_EXECUTOR)
|
||||
@TransactionalEventListener(classes = GroupInviteMemberEvent.class, fallbackExecution = true, phase = TransactionPhase.AFTER_COMMIT)
|
||||
public void sendAddMsg(GroupInviteMemberEvent event) {
|
||||
List<Long> uidList = event.getMemberList();
|
||||
Long roomId = event.getRoomId();
|
||||
User user = userCache.get(event.getUid());
|
||||
ChatMessageReq chatMessageReq = RoomAdapter.buildGroupAddMessage(roomId, user, userCache.getBatch(uidList));
|
||||
chatService.sendMsg(chatMessageReq, user.getId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,12 @@
|
||||
package com.luohuo.flex.im.common.event.listener;
|
||||
|
||||
import com.luohuo.basic.cache.repository.CachePlusOps;
|
||||
import com.luohuo.flex.im.api.PresenceApi;
|
||||
import com.luohuo.flex.im.common.event.GroupMemberAddEvent;
|
||||
import com.luohuo.flex.im.core.chat.dao.GroupMemberDao;
|
||||
import com.luohuo.flex.im.domain.vo.request.ChatMessageReq;
|
||||
import com.luohuo.flex.im.core.chat.service.ChatService;
|
||||
import com.luohuo.flex.im.core.chat.service.adapter.MemberAdapter;
|
||||
import com.luohuo.flex.im.core.chat.service.adapter.RoomAdapter;
|
||||
import com.luohuo.flex.im.core.chat.service.cache.GroupMemberCache;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserInfoCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.core.user.service.impl.PushService;
|
||||
import com.luohuo.flex.model.entity.ws.ChatMember;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -34,28 +30,12 @@ import static com.luohuo.flex.im.common.config.ThreadPoolConfig.LUOHUO_EXECUTOR;
|
||||
@AllArgsConstructor
|
||||
public class GroupMemberAddListener {
|
||||
|
||||
private CachePlusOps cachePlusOps;
|
||||
private ChatService chatService;
|
||||
private UserInfoCache userInfoCache;
|
||||
private UserCache userCache;
|
||||
private GroupMemberDao groupMemberDao;
|
||||
private GroupMemberCache groupMemberCache;
|
||||
private PresenceApi presenceApi;
|
||||
private PushService pushService;
|
||||
|
||||
/**
|
||||
* 触发群主邀请群员的消息
|
||||
* @param event
|
||||
*/
|
||||
@Async(LUOHUO_EXECUTOR)
|
||||
@TransactionalEventListener(classes = GroupMemberAddEvent.class, fallbackExecution = true, phase = TransactionPhase.AFTER_COMMIT)
|
||||
public void sendAddMsg(GroupMemberAddEvent event) {
|
||||
List<Long> uidList = event.getMemberList();
|
||||
Long roomId = event.getRoomId();
|
||||
User user = userInfoCache.get(event.getUid());
|
||||
ChatMessageReq chatMessageReq = RoomAdapter.buildGroupAddMessage(roomId, user, userInfoCache.getBatch(uidList));
|
||||
chatService.sendMsg(chatMessageReq, user.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO 这里要做屏蔽群成员、所有群成员
|
||||
* 群成员变动推送逻辑
|
||||
@@ -68,7 +48,7 @@ public class GroupMemberAddListener {
|
||||
List<Long> memberUidList = groupMemberCache.getMemberExceptUidList(roomId);
|
||||
// 在线的用户
|
||||
List<Long> onlineUids = presenceApi.getGroupOnlineMembers(roomId).getData();
|
||||
Map<Long, User> map = userInfoCache.getBatch(event.getMemberList());
|
||||
Map<Long, User> map = userCache.getBatch(event.getMemberList());
|
||||
|
||||
List<ChatMember> memberResps = groupMemberDao.getMemberListByUid(event.getMemberList());
|
||||
pushService.sendPushMsg(MemberAdapter.buildMemberAddWS(roomId, event.getTotalNum(), onlineUids, memberResps, map), memberUidList, event.getUid());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.luohuo.flex.im.common.event.listener;
|
||||
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.event.EventListener;
|
||||
@@ -12,7 +13,6 @@ import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.domain.entity.UserBackpack;
|
||||
import com.luohuo.flex.im.domain.enums.ItemTypeEnum;
|
||||
import com.luohuo.flex.im.core.user.service.cache.ItemCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -31,7 +31,7 @@ public class ItemReceiveListener {
|
||||
@Resource
|
||||
private ItemCache itemCache;
|
||||
@Resource
|
||||
private UserCache userCache;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
|
||||
/**
|
||||
* 徽章类型,帮忙默认佩戴
|
||||
@@ -47,7 +47,7 @@ public class ItemReceiveListener {
|
||||
User user = userDao.getById(userBackpack.getUid());
|
||||
if (Objects.isNull(user.getItemId())) {
|
||||
userDao.wearingBadge(userBackpack.getUid(), userBackpack.getItemId());
|
||||
userCache.userInfoChange(userBackpack.getUid());
|
||||
userSummaryCache.delete(userBackpack.getUid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.luohuo.flex.im.common.event.listener;
|
||||
|
||||
import com.luohuo.basic.context.ContextUtil;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.event.EventListener;
|
||||
@@ -11,7 +12,6 @@ import com.luohuo.flex.im.core.chat.dao.MessageDao;
|
||||
import com.luohuo.flex.model.entity.WSRespTypeEnum;
|
||||
import com.luohuo.flex.model.entity.WsBaseResp;
|
||||
import com.luohuo.flex.model.entity.ws.WSBlack;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.core.user.service.impl.PushService;
|
||||
|
||||
import static com.luohuo.flex.im.common.config.ThreadPoolConfig.LUOHUO_EXECUTOR;
|
||||
@@ -27,13 +27,13 @@ import static com.luohuo.flex.im.common.config.ThreadPoolConfig.LUOHUO_EXECUTOR;
|
||||
public class UserBlackListener {
|
||||
|
||||
private final MessageDao messageDao;
|
||||
private final UserCache userCache;
|
||||
private final UserSummaryCache userSummaryCache;
|
||||
private final PushService pushService;
|
||||
|
||||
@Async(LUOHUO_EXECUTOR)
|
||||
@EventListener(classes = UserBlackEvent.class)
|
||||
public void refreshRedis(UserBlackEvent event) {
|
||||
userCache.evictBlackMap();
|
||||
userSummaryCache.evictBlackMap();
|
||||
}
|
||||
|
||||
@Async(LUOHUO_EXECUTOR)
|
||||
|
||||
@@ -5,7 +5,7 @@ import cn.hutool.http.ContentType;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.luohuo.basic.base.R;
|
||||
import com.luohuo.basic.context.ContextUtil;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import com.luohuo.flex.im.domain.enums.BlackTypeEnum;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@@ -26,11 +26,11 @@ import java.util.Set;
|
||||
@RequiredArgsConstructor
|
||||
public class BlackInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final UserCache userCache;
|
||||
private final UserSummaryCache userSummaryCache;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
Map<Integer, Set<String>> blackMap = userCache.getBlackMap();
|
||||
Map<Integer, Set<String>> blackMap = userSummaryCache.getBlackMap();
|
||||
Long uid = ContextUtil.getUid();
|
||||
if (isBlackList(uid, blackMap.get(BlackTypeEnum.UID.getType()))) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
|
||||
@@ -22,13 +22,9 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.luohuo.flex.im.domain.enums.GroupRoleEnum.ROLE_LIST;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 群成员表 服务实现类
|
||||
@@ -67,23 +63,6 @@ public class GroupMemberDao extends ServiceImpl<GroupMemberMapper, GroupMember>
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取成员群角色
|
||||
*
|
||||
* @param groupId 群ID
|
||||
* @param uidList 用户列表
|
||||
* @return 成员群角色列表
|
||||
*/
|
||||
public Map<String, Integer> getMemberMapRole(Long groupId, List<Long> uidList) {
|
||||
List<GroupMember> list = lambdaQuery()
|
||||
.eq(GroupMember::getGroupId, groupId)
|
||||
.in(GroupMember::getUid, uidList)
|
||||
.in(GroupMember::getRoleId, ROLE_LIST)
|
||||
.select(GroupMember::getUid, GroupMember::getRoleId)
|
||||
.list();
|
||||
return list.stream().collect(Collectors.toMap(member -> String.valueOf(member.getUid()), GroupMember::getRoleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群聊人员
|
||||
* isSpecial:true -> 获取群主、管理员
|
||||
|
||||
@@ -10,10 +10,8 @@ import com.luohuo.flex.im.domain.vo.request.ChatMessagePageReq;
|
||||
import com.luohuo.flex.im.domain.vo.request.ChatMessageReadInfoReq;
|
||||
import com.luohuo.flex.im.domain.vo.request.ChatMessageReadReq;
|
||||
import com.luohuo.flex.im.domain.vo.request.ChatMessageReq;
|
||||
import com.luohuo.flex.im.domain.vo.request.member.MemberReq;
|
||||
import com.luohuo.flex.im.domain.vo.response.ChatMessageReadResp;
|
||||
import com.luohuo.flex.model.entity.ws.ChatMessageResp;
|
||||
import com.luohuo.flex.model.entity.ws.ChatMemberResp;
|
||||
import jakarta.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -51,15 +49,6 @@ public interface ChatService {
|
||||
*/
|
||||
ChatMessageResp getMsgResp(Long msgId, Long receiveUid);
|
||||
|
||||
/**
|
||||
* 获取群成员列表
|
||||
*
|
||||
* @param memberUidList 成员id集和
|
||||
* @param request 参数
|
||||
* @return {@link CursorPageBaseResp }<{@link ChatMemberResp }>
|
||||
*/
|
||||
CursorPageBaseResp<ChatMemberResp> getMemberPage(List<Long> memberUidList, MemberReq request);
|
||||
|
||||
/**
|
||||
* 获取消息列表
|
||||
*
|
||||
|
||||
@@ -44,10 +44,12 @@ public interface RoomAppService {
|
||||
* @return
|
||||
*/
|
||||
Boolean createContact(Long uid, @Valid ContactAddReq request);
|
||||
|
||||
/**
|
||||
* 获取会话列表--支持未登录态
|
||||
*/
|
||||
CursorPageBaseResp<ChatRoomResp> getContactPage(CursorPageBaseReq request, Long uid);
|
||||
|
||||
/**
|
||||
* 获取用户所有会话列表
|
||||
*/
|
||||
@@ -58,22 +60,41 @@ public interface RoomAppService {
|
||||
*/
|
||||
MemberResp getGroupDetail(Long uid, long roomId);
|
||||
|
||||
CursorPageBaseResp<ChatMemberResp> getMemberPage(MemberReq request);
|
||||
|
||||
/**
|
||||
* 获取群成员
|
||||
*/
|
||||
List<ChatMemberListResp> getMemberList(ChatMessageMemberReq request);
|
||||
|
||||
/**
|
||||
* 移出群聊
|
||||
*/
|
||||
void delMember(Long uid, MemberDelReq request);
|
||||
|
||||
/**
|
||||
* 邀请好友进群
|
||||
*/
|
||||
void addMember(Long uid, MemberAddReq request);
|
||||
|
||||
/**
|
||||
* 创建群聊
|
||||
*/
|
||||
Long addGroup(Long uid, GroupAddReq request);
|
||||
|
||||
/**
|
||||
* 获取单个会话
|
||||
*/
|
||||
ChatRoomResp getContactDetail(Long uid, Long roomId);
|
||||
|
||||
ChatRoomResp getContactDetailByFriend(Long uid, @Valid ContactFriendReq req);
|
||||
|
||||
/**
|
||||
* 获取群聊列表
|
||||
*/
|
||||
List<MemberResp> groupList(Long uid);
|
||||
|
||||
/**
|
||||
* 同步在线状态
|
||||
*/
|
||||
void asyncOnline(List<Long> uidList, Long roomId, boolean online);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.luohuo.flex.im.core.chat.service.adapter;
|
||||
|
||||
import com.luohuo.flex.im.domain.entity.IpDetail;
|
||||
import com.luohuo.flex.im.domain.entity.IpInfo;
|
||||
import com.luohuo.flex.model.entity.ws.ChatMember;
|
||||
import com.luohuo.flex.model.enums.ChatActiveStatusEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -13,15 +11,12 @@ import com.luohuo.flex.im.domain.vo.response.ChatMemberListResp;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.model.entity.WSRespTypeEnum;
|
||||
import com.luohuo.flex.model.entity.WsBaseResp;
|
||||
import com.luohuo.flex.model.entity.ws.ChatMemberResp;
|
||||
import com.luohuo.flex.model.entity.ws.WSFeedMemberResp;
|
||||
import com.luohuo.flex.model.entity.ws.WSMemberChange;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.luohuo.flex.model.entity.ws.WSMemberChange.CHANGE_TYPE_ADD;
|
||||
@@ -34,31 +29,6 @@ import static com.luohuo.flex.model.entity.ws.WSMemberChange.CHANGE_TYPE_ADD;
|
||||
@Slf4j
|
||||
public class MemberAdapter {
|
||||
|
||||
/**
|
||||
* 将User对象转换为ChatMemberResp对象,并注入实时在线状态
|
||||
* @param list 用户列表
|
||||
* @param onlineUids 用户在线状态表
|
||||
* @return 转换后的群成员响应对象列表
|
||||
*/
|
||||
public static List<ChatMemberResp> buildMember(List<User> list, Set<Long> onlineUids) {
|
||||
return list.stream().map(user -> {
|
||||
ChatMemberResp resp = new ChatMemberResp();
|
||||
resp.setUid(String.valueOf(user.getId()));
|
||||
resp.setName(user.getName());
|
||||
resp.setAvatar(user.getAvatar());
|
||||
resp.setAccount(user.getAccount());
|
||||
resp.setLocPlace(Optional.ofNullable(user.getIpInfo()).map(IpInfo::getUpdateIpDetail).map(IpDetail::getCity).orElse(null));
|
||||
resp.setUserStateId(user.getUserStateId());
|
||||
Boolean isOnline = onlineUids.contains(user.getId());
|
||||
resp.setActiveStatus(isOnline? ChatActiveStatusEnum.ONLINE.getStatus(): ChatActiveStatusEnum.OFFLINE.getStatus());
|
||||
// 最后活跃时间(离线用户显示)
|
||||
if (!isOnline && user.getLastOptTime() != null) {
|
||||
resp.setLastOptTime(user.getLastOptTime());
|
||||
}
|
||||
return resp;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<ChatMemberListResp> buildMemberList(List<User> memberList) {
|
||||
return memberList.stream()
|
||||
.map(a -> {
|
||||
|
||||
@@ -150,7 +150,6 @@ public class MessageAdapter {
|
||||
return chatMessageReq;
|
||||
}
|
||||
|
||||
|
||||
public static ChatMessageReq buildAgreeMsg4Group(Long roomId, Long count, String userName) {
|
||||
ChatMessageReq chatMessageReq = new ChatMessageReq();
|
||||
chatMessageReq.setRoomId(roomId);
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.luohuo.flex.im.core.chat.service.helper;
|
||||
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.luohuo.flex.model.enums.ChatActiveStatusEnum;
|
||||
|
||||
/**
|
||||
* 成员列表工具类
|
||||
* @author nyh
|
||||
*/
|
||||
public class ChatMemberHelper {
|
||||
private static final String SEPARATOR = "_";
|
||||
|
||||
public static Pair<ChatActiveStatusEnum, String> getCursorPair(String cursor) {
|
||||
ChatActiveStatusEnum activeStatusEnum = ChatActiveStatusEnum.ONLINE;
|
||||
String timeCursor = null;
|
||||
if (StrUtil.isNotBlank(cursor)) {
|
||||
String activeStr = cursor.split(SEPARATOR)[0];
|
||||
String timeStr = cursor.split(SEPARATOR)[1];
|
||||
activeStatusEnum = ChatActiveStatusEnum.of(Integer.parseInt(activeStr));
|
||||
timeCursor = timeStr;
|
||||
}
|
||||
return Pair.of(activeStatusEnum, timeCursor);
|
||||
}
|
||||
|
||||
public static String generateCursor(ChatActiveStatusEnum activeStatusEnum, String timeCursor) {
|
||||
return activeStatusEnum.getStatus() + SEPARATOR + timeCursor;
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,16 @@ package com.luohuo.flex.im.core.chat.service.impl;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.luohuo.basic.base.R;
|
||||
import com.luohuo.basic.utils.SpringUtils;
|
||||
import com.luohuo.basic.utils.TimeUtils;
|
||||
import com.luohuo.flex.im.api.PresenceApi;
|
||||
import com.luohuo.flex.im.core.chat.dao.*;
|
||||
import com.luohuo.flex.im.core.chat.service.cache.GroupMemberCache;
|
||||
import com.luohuo.flex.im.core.chat.service.cache.MsgCache;
|
||||
import com.luohuo.flex.im.core.user.dao.UserFriendDao;
|
||||
import com.luohuo.flex.im.domain.entity.*;
|
||||
import com.luohuo.flex.im.domain.enums.*;
|
||||
import com.luohuo.flex.model.enums.ChatActiveStatusEnum;
|
||||
import jakarta.annotation.Nullable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -28,7 +22,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import com.luohuo.basic.exception.BizException;
|
||||
import com.luohuo.basic.validator.utils.AssertUtil;
|
||||
import com.luohuo.flex.model.redis.annotation.RedissonLock;
|
||||
import com.luohuo.flex.im.domain.vo.req.CursorPageBaseReq;
|
||||
import com.luohuo.flex.im.domain.vo.res.CursorPageBaseResp;
|
||||
import com.luohuo.flex.im.common.event.MessageSendEvent;
|
||||
import com.luohuo.flex.im.domain.dto.ChatMsgSendDto;
|
||||
@@ -40,24 +33,18 @@ import com.luohuo.flex.im.domain.vo.request.ChatMessagePageReq;
|
||||
import com.luohuo.flex.im.domain.vo.request.ChatMessageReadInfoReq;
|
||||
import com.luohuo.flex.im.domain.vo.request.ChatMessageReadReq;
|
||||
import com.luohuo.flex.im.domain.vo.request.ChatMessageReq;
|
||||
import com.luohuo.flex.im.domain.vo.request.member.MemberReq;
|
||||
import com.luohuo.flex.im.domain.vo.response.ChatMessageReadResp;
|
||||
import com.luohuo.flex.model.entity.ws.ChatMessageResp;
|
||||
import com.luohuo.flex.im.core.chat.service.ChatService;
|
||||
import com.luohuo.flex.im.core.chat.service.ContactService;
|
||||
import com.luohuo.flex.im.core.chat.service.adapter.MemberAdapter;
|
||||
import com.luohuo.flex.im.core.chat.service.adapter.MessageAdapter;
|
||||
import com.luohuo.flex.im.core.chat.service.adapter.RoomAdapter;
|
||||
import com.luohuo.flex.im.core.chat.service.cache.RoomCache;
|
||||
import com.luohuo.flex.im.core.chat.service.cache.RoomGroupCache;
|
||||
import com.luohuo.flex.im.core.chat.service.helper.ChatMemberHelper;
|
||||
import com.luohuo.flex.im.core.chat.service.strategy.mark.AbstractMsgMarkStrategy;
|
||||
import com.luohuo.flex.im.core.chat.service.strategy.mark.MsgMarkFactory;
|
||||
import com.luohuo.flex.im.core.chat.service.strategy.msg.AbstractMsgHandler;
|
||||
import com.luohuo.flex.im.core.chat.service.strategy.msg.MsgHandlerFactory;
|
||||
import com.luohuo.flex.im.core.chat.service.strategy.msg.RecallMsgHandler;
|
||||
import com.luohuo.flex.im.core.user.dao.UserDao;
|
||||
import com.luohuo.flex.model.entity.ws.ChatMemberResp;
|
||||
import com.luohuo.flex.im.core.user.service.RoleService;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -77,7 +64,6 @@ public class ChatServiceImpl implements ChatService {
|
||||
private final GroupMemberCache groupMemberCache;
|
||||
private MsgCache msgCache;
|
||||
private MessageDao messageDao;
|
||||
private UserDao userDao;
|
||||
private MessageMarkDao messageMarkDao;
|
||||
private RoomFriendDao roomFriendDao;
|
||||
private RoleService roleService;
|
||||
@@ -86,8 +72,6 @@ public class ChatServiceImpl implements ChatService {
|
||||
private ContactDao contactDao;
|
||||
private RoomCache roomCache;
|
||||
private GroupMemberDao groupMemberDao;
|
||||
private RoomGroupCache roomGroupCache;
|
||||
private PresenceApi presenceApi;
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
@@ -190,63 +174,6 @@ public class ChatServiceImpl implements ChatService {
|
||||
return getMsgResp(msg, receiveUid);
|
||||
}
|
||||
|
||||
private String generateCursor(ChatActiveStatusEnum type, String innerCursor) {
|
||||
return type.name() + "_" + innerCursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorPageBaseResp<ChatMemberResp> getMemberPage(List<Long> memberUidList, MemberReq request) {
|
||||
Pair<ChatActiveStatusEnum, String> pair = ChatMemberHelper.getCursorPair(request.getCursor());
|
||||
ChatActiveStatusEnum activeStatusEnum = pair.getKey();
|
||||
String timeCursor = pair.getValue();
|
||||
|
||||
// 1. 批量获取所有成员在线状态、分离在线用户与离线用户
|
||||
Set<Long> onlineUids = presenceApi.getOnlineUsersList(memberUidList).getData();
|
||||
Set<Long> offlineUids = memberUidList.stream().filter(uid -> !onlineUids.contains(uid)).collect(Collectors.toSet());
|
||||
|
||||
// 3. 动态分页组装
|
||||
List<ChatMemberResp> resultList = new ArrayList<>();
|
||||
Boolean isLast = Boolean.FALSE;
|
||||
if (activeStatusEnum == ChatActiveStatusEnum.ONLINE) {
|
||||
// 在线列表
|
||||
CursorPageBaseResp<User> onlinePage = userDao.getCursorPage(onlineUids, new CursorPageBaseReq(request.getPageSize(), timeCursor));
|
||||
// 添加在线列表
|
||||
resultList.addAll(MemberAdapter.buildMember(onlinePage.getList(), onlineUids));
|
||||
|
||||
if (onlinePage.getIsLast()) {
|
||||
// 如果是最后一页,从离线列表再补点数据
|
||||
CursorPageBaseResp<User> offlinePage = userDao.getCursorPage(offlineUids, new CursorPageBaseReq(request.getPageSize() - onlinePage.getList().size(), null));
|
||||
resultList.addAll(MemberAdapter.buildMember(offlinePage.getList(), onlineUids));
|
||||
timeCursor = generateCursor(ChatActiveStatusEnum.OFFLINE, offlinePage.getCursor());
|
||||
isLast = offlinePage.getIsLast();
|
||||
} else {
|
||||
timeCursor = generateCursor(ChatActiveStatusEnum.ONLINE, onlinePage.getCursor());
|
||||
isLast = false;
|
||||
}
|
||||
} else if (activeStatusEnum == ChatActiveStatusEnum.OFFLINE) {
|
||||
// 离线列表
|
||||
CursorPageBaseResp<User> cursorPage = userDao.getCursorPage(offlineUids, new CursorPageBaseReq(request.getPageSize(), timeCursor));
|
||||
// 添加离线线列表
|
||||
resultList.addAll(MemberAdapter.buildMember(cursorPage.getList(), onlineUids));
|
||||
timeCursor = cursorPage.getCursor();
|
||||
isLast = cursorPage.getIsLast();
|
||||
}
|
||||
// 获取群成员角色ID
|
||||
List<Long> uidList = resultList.stream().map(item -> Long.parseLong(item.getUid())).collect(Collectors.toList());
|
||||
RoomGroup roomGroup = roomGroupCache.getByRoomId(request.getRoomId());
|
||||
|
||||
// 更新角色和群信息
|
||||
Map<String, Integer> uidMapRole = groupMemberDao.getMemberMapRole(roomGroup.getId(), uidList);
|
||||
resultList.forEach(member -> {
|
||||
member.setRoleId(uidMapRole.get(member.getUid()));
|
||||
GroupMember groupMember = groupMemberCache.getMemberDetail(request.getRoomId(), Long.parseLong(member.getUid()));
|
||||
member.setId(groupMember.getId());
|
||||
member.setMyName(groupMember.getMyName());
|
||||
});
|
||||
// 组装结果
|
||||
return new CursorPageBaseResp<>(ChatMemberHelper.generateCursor(activeStatusEnum, timeCursor), isLast, resultList, groupMemberDao.lambdaQuery().eq(GroupMember::getGroupId, roomGroup.getId()).count());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorPageBaseResp<ChatMessageResp> getMsgPage(ChatMessagePageReq request, Long receiveUid) {
|
||||
// 1. 用最后一条消息id,来限制被踢出的人能看见的最大一条消息
|
||||
|
||||
@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.luohuo.basic.cache.repository.CachePlusOps;
|
||||
@@ -14,12 +13,15 @@ import com.luohuo.basic.utils.SpringUtils;
|
||||
import com.luohuo.basic.utils.TimeUtils;
|
||||
import com.luohuo.flex.common.cache.PresenceCacheKeyBuilder;
|
||||
import com.luohuo.flex.im.api.PresenceApi;
|
||||
import com.luohuo.flex.im.common.event.GroupInviteMemberEvent;
|
||||
import com.luohuo.flex.im.core.chat.dao.RoomFriendDao;
|
||||
import com.luohuo.flex.im.core.chat.dao.RoomGroupDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserApplyDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserBackpackDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserFriendDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserPrivacyDao;
|
||||
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.*;
|
||||
import com.luohuo.flex.im.domain.enums.ApplyStatusEnum;
|
||||
import com.luohuo.flex.im.domain.vo.req.room.UserApplyResp;
|
||||
@@ -35,7 +37,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.redis.core.ZSetOperations;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
@@ -102,7 +103,6 @@ import com.luohuo.flex.im.core.user.service.FriendService;
|
||||
import com.luohuo.flex.im.core.user.service.RoleService;
|
||||
import com.luohuo.flex.im.core.user.service.adapter.WsAdapter;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserInfoCache;
|
||||
import com.luohuo.flex.im.core.user.service.impl.PushService;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -111,7 +111,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.luohuo.flex.im.common.config.ThreadPoolConfig.LUOHUO_EXECUTOR;
|
||||
import static com.luohuo.flex.im.core.chat.constant.GroupConst.MAX_MANAGE_COUNT;
|
||||
import static com.luohuo.flex.im.domain.enums.ApplyReadStatusEnum.UNREAD;
|
||||
|
||||
@@ -131,10 +130,10 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
private RoomGroupCache roomGroupCache;
|
||||
private RoomFriendCache roomFriendCache;
|
||||
private CachePlusOps cachePlusOps;
|
||||
private UserInfoCache userInfoCache;
|
||||
private UserCache userCache;
|
||||
private MessageDao messageDao;
|
||||
private HotRoomCache hotRoomCache;
|
||||
private UserCache userCache;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
private RoomAnnouncementsCache roomAnnouncementsCache;
|
||||
private GroupMemberDao groupMemberDao;
|
||||
private UserDao userDao;
|
||||
@@ -371,6 +370,10 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
roomService.createGroupMember(groupId, uid);
|
||||
// 创建系统消息
|
||||
friendService.createSystemFriend(uid);
|
||||
// 发送进群事件
|
||||
CacheKey gKey = PresenceCacheKeyBuilder.groupMembersKey(roomId);
|
||||
CacheKey onlineGroupMembersKey = PresenceCacheKeyBuilder.onlineGroupMembersKey(roomId);
|
||||
SpringUtils.publishEvent(new GroupMemberAddEvent(this, roomId, Math.toIntExact(cachePlusOps.sCard(gKey)), Math.toIntExact(cachePlusOps.sCard(onlineGroupMembersKey)), Arrays.asList(uid), uid));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -673,7 +676,7 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
RoomFriend roomFriend = roomFriendCache.get(request.getRoomId());
|
||||
roomService.updateState(uid.equals(roomFriend.getUid1()), roomFriend.getUid1(), roomFriend.getUid2(), request.getState());
|
||||
|
||||
name = userCache.getUserInfo(roomFriend.getUid1().equals(uid) ? roomFriend.getUid2() : roomFriend.getUid1()).getName();
|
||||
name = userSummaryCache.get(roomFriend.getUid1().equals(uid) ? roomFriend.getUid2() : roomFriend.getUid1()).getName();
|
||||
}
|
||||
|
||||
// 3. 通知所有设备我已经屏蔽这个房间
|
||||
@@ -703,7 +706,7 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
|
||||
// 2. 当是转发单条消息的时候
|
||||
List<Message> messagess = chatService.getMsgByIds(req.getMessageIds());
|
||||
List<MergeMsg> msgs = messagess.stream().filter(message -> message.getRoomId().equals(req.getFromRoomId())).map(message -> new MergeMsg(message.getContent(), message.getCreateTime(), userInfoCache.get(message.getFromUid()).getName())).collect(Collectors.toUnmodifiableList());
|
||||
List<MergeMsg> msgs = messagess.stream().filter(message -> message.getRoomId().equals(req.getFromRoomId())).map(message -> new MergeMsg(message.getContent(), message.getCreateTime(), userCache.get(message.getFromUid()).getName())).collect(Collectors.toUnmodifiableList());
|
||||
|
||||
// 3. 发布合并消息
|
||||
Long msgId = chatService.sendMsg(MessageAdapter.buildMergeMsg(req.getRoomId(), msgs), uid);
|
||||
@@ -741,21 +744,6 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorPageBaseResp<ChatMemberResp> getMemberPage(MemberReq request) {
|
||||
Room room = roomCache.get(request.getRoomId());
|
||||
AssertUtil.isNotEmpty(room, "房间号有误");
|
||||
List<Long> memberUidList;
|
||||
if (isHotGroup(room)) {
|
||||
// 全员群展示所有用户
|
||||
memberUidList = null;
|
||||
} else {// 只展示房间内的群成员
|
||||
RoomGroup roomGroup = roomGroupCache.get(request.getRoomId());
|
||||
memberUidList = groupMemberDao.getMemberUidList(roomGroup.getId(), null);
|
||||
}
|
||||
return chatService.getMemberPage(memberUidList, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChatMemberResp> listMember(MemberReq request) {
|
||||
// 1. 基础校验
|
||||
@@ -769,8 +757,8 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
List<ChatMemberResp> chatMemberResps = groupMemberDao.getMemberListByGroupId(roomGroupCache.get(request.getRoomId()).getId());
|
||||
|
||||
// 3. 批量获取用户信息
|
||||
Set<Long> uids = chatMemberResps.stream().map(ChatMemberResp::getUid).map(Long::parseLong).collect(Collectors.toSet());
|
||||
Map<Long, User> userInfoBatch = userCache.getUserInfoBatch(uids);
|
||||
List<Long> uids = chatMemberResps.stream().map(ChatMemberResp::getUid).map(Long::parseLong).collect(Collectors.toList());
|
||||
Map<Long, SummeryInfoDTO> batch = userSummaryCache.getBatch(uids);
|
||||
|
||||
// 5. 批量获取在线状态
|
||||
Set<Long> onlineList = presenceApi.getOnlineUsersList(new ArrayList<>(uids)).getData();
|
||||
@@ -778,15 +766,19 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
// 6. 填充用户信息和在线状态
|
||||
chatMemberResps.forEach(item -> {
|
||||
Long uid = Long.parseLong(item.getUid());
|
||||
User user = userInfoBatch.get(uid);
|
||||
SummeryInfoDTO user = batch.get(uid);
|
||||
|
||||
if (user != null) {
|
||||
item.setActiveStatus(onlineList.contains(uid) ? ChatActiveStatusEnum.ONLINE.getStatus() : ChatActiveStatusEnum.OFFLINE.getStatus());
|
||||
item.setLastOptTime(user.getLastOptTime());
|
||||
item.setName(user.getName());
|
||||
item.setAvatar(user.getAvatar());
|
||||
item.setLocPlace(user.getLocPlace());
|
||||
item.setAccount(user.getAccount());
|
||||
item.setUserStateId(user.getUserStateId());
|
||||
item.setItemIds(user.getItemIds());
|
||||
item.setUserType(user.getUserType());
|
||||
item.setWearingItemId(user.getWearingItemId());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -817,7 +809,7 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
} else {
|
||||
RoomGroup roomGroup = roomGroupCache.get(room.getId());
|
||||
List<Long> memberUidList = groupMemberDao.getMemberUidList(roomGroup.getId(), null);
|
||||
Map<Long, User> batch = userInfoCache.getBatch(memberUidList);
|
||||
Map<Long, User> batch = userCache.getBatch(memberUidList);
|
||||
return MemberAdapter.buildMemberList(batch);
|
||||
}
|
||||
}
|
||||
@@ -908,14 +900,14 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
|
||||
// 2. 创建邀请记录
|
||||
transactionTemplate.execute(e -> {
|
||||
List<UserApply> invites = validUids.stream().map(inviteeUid -> new UserApply(uid, RoomTypeEnum.GROUP.getType(), roomGroup.getRoomId(), inviteeUid, StrUtil.format("{}邀请你加入{}", userCache.getUserInfo(uid).getName(), roomGroup.getName()), ApplyStatusEnum.WAIT_APPROVAL.getCode(), UNREAD.getCode(), 0, false)).collect(Collectors.toList());
|
||||
List<UserApply> invites = validUids.stream().map(inviteeUid -> new UserApply(uid, RoomTypeEnum.GROUP.getType(), roomGroup.getRoomId(), inviteeUid, StrUtil.format("{}邀请你加入{}", userSummaryCache.get(uid).getName(), roomGroup.getName()), ApplyStatusEnum.WAIT_APPROVAL.getCode(), UNREAD.getCode(), 0, false)).collect(Collectors.toList());
|
||||
userApplyDao.saveBatch(invites);
|
||||
return true;
|
||||
});
|
||||
|
||||
// 3. 通知被邀请的人进群
|
||||
validUids.forEach(inviteId -> {
|
||||
User user = userCache.getUserInfo(inviteId);
|
||||
SummeryInfoDTO user = userSummaryCache.get(inviteId);
|
||||
if(ObjectUtil.isNotNull(user)){
|
||||
pushService.sendPushMsg(MessageAdapter.buildInviteeUserAddGroupMessage(userApplyDao.getUnReadCount(inviteId, inviteId)), inviteId, uid);
|
||||
}
|
||||
@@ -1014,7 +1006,7 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
@Override
|
||||
@RedissonLock(prefixKey = "addGroup:", key = "#uid")
|
||||
public Long addGroup(Long uid, GroupAddReq request) {
|
||||
Map<Long, User> userMap = userCache.getUserInfoBatch(request.getUidList().stream().collect(Collectors.toSet()));
|
||||
Map<Long, SummeryInfoDTO> userMap = userSummaryCache.getBatch(request.getUidList());
|
||||
AssertUtil.isTrue(userMap.size() > 1,"群聊人数应大于2人");
|
||||
|
||||
List<Long> uidList = new ArrayList<>(userMap.keySet());
|
||||
@@ -1044,6 +1036,7 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
cachePlusOps.sAdd(PresenceCacheKeyBuilder.userGroupsKey(id), roomIdAtomic.get());
|
||||
});
|
||||
asyncOnline(uidList, roomIdAtomic.get(), true);
|
||||
SpringUtils.publishEvent(new GroupInviteMemberEvent(this, roomIdAtomic.get(), request.getUidList(), uid));
|
||||
SpringUtils.publishEvent(new GroupMemberAddEvent(this, roomIdAtomic.get(), Math.toIntExact(cachePlusOps.sCard(gKey)), Math.toIntExact(cachePlusOps.sCard(onlineGroupMembersKey)), request.getUidList(), uid));
|
||||
}
|
||||
return roomIdAtomic.get();
|
||||
@@ -1092,7 +1085,6 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
List<Long> msgIds = roomBaseInfoMap.values().stream().map(RoomBaseInfo::getLastMsgId).collect(Collectors.toList());
|
||||
List<Message> messages = CollectionUtil.isEmpty(msgIds) ? new ArrayList<>() : messageDao.listByIds(msgIds);
|
||||
Map<Long, Message> msgMap = messages.stream().collect(Collectors.toMap(Message::getId, Function.identity()));
|
||||
// Map<Long, User> lastMsgUidMap = userInfoCache.getBatch(messages.stream().map(Message::getFromUid).collect(Collectors.toList()));
|
||||
// 消息未读数
|
||||
Map<Long, Integer> unReadCountMap = getUnReadCountMap(contactMap.values());
|
||||
|
||||
@@ -1174,7 +1166,7 @@ public class RoomAppServiceImpl implements RoomAppService, InitializingBean {
|
||||
}
|
||||
Map<Long, RoomFriend> roomFriendMap = roomFriendCache.getBatch(roomIds);
|
||||
Set<Long> friendUidSet = ChatAdapter.getFriendUidSet(roomFriendMap.values(), uid);
|
||||
Map<Long, User> userBatch = userInfoCache.getBatch(new ArrayList<>(friendUidSet));
|
||||
Map<Long, User> userBatch = userCache.getBatch(new ArrayList<>(friendUidSet));
|
||||
return roomFriendMap.values()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(RoomFriend::getRoomId, roomFriend -> {
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.luohuo.flex.im.domain.enums.RoomTypeEnum;
|
||||
import com.luohuo.flex.im.core.chat.service.RoomService;
|
||||
import com.luohuo.flex.im.core.chat.service.adapter.ChatAdapter;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserInfoCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.domain.vo.response.MemberResp;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -46,7 +46,7 @@ public class RoomServiceImpl implements RoomService {
|
||||
private GroupMemberDao groupMemberDao;
|
||||
private AnnouncementsDao announcementsDao;
|
||||
private AnnouncementsReadRecordDao announcementsReadRecordDao;
|
||||
private UserInfoCache userInfoCache;
|
||||
private UserCache userCache;
|
||||
private RoomGroupDao roomGroupDao;
|
||||
|
||||
@Override
|
||||
@@ -86,7 +86,7 @@ public class RoomServiceImpl implements RoomService {
|
||||
public RoomGroup createGroupRoom(Long uid, GroupAddReq groupAddReq) {
|
||||
List<GroupMember> selfGroup = groupMemberDao.getSelfGroup(uid);
|
||||
AssertUtil.isTrue(selfGroup.size() < 20, "每个人只能创建20个群");
|
||||
User user = userInfoCache.get(uid);
|
||||
User user = userCache.get(uid);
|
||||
Room room = createRoom(RoomTypeEnum.GROUP);
|
||||
// 插入群
|
||||
RoomGroup roomGroup = ChatAdapter.buildGroupRoom(user, room.getId(), groupAddReq.getGroupName());
|
||||
@@ -137,7 +137,7 @@ public class RoomServiceImpl implements RoomService {
|
||||
public AnnouncementsResp getAnnouncement(Long id) {
|
||||
AnnouncementsResp resp = new AnnouncementsResp();
|
||||
BeanUtils.copyProperties(announcementsDao.getById(id), resp);
|
||||
User user = userInfoCache.get(resp.getUid());
|
||||
User user = userCache.get(resp.getUid());
|
||||
resp.setUName(user.getName());
|
||||
return resp;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import com.luohuo.flex.im.core.frequencyControl.FrequencyControlException;
|
||||
import com.luohuo.flex.im.core.frequencyControl.constant.FrequencyControlConstant;
|
||||
import com.luohuo.flex.im.core.frequencyControl.dto.FrequencyControlDTO;
|
||||
import com.luohuo.flex.im.core.frequencyControl.util.FrequencyControlUtil;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
@@ -40,18 +40,16 @@ public class WeChatMsgOperationServiceImpl implements WeChatMsgOperationService
|
||||
|
||||
private final String WE_CHAT_MSG_COLOR = "#A349A4";
|
||||
|
||||
@Resource
|
||||
private UserCache userCache;
|
||||
@Resource
|
||||
private UserSummaryCache userSummaryCache;
|
||||
|
||||
@Resource
|
||||
private WxMpService wxMpService;
|
||||
|
||||
@Override
|
||||
public void publishChatMsgToWeChatUser(long senderUid, List<Long> receiverUidList, String msg) {
|
||||
User sender = userCache.getUserInfo(senderUid);
|
||||
Set uidSet = new HashSet();
|
||||
uidSet.addAll(receiverUidList);
|
||||
Map<Long, User> userMap = userCache.getUserInfoBatch(uidSet);
|
||||
SummeryInfoDTO sender = userSummaryCache.get(senderUid);
|
||||
Map<Long, SummeryInfoDTO> userMap = userSummaryCache.getBatch(receiverUidList);
|
||||
userMap.values().forEach(user -> {
|
||||
if (Objects.nonNull(user.getOpenId())) {
|
||||
executor.execute(() -> {
|
||||
@@ -81,7 +79,7 @@ public class WeChatMsgOperationServiceImpl implements WeChatMsgOperationService
|
||||
/*
|
||||
* 构造微信模板消息
|
||||
*/
|
||||
private WxMpTemplateMessage getAtMsgTemplate(User sender, String openId, String msg) {
|
||||
private WxMpTemplateMessage getAtMsgTemplate(SummeryInfoDTO sender, String openId, String msg) {
|
||||
return WxMpTemplateMessage.builder()
|
||||
.toUser(openId)
|
||||
.templateId(atMsgPublishTemplateId)
|
||||
@@ -92,7 +90,7 @@ public class WeChatMsgOperationServiceImpl implements WeChatMsgOperationService
|
||||
/*
|
||||
* 构造微信消息模板的数据
|
||||
*/
|
||||
private List<WxMpTemplateData> generateAtMsgData(User sender, String msg) {
|
||||
private List<WxMpTemplateData> generateAtMsgData(SummeryInfoDTO sender, String msg) {
|
||||
List dataList = new ArrayList<WxMpTemplateData>();
|
||||
// todo: 没有消息模板,暂不实现
|
||||
dataList.add(new WxMpTemplateData("name", sender.getName(), WE_CHAT_MSG_COLOR));
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.luohuo.flex.im.core.chat.service.strategy.msg;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -14,9 +16,6 @@ import com.luohuo.flex.im.domain.enums.MessageTypeEnum;
|
||||
import com.luohuo.flex.im.domain.vo.request.ChatMessageReq;
|
||||
import com.luohuo.flex.im.core.chat.service.adapter.MessageAdapter;
|
||||
import com.luohuo.flex.im.core.chat.service.cache.MsgPlusCache;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@@ -30,7 +29,7 @@ public abstract class AbstractMsgHandler<T> {
|
||||
private MsgPlusCache msgPlusCache;
|
||||
|
||||
@Resource
|
||||
private UserCache userCache;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
|
||||
private Class<T> bodyClass;
|
||||
|
||||
@@ -89,11 +88,11 @@ public abstract class AbstractMsgHandler<T> {
|
||||
if (reply.isPresent()) {
|
||||
Message replyMessage = reply.get();
|
||||
ReplyMsg replyMsgVO = new ReplyMsg();
|
||||
replyMsgVO.setId(replyMessage.getId());
|
||||
replyMsgVO.setUid(replyMessage.getFromUid());
|
||||
replyMsgVO.setId(replyMessage.getId().toString());
|
||||
replyMsgVO.setUid(replyMessage.getFromUid().toString());
|
||||
replyMsgVO.setType(replyMessage.getType());
|
||||
replyMsgVO.setBody(MsgHandlerFactory.getStrategyNoNull(replyMessage.getType()).showReplyMsg(replyMessage));
|
||||
User replyUser = userCache.getUserInfo(replyMessage.getFromUid());
|
||||
SummeryInfoDTO replyUser = userSummaryCache.get(replyMessage.getFromUid());
|
||||
replyMsgVO.setUsername(replyUser.getName());
|
||||
replyMsgVO.setCanCallback(YesOrNoEnum.toStatus(Objects.nonNull(msg.getGapCount()) && msg.getGapCount() <= MessageAdapter.CAN_CALLBACK_GAP_COUNT));
|
||||
replyMsgVO.setGapCount(msg.getGapCount());
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.luohuo.flex.im.common.utils.discover.PrioritizedUrlDiscover;
|
||||
import com.luohuo.flex.im.common.utils.sensitiveword.SensitiveWordBs;
|
||||
import com.luohuo.flex.im.core.chat.dao.MessageDao;
|
||||
import com.luohuo.flex.im.core.user.service.RoleService;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserInfoCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.domain.UrlInfo;
|
||||
import com.luohuo.flex.im.domain.entity.Message;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
@@ -33,7 +33,7 @@ import java.util.stream.Collectors;
|
||||
public class BotMsgHandler extends AbstractMsgHandler<TextMsgReq> {
|
||||
|
||||
private MessageDao messageDao;
|
||||
private UserInfoCache userInfoCache;
|
||||
private UserCache userCache;
|
||||
private RoleService roleService;
|
||||
private SensitiveWordBs sensitiveWordBs;
|
||||
|
||||
@@ -55,7 +55,7 @@ public class BotMsgHandler extends AbstractMsgHandler<TextMsgReq> {
|
||||
if (CollectionUtil.isNotEmpty(body.getAtUidList())) {
|
||||
//前端传入的@用户列表可能会重复,需要去重
|
||||
List<Long> atUidList = body.getAtUidList().stream().distinct().collect(Collectors.toList());
|
||||
Map<Long, User> batch = userInfoCache.getBatch(atUidList);
|
||||
Map<Long, User> batch = userCache.getBatch(atUidList);
|
||||
//如果@用户不存在,userInfoCache 返回的map中依然存在该key,但是value为null,需要过滤掉再校验
|
||||
long batchCount = batch.values().stream().filter(Objects::nonNull).count();
|
||||
AssertUtil.equal((long)atUidList.size(), batchCount, "@用户不存在");
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.luohuo.flex.im.core.chat.service.strategy.msg;
|
||||
|
||||
import com.luohuo.basic.utils.SpringUtils;
|
||||
import com.luohuo.basic.utils.TimeUtils;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.luohuo.flex.im.common.event.MessageRecallEvent;
|
||||
@@ -12,8 +14,6 @@ import com.luohuo.flex.im.domain.entity.msg.MessageExtra;
|
||||
import com.luohuo.flex.im.domain.entity.msg.MsgRecall;
|
||||
import com.luohuo.flex.im.domain.enums.MessageTypeEnum;
|
||||
import com.luohuo.flex.im.core.chat.service.cache.MsgCache;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
@@ -28,7 +28,7 @@ import java.util.Objects;
|
||||
public class RecallMsgHandler extends AbstractMsgHandler<Object> {
|
||||
|
||||
private MessageDao messageDao;
|
||||
private UserCache userCache;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
private MsgCache msgCache;
|
||||
|
||||
@Override
|
||||
@@ -44,7 +44,7 @@ public class RecallMsgHandler extends AbstractMsgHandler<Object> {
|
||||
@Override
|
||||
public Object showMsg(Message msg) {
|
||||
MsgRecall recall = msg.getExtra().getRecall();
|
||||
User userInfo = userCache.getUserInfo(recall.getRecallUid());
|
||||
SummeryInfoDTO userInfo = userSummaryCache.get(recall.getRecallUid());
|
||||
if (!Objects.equals(recall.getRecallUid(), msg.getFromUid())) {
|
||||
return "管理员\"" + userInfo.getName() + "\"撤回了一条成员消息";
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.luohuo.flex.im.domain.vo.response.msg.TextMsgResp;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.domain.enums.RoleTypeEnum;
|
||||
import com.luohuo.flex.im.core.user.service.RoleService;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserInfoCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -33,7 +33,7 @@ import java.util.stream.Collectors;
|
||||
public class TextMsgHandler extends AbstractMsgHandler<TextMsgReq> {
|
||||
|
||||
private MessageDao messageDao;
|
||||
private UserInfoCache userInfoCache;
|
||||
private UserCache userCache;
|
||||
private RoleService roleService;
|
||||
private SensitiveWordBs sensitiveWordBs;
|
||||
|
||||
@@ -55,7 +55,7 @@ public class TextMsgHandler extends AbstractMsgHandler<TextMsgReq> {
|
||||
if (CollectionUtil.isNotEmpty(body.getAtUidList())) {
|
||||
//前端传入的@用户列表可能会重复,需要去重
|
||||
List<Long> atUidList = body.getAtUidList().stream().distinct().collect(Collectors.toList());
|
||||
Map<Long, User> batch = userInfoCache.getBatch(atUidList);
|
||||
Map<Long, User> batch = userCache.getBatch(atUidList);
|
||||
//如果@用户不存在,userInfoCache 返回的map中依然存在该key,但是value为null,需要过滤掉再校验
|
||||
long batchCount = batch.values().stream().filter(Objects::nonNull).count();
|
||||
AssertUtil.equal((long)atUidList.size(), batchCount, "@用户不存在");
|
||||
|
||||
@@ -2,9 +2,8 @@ package com.luohuo.flex.im.core.chat.service.strategy.msg;
|
||||
|
||||
import com.luohuo.basic.context.ContextUtil;
|
||||
import com.luohuo.flex.im.core.chat.dao.MessageDao;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import com.luohuo.flex.im.domain.entity.Message;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.domain.entity.msg.VideoCallMsgDTO;
|
||||
import com.luohuo.flex.im.domain.entity.msg.MessageExtra;
|
||||
import com.luohuo.flex.im.domain.enums.MessageTypeEnum;
|
||||
@@ -22,7 +21,7 @@ import java.util.Optional;
|
||||
public class VideoCallMsgHandler extends AbstractMsgHandler<VideoCallMsgDTO> {
|
||||
|
||||
@Resource
|
||||
private UserCache userCache;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
|
||||
@Override
|
||||
MessageTypeEnum getMsgTypeEnum() {
|
||||
@@ -47,8 +46,7 @@ public class VideoCallMsgHandler extends AbstractMsgHandler<VideoCallMsgDTO> {
|
||||
|
||||
// ===== 群聊场景 =====
|
||||
if (Boolean.TRUE.equals(dto.getIsGroup())) {
|
||||
User user = userCache.getUserInfo(msg.getFromUid());
|
||||
return dto.getBegin() ? user.getName() + "发起了视频通话" : "视频通话已结束";
|
||||
return dto.getBegin() ? userSummaryCache.get(msg.getFromUid()).getName() + "发起了视频通话" : "视频通话已结束";
|
||||
}
|
||||
|
||||
// ===== 私聊场景 =====
|
||||
|
||||
@@ -2,13 +2,8 @@ package com.luohuo.flex.im.core.user.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.luohuo.flex.im.common.enums.NormalOrNoEnum;
|
||||
import com.luohuo.flex.im.domain.vo.req.CursorPageBaseReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ModifyNameReq;
|
||||
import com.luohuo.flex.im.domain.vo.res.CursorPageBaseResp;
|
||||
import com.luohuo.flex.im.common.utils.CursorUtils;
|
||||
import com.luohuo.flex.im.domain.vo.response.ChatMemberListResp;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.core.user.mapper.UserMapper;
|
||||
@@ -32,14 +27,6 @@ public class UserDao extends ServiceImpl<UserMapper, User> {
|
||||
return getOne(wrapper);
|
||||
}
|
||||
|
||||
public void modifyName(Long uid, ModifyNameReq req) {
|
||||
User update = new User();
|
||||
update.setId(uid);
|
||||
update.setName(req.getName());
|
||||
update.setResume(req.getResume());
|
||||
updateById(update);
|
||||
}
|
||||
|
||||
public void wearingBadge(Long uid, Long badgeId) {
|
||||
User update = new User();
|
||||
update.setId(uid);
|
||||
@@ -67,18 +54,6 @@ public class UserDao extends ServiceImpl<UserMapper, User> {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param memberUidList 在线或离线的群成员id
|
||||
*/
|
||||
public CursorPageBaseResp<User> getCursorPage(Set<Long> memberUidList, CursorPageBaseReq request) {
|
||||
if(memberUidList == null || memberUidList.size() == 0){
|
||||
return new CursorPageBaseResp<>();
|
||||
}
|
||||
return CursorUtils.getCursorPageByMysql(this, request, wrapper -> {
|
||||
wrapper.in(CollectionUtils.isNotEmpty(memberUidList), User::getId, memberUidList);//普通群对uid列表做限制
|
||||
}, User::getLastOptTime);
|
||||
}
|
||||
|
||||
public int changeUserState(Long uid, Long userStateId) {
|
||||
return baseMapper.changeUserState(uid, userStateId);
|
||||
}
|
||||
|
||||
@@ -133,23 +133,13 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
return baseMapper.selectObjs(queryWrapper).stream().map(obj -> (Long) obj).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 当uid的朋友改变后需要调用此方法
|
||||
* @param uid
|
||||
* @return
|
||||
*/
|
||||
@CacheEvict(cacheNames = "user", key = "'findGroup'+#uid")
|
||||
public List<Long> evictGroup(Long uid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据房间号+自己的id 定位好友关系
|
||||
* @param roomId
|
||||
* @param uid
|
||||
* @return
|
||||
*/
|
||||
@Cacheable(cacheNames = "userFriend", key = "'room:'+#roomId+':uid:'+#uid", unless = "#result == null")
|
||||
@Cacheable(cacheNames = "luohuo:userFriend", key = "'room:'+#roomId+':uid:'+#uid", unless = "#result == null")
|
||||
public UserFriend getByRoomId(Long roomId, Long uid) {
|
||||
return lambdaQuery()
|
||||
.eq(UserFriend::getRoomId, roomId)
|
||||
@@ -162,7 +152,7 @@ public class UserFriendDao extends ServiceImpl<UserFriendMapper, UserFriend> {
|
||||
* @param roomId 房间ID
|
||||
* @param uid 用户ID
|
||||
*/
|
||||
@CacheEvict(cacheNames = "userFriend", key = "'room:'+#roomId+':uid:'+#uid")
|
||||
@CacheEvict(cacheNames = "luohuo:userFriend", key = "'room:'+#roomId+':uid:'+#uid")
|
||||
public void evictFriendCache(Long roomId, Long uid) {
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,13 @@ package com.luohuo.flex.im.core.user.service;
|
||||
|
||||
import com.luohuo.flex.im.api.vo.UserRegisterVo;
|
||||
import com.luohuo.flex.im.domain.dto.ItemInfoDTO;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.model.entity.base.IpInfo;
|
||||
import com.luohuo.flex.model.vo.query.BindEmailReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.BlackReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ItemInfoReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ModifyAvatarReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ModifyNameReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.SummeryInfoReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.WearingBadgeReq;
|
||||
import com.luohuo.flex.im.domain.vo.resp.user.BadgeResp;
|
||||
import com.luohuo.flex.im.domain.vo.resp.user.UserInfoResp;
|
||||
@@ -25,6 +24,12 @@ import java.util.List;
|
||||
*/
|
||||
public interface UserService {
|
||||
|
||||
/**
|
||||
* 刷新ip信息
|
||||
* @param uid 用户id
|
||||
*/
|
||||
Boolean refreshIpInfo(Long uid, IpInfo ipInfo);
|
||||
|
||||
/**
|
||||
* 校验邮箱是否存在
|
||||
* @param email 邮箱
|
||||
@@ -87,14 +92,6 @@ public interface UserService {
|
||||
|
||||
void black(BlackReq req);
|
||||
|
||||
/**
|
||||
* 获取用户汇总信息
|
||||
*
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
List<SummeryInfoDTO> getSummeryUserInfo(SummeryInfoReq req);
|
||||
|
||||
List<ItemInfoDTO> getItemInfo(ItemInfoReq req);
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.luohuo.flex.im.core.user.service.adapter;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
|
||||
import com.luohuo.flex.im.common.enums.YesOrNoEnum;
|
||||
import com.luohuo.flex.im.domain.entity.ItemConfig;
|
||||
@@ -22,12 +23,6 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class UserAdapter {
|
||||
|
||||
public static User buildUser(String openId) {
|
||||
User user = new User();
|
||||
user.setOpenId(openId);
|
||||
return user;
|
||||
}
|
||||
|
||||
public static User buildAuthorizeUser(Long id, String account, WxOAuth2UserInfo userInfo) {
|
||||
User user = new User();
|
||||
user.setId(id);
|
||||
@@ -43,10 +38,10 @@ public class UserAdapter {
|
||||
return user;
|
||||
}
|
||||
|
||||
public static UserInfoResp buildUserInfoResp(User userInfo, Integer countByValidItemId) {
|
||||
public static UserInfoResp buildUserInfoResp(SummeryInfoDTO userInfo, Integer countByValidItemId) {
|
||||
UserInfoResp userInfoResp = new UserInfoResp();
|
||||
BeanUtil.copyProperties(userInfo, userInfoResp);
|
||||
userInfoResp.setUid(userInfo.getId());
|
||||
userInfoResp.setUid(userInfo.getUid());
|
||||
userInfoResp.setModifyNameChance(countByValidItemId);
|
||||
return userInfoResp;
|
||||
}
|
||||
|
||||
@@ -1,148 +1,39 @@
|
||||
package com.luohuo.flex.im.core.user.service.cache;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.luohuo.basic.cache.repository.CachePlusOps;
|
||||
import com.luohuo.basic.model.cache.CacheKey;
|
||||
import com.luohuo.basic.utils.TimeUtils;
|
||||
import com.luohuo.flex.im.common.constant.RedisKey;
|
||||
import com.luohuo.flex.im.domain.vo.response.ChatMemberListResp;
|
||||
import com.luohuo.flex.im.core.user.dao.BlackDao;
|
||||
import com.luohuo.flex.im.common.service.cache.AbstractRedisStringCache;
|
||||
import com.luohuo.flex.im.core.user.dao.UserDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserRoleDao;
|
||||
import com.luohuo.flex.im.domain.entity.Black;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.domain.entity.UserRole;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author 乾乾
|
||||
* 用户基本信息的缓存
|
||||
* @author nyh
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class UserCache {
|
||||
public class UserCache extends AbstractRedisStringCache<Long, User> {
|
||||
@Resource
|
||||
private UserDao userDao;
|
||||
|
||||
private final UserDao userDao;
|
||||
private final BlackDao blackDao;
|
||||
private final UserRoleDao userRoleDao;
|
||||
private final UserSummaryCache userSummaryCache;
|
||||
private final CachePlusOps cachePlusOps;
|
||||
|
||||
/**
|
||||
* 每小时一执行
|
||||
*/
|
||||
@Scheduled(cron = "0 0 * * * ?")
|
||||
public void cleanExpiredBlacks() {
|
||||
evictBlackMap();
|
||||
}
|
||||
|
||||
public List<Long> getUserModifyTime(List<Long> uidList) {
|
||||
List<String> keys = uidList.stream().map(uid -> RedisKey.getKey(RedisKey.USER_MODIFY_FORMAT, uid)).collect(Collectors.toList());
|
||||
return cachePlusOps.mGet(keys, Long.class);
|
||||
@Override
|
||||
protected String getKey(Long uid) {
|
||||
return RedisKey.getKey(RedisKey.USER_INFO_FORMAT, uid);
|
||||
}
|
||||
|
||||
public void refreshUserModifyTime(Long uid) {
|
||||
String key = RedisKey.getKey(RedisKey.USER_MODIFY_FORMAT, uid);
|
||||
cachePlusOps.set(new CacheKey(key), TimeUtils.getTime());
|
||||
@Override
|
||||
protected Long getExpireSeconds() {
|
||||
return 5 * 60L;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息,盘路缓存模式
|
||||
*/
|
||||
public User getUserInfo(Long uid) {//todo 后期做二级缓存
|
||||
return getUserInfoBatch(Collections.singleton(uid)).get(uid);
|
||||
@Override
|
||||
protected Map<Long, User> load(List<Long> uidList) {
|
||||
List<User> needLoadUserList = userDao.listByIds(uidList);
|
||||
return needLoadUserList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息,盘路缓存模式
|
||||
*/
|
||||
public Map<Long, User> getUserInfoBatch(Set<Long> uids) {
|
||||
//批量组装key
|
||||
List<String> keys = uids.stream().map(a -> RedisKey.getKey(RedisKey.USER_INFO_FORMAT, a)).collect(Collectors.toList());
|
||||
//批量get
|
||||
List<User> mget = cachePlusOps.mGet(keys, User.class);
|
||||
Map<Long, User> map = mget.stream().filter(Objects::nonNull).collect(Collectors.toMap(User::getId, Function.identity()));
|
||||
//发现差集——还需要load更新的uid
|
||||
List<Long> needLoadUidList = uids.stream().filter(a -> !map.containsKey(a)).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(needLoadUidList)) {
|
||||
//批量load
|
||||
List<User> needLoadUserList = userDao.listByIds(needLoadUidList);
|
||||
Map<String, User> redisMap = needLoadUserList.stream().collect(Collectors.toMap(a -> RedisKey.getKey(RedisKey.USER_INFO_FORMAT, a.getId()), Function.identity()));
|
||||
cachePlusOps.mSet(redisMap, 5 * 60);
|
||||
//加载回redis
|
||||
map.putAll(needLoadUserList.stream().collect(Collectors.toMap(User::getId, Function.identity())));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空用户缓存,下次请求最新数据
|
||||
* @param uid 用户id
|
||||
**/
|
||||
public void userInfoChange(Long uid) {
|
||||
cachePlusOps.del(RedisKey.getKey(RedisKey.USER_INFO_FORMAT, uid));
|
||||
|
||||
//删除UserSummaryCache,前端下次懒加载的时候可以获取到最新的数据
|
||||
userSummaryCache.delete(uid);
|
||||
refreshUserModifyTime(uid);
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = "luohuo:user", key = "'blackList'")
|
||||
public Map<Integer, Set<String>> getBlackMap() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Map<Integer, List<Black>> collect = blackDao.getBaseMapper().selectList(new QueryWrapper<Black>().gt("deadline", now)).stream().collect(Collectors.groupingBy(Black::getType));
|
||||
Map<Integer, Set<String>> result = new HashMap<>(collect.size());
|
||||
for (Map.Entry<Integer, List<Black>> entry : collect.entrySet()) {
|
||||
result.put(entry.getKey(), entry.getValue().stream().map(Black::getTarget).collect(Collectors.toSet()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@CacheEvict(cacheNames = "luohuo:user", key = "'blackList'")
|
||||
public Map<Integer, Set<String>> evictBlackMap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = "luohuo:user", key = "'roles'+#uid")
|
||||
public Set<Long> getRoleSet(Long uid) {
|
||||
List<UserRole> userRoles = userRoleDao.listByUid(uid);
|
||||
return userRoles.stream()
|
||||
.map(UserRole::getRoleId)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据key查询好友
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
@Cacheable(cacheNames = "luohuo:user", key = "'findFriend:'+#key")
|
||||
public List<ChatMemberListResp> getFriend(String key) {
|
||||
return userDao.getFriend(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 key 的数据改变后需要调用此方法
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
@CacheEvict(cacheNames = "luohuo:user", key = "'findFriend:'+#key")
|
||||
public List<Long> evictFriend(String key) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.luohuo.flex.im.core.user.service.cache;
|
||||
|
||||
import com.luohuo.flex.im.common.constant.RedisKey;
|
||||
import com.luohuo.flex.im.common.service.cache.AbstractRedisStringCache;
|
||||
import com.luohuo.flex.im.core.user.dao.UserDao;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户基本信息的缓存
|
||||
* @author nyh
|
||||
*/
|
||||
@Component
|
||||
public class UserInfoCache extends AbstractRedisStringCache<Long, User> {
|
||||
@Resource
|
||||
private UserDao userDao;
|
||||
|
||||
@Override
|
||||
protected String getKey(Long uid) {
|
||||
return RedisKey.getKey(RedisKey.USER_INFO_FORMAT, uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getExpireSeconds() {
|
||||
return 5 * 60L;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<Long, User> load(List<Long> uidList) {
|
||||
List<User> needLoadUserList = userDao.listByIds(uidList);
|
||||
return needLoadUserList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,29 @@
|
||||
package com.luohuo.flex.im.core.user.service.cache;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.luohuo.flex.im.common.constant.RedisKey;
|
||||
import com.luohuo.flex.im.common.service.cache.AbstractRedisStringCache;
|
||||
import com.luohuo.flex.im.core.user.dao.BlackDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserBackpackDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserDao;
|
||||
import com.luohuo.flex.im.core.user.dao.UserRoleDao;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import com.luohuo.flex.im.domain.entity.IpDetail;
|
||||
import com.luohuo.flex.im.domain.entity.IpInfo;
|
||||
import com.luohuo.flex.im.domain.entity.Black;
|
||||
import com.luohuo.flex.im.domain.entity.ItemConfig;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.domain.entity.UserBackpack;
|
||||
import com.luohuo.flex.im.domain.entity.UserRole;
|
||||
import com.luohuo.flex.im.domain.enums.ItemTypeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import com.luohuo.flex.im.domain.vo.response.ChatMemberListResp;
|
||||
import com.luohuo.flex.model.entity.base.IpDetail;
|
||||
import com.luohuo.flex.model.entity.base.IpInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -22,13 +33,22 @@ import java.util.stream.Collectors;
|
||||
* @author nyh
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class UserSummaryCache extends AbstractRedisStringCache<Long, SummeryInfoDTO> {
|
||||
@Resource
|
||||
private UserInfoCache userInfoCache;
|
||||
@Resource
|
||||
private UserBackpackDao userBackpackDao;
|
||||
@Resource
|
||||
private ItemCache itemCache;
|
||||
private final UserDao userDao;
|
||||
private final UserCache userCache;
|
||||
private final UserBackpackDao userBackpackDao;
|
||||
private final ItemCache itemCache;
|
||||
private final UserRoleDao userRoleDao;
|
||||
private final BlackDao blackDao;
|
||||
|
||||
/**
|
||||
* 每小时一执行
|
||||
*/
|
||||
@Scheduled(cron = "0 0 * * * ?")
|
||||
public void cleanExpiredBlacks() {
|
||||
evictBlackMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKey(Long uid) {
|
||||
@@ -43,7 +63,7 @@ public class UserSummaryCache extends AbstractRedisStringCache<Long, SummeryInfo
|
||||
@Override
|
||||
protected Map<Long, SummeryInfoDTO> load(List<Long> uidList) {//后续可优化徽章信息也异步加载
|
||||
//用户基本信息
|
||||
Map<Long, User> userMap = userInfoCache.getBatch(uidList);
|
||||
Map<Long, User> userMap = userCache.getBatch(uidList);
|
||||
//用户徽章信息
|
||||
List<ItemConfig> itemConfigs = itemCache.getByType(ItemTypeEnum.BADGE.getType());
|
||||
List<Long> itemIds = itemConfigs.stream().map(ItemConfig::getId).collect(Collectors.toList());
|
||||
@@ -66,7 +86,61 @@ public class UserSummaryCache extends AbstractRedisStringCache<Long, SummeryInfo
|
||||
summeryInfoDTO.setWearingItemId(user.getItemId());
|
||||
summeryInfoDTO.setItemIds(userBackpacks.stream().map(UserBackpack::getItemId).collect(Collectors.toList()));
|
||||
summeryInfoDTO.setUserType(user.getUserType());
|
||||
summeryInfoDTO.setEmail(user.getEmail());
|
||||
summeryInfoDTO.setOpenId(user.getOpenId());
|
||||
summeryInfoDTO.setSex(user.getSex());
|
||||
summeryInfoDTO.setResume(user.getResume());
|
||||
summeryInfoDTO.setLastOptTime(user.getLastOptTime());
|
||||
return summeryInfoDTO;
|
||||
}).filter(Objects::nonNull).collect(Collectors.toMap(SummeryInfoDTO::getUid, Function.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key查询好友
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
@Cacheable(cacheNames = "luohuo:user", key = "'findFriend:'+#key")
|
||||
public List<ChatMemberListResp> getFriend(String key) {
|
||||
return userDao.getFriend(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 key 的数据改变后需要调用此方法
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
@CacheEvict(cacheNames = "luohuo:user", key = "'findFriend:'+#key")
|
||||
public List<Long> evictFriend(String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = "luohuo:user", key = "'roles:'+#uid")
|
||||
public Set<Long> getRoleSet(Long uid) {
|
||||
List<UserRole> userRoles = userRoleDao.listByUid(uid);
|
||||
return userRoles.stream()
|
||||
.map(UserRole::getRoleId)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@CacheEvict(cacheNames = "luohuo:user", key = "'roles'")
|
||||
public Map<Integer, Set<String>> evictrolesSet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = "luohuo:user", key = "'blackList'")
|
||||
public Map<Integer, Set<String>> getBlackMap() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Map<Integer, List<Black>> collect = blackDao.getBaseMapper().selectList(new QueryWrapper<Black>().gt("deadline", now)).stream().collect(Collectors.groupingBy(Black::getType));
|
||||
Map<Integer, Set<String>> result = new HashMap<>(collect.size());
|
||||
for (Map.Entry<Integer, List<Black>> entry : collect.entrySet()) {
|
||||
result.put(entry.getKey(), entry.getValue().stream().map(Black::getTarget).collect(Collectors.toSet()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@CacheEvict(cacheNames = "luohuo:user", key = "'blackList'")
|
||||
public Map<Integer, Set<String>> evictBlackMap() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.luohuo.basic.utils.SpringUtils;
|
||||
import com.luohuo.basic.validator.utils.AssertUtil;
|
||||
import com.luohuo.flex.common.cache.FriendCacheKeyBuilder;
|
||||
import com.luohuo.flex.common.cache.PresenceCacheKeyBuilder;
|
||||
import com.luohuo.flex.im.common.event.GroupInviteMemberEvent;
|
||||
import com.luohuo.flex.im.common.event.GroupMemberAddEvent;
|
||||
import com.luohuo.flex.im.common.event.UserApplyEvent;
|
||||
import com.luohuo.flex.im.common.event.UserApprovalEvent;
|
||||
@@ -30,13 +31,13 @@ import com.luohuo.flex.im.core.user.service.ApplyService;
|
||||
import com.luohuo.flex.im.core.user.service.FriendService;
|
||||
import com.luohuo.flex.im.core.user.service.adapter.FriendAdapter;
|
||||
import com.luohuo.flex.im.core.user.service.adapter.WsAdapter;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import com.luohuo.flex.im.domain.dto.RequestApprovalDto;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import com.luohuo.flex.im.domain.entity.GroupMember;
|
||||
import com.luohuo.flex.im.domain.entity.Room;
|
||||
import com.luohuo.flex.im.domain.entity.RoomFriend;
|
||||
import com.luohuo.flex.im.domain.entity.RoomGroup;
|
||||
import com.luohuo.flex.im.domain.entity.User;
|
||||
import com.luohuo.flex.im.domain.entity.UserApply;
|
||||
import com.luohuo.flex.im.domain.entity.UserFriend;
|
||||
import com.luohuo.flex.im.domain.enums.ApplyDeletedEnum;
|
||||
@@ -84,7 +85,7 @@ public class ApplyServiceImpl implements ApplyService {
|
||||
private ChatService chatService;
|
||||
private RoomCache roomCache;
|
||||
private PushService pushService;
|
||||
private UserCache userCache;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
private GroupMemberCache groupMemberCache;
|
||||
private RoomGroupCache roomGroupCache;
|
||||
private GroupMemberDao groupMemberDao;
|
||||
@@ -145,7 +146,7 @@ public class ApplyServiceImpl implements ApplyService {
|
||||
|
||||
// 获取到这个群的管理员的人的信息
|
||||
List<Long> groupAdminIds = roomService.getGroupUsers(roomGroup.getId(), true);
|
||||
User userInfo = userCache.getUserInfo(uid);
|
||||
SummeryInfoDTO userInfo = userSummaryCache.get(uid);
|
||||
String msg = StrUtil.format("用户{}申请加入群聊{}", userInfo.getName(), roomGroup.getName());
|
||||
|
||||
for (Long groupAdminId : groupAdminIds) {
|
||||
@@ -226,7 +227,7 @@ public class ApplyServiceImpl implements ApplyService {
|
||||
Room room = roomCache.get(invite.getRoomId());
|
||||
GroupMember member = groupMemberDao.getMemberByGroupId(roomGroup.getId(), uid);
|
||||
if(ObjectUtil.isNotNull(member)){
|
||||
throw new BizException(StrUtil.format("{}已经在{}里", userCache.getUserInfo(invite.getTargetId()).getName(), roomGroup.getName()));
|
||||
throw new BizException(StrUtil.format("{}已经在{}里", userSummaryCache.get(invite.getTargetId()).getName(), roomGroup.getName()));
|
||||
}
|
||||
|
||||
transactionTemplate.execute(e -> {
|
||||
@@ -250,6 +251,7 @@ public class ApplyServiceImpl implements ApplyService {
|
||||
cachePlusOps.sAdd(gKey, invite.getTargetId());
|
||||
roomAppService.asyncOnline(Arrays.asList(invite.getTargetId()), room.getId(), true);
|
||||
|
||||
SpringUtils.publishEvent(new GroupInviteMemberEvent(this, room.getId(), Arrays.asList(invite.getTargetId()), invite.getUid()));
|
||||
SpringUtils.publishEvent(new GroupMemberAddEvent(this, room.getId(), Math.toIntExact(cachePlusOps.sCard(gKey)), Math.toIntExact(cachePlusOps.sCard(onlineGroupMembersKey)), Arrays.asList(invite.getTargetId()), invite.getUid()));
|
||||
}
|
||||
}
|
||||
@@ -313,6 +315,7 @@ public class ApplyServiceImpl implements ApplyService {
|
||||
roomAppService.asyncOnline(Arrays.asList(apply.getUid()), room.getId(), true);
|
||||
|
||||
// 5.5 发布成员增加事件
|
||||
SpringUtils.publishEvent(new GroupInviteMemberEvent(this, room.getId(), Arrays.asList(apply.getUid()), apply.getUid()));
|
||||
SpringUtils.publishEvent(new GroupMemberAddEvent(this, room.getId(), Math.toIntExact(cachePlusOps.sCard(gKey)), Math.toIntExact(cachePlusOps.sCard(onlineGroupMembersKey)), Collections.singletonList(apply.getUid()), apply.getUid()));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import com.luohuo.flex.common.cache.FriendCacheKeyBuilder;
|
||||
import com.luohuo.flex.common.cache.PresenceCacheKeyBuilder;
|
||||
import com.luohuo.flex.common.constant.DefValConstants;
|
||||
import com.luohuo.flex.im.api.PresenceApi;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import com.luohuo.flex.im.domain.enums.ApplyReadStatusEnum;
|
||||
import com.luohuo.flex.im.domain.enums.ApplyStatusEnum;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
@@ -42,7 +44,6 @@ import com.luohuo.flex.im.domain.vo.resp.friend.FriendCheckResp;
|
||||
import com.luohuo.flex.im.domain.vo.resp.friend.FriendResp;
|
||||
import com.luohuo.flex.im.core.user.service.FriendService;
|
||||
import com.luohuo.flex.im.core.user.service.adapter.FriendAdapter;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -63,7 +64,7 @@ public class FriendServiceImpl implements FriendService, InitializingBean {
|
||||
private RoomService roomService;
|
||||
private ChatService chatService;
|
||||
private UserDao userDao;
|
||||
private UserCache userCache;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
private BaseRedis baseRedis;
|
||||
private CachePlusOps cachePlusOps;
|
||||
private PresenceApi presenceApi;
|
||||
@@ -151,7 +152,7 @@ public class FriendServiceImpl implements FriendService, InitializingBean {
|
||||
|
||||
@Override
|
||||
public List<ChatMemberListResp> searchFriend(FriendReq friendReq) {
|
||||
return userCache.getFriend(friendReq.getKey());
|
||||
return userSummaryCache.getFriend(friendReq.getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,7 +180,7 @@ public class FriendServiceImpl implements FriendService, InitializingBean {
|
||||
// 发送一条同意消息。。我们已经是好友了,开始聊天吧
|
||||
chatService.sendMsg(MessageAdapter.buildAgreeMsg(roomFriend.getRoomId(), true), uid);
|
||||
// 系统账号在群内发送一条欢迎消息
|
||||
User user = userCache.getUserInfo(uid);
|
||||
SummeryInfoDTO user = userSummaryCache.get(uid);
|
||||
Long total = cachePlusOps.inc("luohuo:user:total_count", 0, TimeUnit.DAYS); // 查询系统总注册人员
|
||||
chatService.sendMsg(MessageAdapter.buildAgreeMsg4Group(DefValConstants.DEF_ROOM_ID, total, user.getName()), DefValConstants.DEF_BOT_ID);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package com.luohuo.flex.im.core.user.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ConcurrentHashSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.luohuo.basic.cache.repository.CachePlusOps;
|
||||
import com.luohuo.basic.model.cache.CacheKey;
|
||||
import com.luohuo.basic.service.MQProducer;
|
||||
import com.luohuo.flex.common.cache.FriendCacheKeyBuilder;
|
||||
import com.luohuo.flex.common.cache.PresenceCacheKeyBuilder;
|
||||
import com.luohuo.flex.common.constant.MqConstant;
|
||||
import com.luohuo.flex.model.entity.dto.NodePushDTO;
|
||||
@@ -140,30 +139,6 @@ public class PushService {
|
||||
log.info("推送线程池已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将消息推送给好友
|
||||
* @param uid 推送给这个uid相关的所有人员
|
||||
* @param type 前端监听的类型
|
||||
* @param data 推送的数据
|
||||
*/
|
||||
public void pushFriends(Long uid, String type, Object data) {
|
||||
// 1. 获取反向好友关系
|
||||
CacheKey reverseKey = FriendCacheKeyBuilder.reverseFriendsKey(uid);
|
||||
Set<Long> reverseFriends = cachePlusOps.sMembers(reverseKey).parallelStream()
|
||||
.map(obj -> Long.parseLong(obj.toString()))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 2. 创建通用响应对象
|
||||
WsBaseResp commonResp = new WsBaseResp();
|
||||
commonResp.setType(type);
|
||||
commonResp.setData(data);
|
||||
|
||||
// 3. 推送给反向好友
|
||||
if (CollUtil.isNotEmpty(reverseFriends)) {
|
||||
sendPushMsg(commonResp, new ArrayList<>(reverseFriends), uid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将消息推送给跟我相关的所有房间
|
||||
* @param uid 推送给这个uid相关的所有人员
|
||||
@@ -173,32 +148,40 @@ public class PushService {
|
||||
public void pushRoom(Long uid, String type, Object data) {
|
||||
// 1. 获取用户所在群聊ID列表
|
||||
CacheKey ugKey = PresenceCacheKeyBuilder.userGroupsKey(uid);
|
||||
Set<Long> roomIds = cachePlusOps.sMembers(ugKey).parallelStream()
|
||||
.map(obj -> Long.parseLong(obj.toString()))
|
||||
.collect(Collectors.toSet());
|
||||
Set<Long> roomIds = cachePlusOps.sMembers(ugKey).parallelStream().map(obj -> Long.parseLong(obj.toString())).collect(Collectors.toSet());
|
||||
|
||||
if (roomIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 创建通用响应对象
|
||||
WsBaseResp commonResp = new WsBaseResp();
|
||||
commonResp.setType(type);
|
||||
commonResp.setData(data);
|
||||
|
||||
// 3. 获取群聊在线成员
|
||||
List<CacheKey> groupKeys = roomIds.stream().map(PresenceCacheKeyBuilder::onlineGroupMembersKey).collect(Collectors.toList());
|
||||
// 3. 获取所有群聊的在线成员并去重
|
||||
Set<Long> allMemberIds = new ConcurrentHashSet<>();
|
||||
|
||||
// 5. 并行处理群聊推送
|
||||
int pageSize = 200;
|
||||
groupKeys.parallelStream().forEach(groupKey -> {
|
||||
// 5.1 检查群是否在线
|
||||
// 并行处理每个群聊的在线成员
|
||||
roomIds.parallelStream().forEach(roomId -> {
|
||||
CacheKey groupKey = PresenceCacheKeyBuilder.onlineGroupMembersKey(roomId);
|
||||
// 检查群是否在线
|
||||
if (cachePlusOps.sCard(groupKey) < 1L) return;
|
||||
|
||||
// 5.2 分批获取群成员
|
||||
List<Long> memberIdList = cachePlusOps.sMembers(groupKey).stream()
|
||||
// 获取群成员并添加到总集合中
|
||||
cachePlusOps.sMembers(groupKey).stream()
|
||||
.map(obj -> Long.parseLong(obj.toString()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 5.3 分批推送
|
||||
Lists.partition(memberIdList, pageSize).forEach(batch -> sendPushMsg(commonResp, batch, uid));
|
||||
.forEach(allMemberIds::add);
|
||||
});
|
||||
|
||||
// 4. 移除自己,避免给自己发送消息
|
||||
// allMemberIds.remove(uid);
|
||||
|
||||
// 5. 分批推送
|
||||
int pageSize = 200;
|
||||
List<Long> memberIdList = new ArrayList<>(allMemberIds);
|
||||
|
||||
Lists.partition(memberIdList, pageSize).forEach(batch -> sendPushMsg(commonResp, batch, uid));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package com.luohuo.flex.im.core.user.service.impl;
|
||||
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.luohuo.flex.im.domain.enums.RoleTypeEnum;
|
||||
import com.luohuo.flex.im.core.user.service.RoleService;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -15,11 +14,11 @@ import java.util.Set;
|
||||
public class RoleServiceImpl implements RoleService {
|
||||
|
||||
@Resource
|
||||
private UserCache userCache;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
|
||||
@Override
|
||||
public boolean hasRole(Long uid, RoleTypeEnum roleTypeEnum) {
|
||||
Set<Long> roleSet = userCache.getRoleSet(uid);
|
||||
Set<Long> roleSet = userSummaryCache.getRoleSet(uid);
|
||||
return isAdmin(roleSet) || roleSet.contains(roleTypeEnum.getId());
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ import com.luohuo.flex.common.cache.PresenceCacheKeyBuilder;
|
||||
import com.luohuo.flex.common.constant.DefValConstants;
|
||||
import com.luohuo.flex.im.api.vo.UserRegisterVo;
|
||||
import com.luohuo.flex.im.common.event.UserRegisterEvent;
|
||||
import com.luohuo.flex.im.core.chat.service.ContactService;
|
||||
import com.luohuo.flex.im.core.chat.service.RoomAppService;
|
||||
import com.luohuo.flex.im.core.chat.service.RoomService;
|
||||
import com.luohuo.flex.im.core.user.service.cache.DefUserCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.model.entity.base.IpInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -44,21 +44,17 @@ import com.luohuo.flex.im.domain.vo.req.user.BlackReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ItemInfoReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ModifyAvatarReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ModifyNameReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.SummeryInfoReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.WearingBadgeReq;
|
||||
import com.luohuo.flex.im.domain.vo.resp.user.BadgeResp;
|
||||
import com.luohuo.flex.im.domain.vo.resp.user.UserInfoResp;
|
||||
import com.luohuo.flex.im.core.user.service.UserService;
|
||||
import com.luohuo.flex.im.core.user.service.adapter.UserAdapter;
|
||||
import com.luohuo.flex.im.core.user.service.cache.ItemCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -71,19 +67,30 @@ import java.util.stream.Collectors;
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
public static final LocalDateTime MAX_DATE = LocalDateTime.of(2099, 12, 31, 00, 00, 00);
|
||||
private final ContactService contactService;
|
||||
private final RoomService roomService;
|
||||
private final RoomAppService roomAppService;
|
||||
private UserCache userCache;
|
||||
private DefUserCache defUserCache;
|
||||
private UserBackpackDao userBackpackDao;
|
||||
private UserDao userDao;
|
||||
private ItemConfigDao itemConfigDao;
|
||||
private ItemCache itemCache;
|
||||
private BlackDao blackDao;
|
||||
private final DefUserCache defUserCache;
|
||||
private final UserBackpackDao userBackpackDao;
|
||||
private final UserDao userDao;
|
||||
private final ItemConfigDao itemConfigDao;
|
||||
private final ItemCache itemCache;
|
||||
private final BlackDao blackDao;
|
||||
private final CachePlusOps cachePlusOps;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
private SensitiveWordBs sensitiveWordBs;
|
||||
private final UserCache userCache;
|
||||
private final UserSummaryCache userSummaryCache;
|
||||
private final SensitiveWordBs sensitiveWordBs;
|
||||
|
||||
@Override
|
||||
public Boolean refreshIpInfo(Long uid, IpInfo ipInfo) {
|
||||
User user = new User();
|
||||
user.setId(uid);
|
||||
user.setIpInfo(ipInfo);
|
||||
boolean updated = userDao.updateById(user);
|
||||
|
||||
// 清空缓存
|
||||
userCache.delete(uid);
|
||||
userSummaryCache.delete(uid);
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkEmail(String email) {
|
||||
@@ -108,7 +115,7 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
public UserInfoResp getUserInfo(Long uid) {
|
||||
User userInfo = userCache.getUserInfo(uid);
|
||||
SummeryInfoDTO userInfo = userSummaryCache.get(uid);
|
||||
Integer countByValidItemId = userBackpackDao.getCountByValidItemId(uid, ItemEnum.MODIFY_NAME_CARD.getId());
|
||||
return UserAdapter.buildUserInfoResp(userInfo, countByValidItemId);
|
||||
}
|
||||
@@ -119,23 +126,27 @@ public class UserServiceImpl implements UserService {
|
||||
// 判断名字是不是重复
|
||||
String newName = req.getName();
|
||||
AssertUtil.isFalse(sensitiveWordBs.hasSensitiveWord(newName), "名字中包含敏感词,请重新输入"); // 判断名字中有没有敏感词
|
||||
// 名称可以重复
|
||||
// User oldUser = userDao.getByName(newName);
|
||||
// AssertUtil.isEmpty(oldUser, "名字已经被抢占了,请换一个哦~~");
|
||||
// 判断改名卡够不够
|
||||
UserBackpack firstValidItem = userBackpackDao.getFirstValidItem(uid, ItemEnum.MODIFY_NAME_CARD.getId());
|
||||
AssertUtil.isNotEmpty(firstValidItem, "改名次数不够了,等后续活动送改名卡哦");
|
||||
// 使用改名卡
|
||||
boolean useSuccess = userBackpackDao.invalidItem(firstValidItem.getId());
|
||||
// 用乐观锁,就不用分布式锁了
|
||||
if (useSuccess) {
|
||||
// 改名
|
||||
userDao.modifyName(uid, req);
|
||||
// 删除缓存
|
||||
userSummaryCache.delete(uid);
|
||||
userCache.userInfoChange(uid);
|
||||
userCache.evictFriend(userCache.getUserInfo(uid).getAccount());
|
||||
}
|
||||
|
||||
User user = userDao.getById(uid);
|
||||
AssertUtil.isTrue(req.getAvatar().equals(user.getAvatar()) ||
|
||||
(user.getAvatarUpdateTime() != null && user.getAvatarUpdateTime().plusDays(30).isBefore(LocalDateTime.now())),
|
||||
"30天内只能修改一次头像");
|
||||
// 更新
|
||||
User update = new User();
|
||||
update.setId(uid);
|
||||
update.setSex(req.getSex());
|
||||
update.setName(req.getName());
|
||||
update.setResume(req.getResume());
|
||||
|
||||
if(StrUtil.isNotEmpty(req.getAvatar()) && !req.getAvatar().equals(user.getAvatar())){
|
||||
update.setAvatar(req.getAvatar());
|
||||
update.setAvatarUpdateTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
userDao.updateById(update);
|
||||
// 删除缓存
|
||||
userSummaryCache.delete(uid);
|
||||
userSummaryCache.evictFriend(userSummaryCache.get(uid).getAccount());
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -154,9 +165,8 @@ public class UserServiceImpl implements UserService {
|
||||
updateUser.setId(user.getId());
|
||||
userDao.updateById(updateUser);
|
||||
// 删除缓存
|
||||
userCache.userInfoChange(uid);
|
||||
userSummaryCache.delete(uid);
|
||||
userCache.evictFriend(user.getAccount());
|
||||
userSummaryCache.evictFriend(user.getAccount());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,8 +190,6 @@ public class UserServiceImpl implements UserService {
|
||||
AssertUtil.equal(itemConfig.getType(), ItemTypeEnum.BADGE.getType(), "该徽章不可佩戴");
|
||||
// 佩戴徽章
|
||||
userDao.wearingBadge(uid, req.getBadgeId());
|
||||
// 删除用户缓存
|
||||
userCache.userInfoChange(uid);
|
||||
userSummaryCache.delete(uid);
|
||||
}
|
||||
|
||||
@@ -203,19 +211,6 @@ public class UserServiceImpl implements UserService {
|
||||
SpringUtils.publishEvent(new UserBlackEvent(this, byId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SummeryInfoDTO> getSummeryUserInfo(SummeryInfoReq req) {
|
||||
//需要前端同步的uid
|
||||
List<Long> uidList = getNeedSyncUidList(req.getReqList());
|
||||
//加载用户信息
|
||||
Map<Long, SummeryInfoDTO> batch = userSummaryCache.getBatch(uidList);
|
||||
return req.getReqList()
|
||||
.stream()
|
||||
.map(a -> batch.containsKey(a.getUid()) ? batch.get(a.getUid()) : SummeryInfoDTO.skip(a.getUid()))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemInfoDTO> getItemInfo(ItemInfoReq req) {//简单做,更新时间可判断被修改
|
||||
return req.getReqList().stream().map(a -> {
|
||||
@@ -261,30 +256,18 @@ public class UserServiceImpl implements UserService {
|
||||
}
|
||||
|
||||
// 3. 修改邮箱
|
||||
User userInfo = userCache.getUserInfo(uid);
|
||||
userInfo.setEmail(req.getEmail());
|
||||
boolean save = userDao.save(userInfo);
|
||||
SummeryInfoDTO userInfo = userSummaryCache.get(uid);
|
||||
User user = new User();
|
||||
user.setId(userInfo.getUid());
|
||||
user.setEmail(req.getEmail());
|
||||
boolean save = userDao.updateById(user);
|
||||
if(save){
|
||||
cachePlusOps.hDel("emailCode", req.getUuid());
|
||||
userCache.userInfoChange(uid);
|
||||
userSummaryCache.delete(uid);
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
private List<Long> getNeedSyncUidList(List<SummeryInfoReq.infoReq> reqList) {
|
||||
List<Long> needSyncUidList = new ArrayList<>();
|
||||
List<Long> userModifyTime = userCache.getUserModifyTime(reqList.stream().map(SummeryInfoReq.infoReq::getUid).collect(Collectors.toList()));
|
||||
for (int i = 0; i < reqList.size(); i++) {
|
||||
SummeryInfoReq.infoReq infoReq = reqList.get(i);
|
||||
Long modifyTime = userModifyTime.get(i);
|
||||
if (Objects.isNull(infoReq.getLastModifyTime()) || (Objects.nonNull(modifyTime) && modifyTime > infoReq.getLastModifyTime())) {
|
||||
needSyncUidList.add(infoReq.getUid());
|
||||
}
|
||||
}
|
||||
return needSyncUidList;
|
||||
}
|
||||
|
||||
public void blackIp(String ip) {
|
||||
if (StrUtil.isBlank(ip)) {
|
||||
return;
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.luohuo.flex.im.domain.vo.resp.user.UserInfoResp;
|
||||
import com.luohuo.flex.im.domain.vo.resp.user.UserStateVo;
|
||||
import com.luohuo.flex.im.core.user.service.UserService;
|
||||
import com.luohuo.flex.im.core.user.service.UserStateService;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -26,7 +25,6 @@ public class UserStateServiceImpl implements UserStateService {
|
||||
|
||||
private UserStateDao userStateDao;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
private UserCache userCache;
|
||||
private UserService userService;
|
||||
private PushService pushService;
|
||||
|
||||
@@ -48,11 +46,10 @@ public class UserStateServiceImpl implements UserStateService {
|
||||
|
||||
if (ObjectUtil.isNotNull(userState) && changeUserState){
|
||||
// 1.清除缓存
|
||||
userCache.userInfoChange(uid);
|
||||
userSummaryCache.delete(uid);
|
||||
|
||||
// 2.推送数据
|
||||
pushService.pushFriends(uid, WSRespTypeEnum.USER_STATE_CHANGE.getType(), new UserStateVo(uid, userState.getId()));
|
||||
pushService.pushRoom(uid, WSRespTypeEnum.USER_STATE_CHANGE.getType(), new UserStateVo(uid, userState.getId()));
|
||||
return true;
|
||||
}else {
|
||||
throw new RuntimeException("用户状态更新失败");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.luohuo.flex.im.controller.chat;
|
||||
|
||||
import com.luohuo.basic.tenant.core.aop.TenantIgnore;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserSummaryCache;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -29,7 +30,6 @@ import com.luohuo.flex.model.entity.ws.ChatMessageResp;
|
||||
import com.luohuo.flex.im.core.chat.service.ChatService;
|
||||
import com.luohuo.flex.im.core.frequencyControl.annotation.FrequencyControl;
|
||||
import com.luohuo.flex.im.domain.enums.BlackTypeEnum;
|
||||
import com.luohuo.flex.im.core.user.service.cache.UserCache;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
@@ -48,10 +48,10 @@ public class ChatController {
|
||||
@Resource
|
||||
private ChatService chatService;
|
||||
@Resource
|
||||
private UserCache userCache;
|
||||
private UserSummaryCache userSummaryCache;
|
||||
|
||||
private Set<String> getBlackUidSet() {
|
||||
return userCache.getBlackMap().getOrDefault(BlackTypeEnum.UID.getType(), new HashSet<>());
|
||||
return userSummaryCache.getBlackMap().getOrDefault(BlackTypeEnum.UID.getType(), new HashSet<>());
|
||||
}
|
||||
|
||||
@GetMapping("/msg/page")
|
||||
|
||||
@@ -71,12 +71,6 @@ public class RoomController {
|
||||
return R.success(roomService.groupList(uid));
|
||||
}
|
||||
|
||||
@GetMapping("/group/member/page")
|
||||
@Operation(summary ="群成员列表")
|
||||
public R<CursorPageBaseResp<ChatMemberResp>> getMemberPage(@Valid MemberReq request) {
|
||||
return R.success(roomService.getMemberPage(request));
|
||||
}
|
||||
|
||||
@GetMapping("/group/listMember")
|
||||
@Operation(summary ="群成员列表")
|
||||
public R<List<ChatMemberResp>> listMember(@Valid MemberReq request) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.luohuo.flex.im.controller.user;
|
||||
|
||||
import com.luohuo.basic.tenant.core.aop.TenantIgnore;
|
||||
import com.luohuo.flex.im.api.vo.UserRegisterVo;
|
||||
import com.luohuo.flex.model.entity.base.RefreshIpInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -11,14 +12,12 @@ import com.luohuo.basic.base.R;
|
||||
import com.luohuo.basic.context.ContextUtil;
|
||||
import com.luohuo.basic.validator.utils.AssertUtil;
|
||||
import com.luohuo.flex.im.domain.dto.ItemInfoDTO;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import com.luohuo.flex.im.domain.enums.RoleTypeEnum;
|
||||
import com.luohuo.flex.model.vo.query.BindEmailReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.BlackReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ItemInfoReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ModifyAvatarReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.ModifyNameReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.SummeryInfoReq;
|
||||
import com.luohuo.flex.im.domain.vo.req.user.WearingBadgeReq;
|
||||
import com.luohuo.flex.im.domain.vo.resp.user.BadgeResp;
|
||||
import com.luohuo.flex.im.domain.vo.resp.user.UserInfoResp;
|
||||
@@ -41,6 +40,12 @@ public class UserController {
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
|
||||
@PostMapping("refreshIpInfo")
|
||||
@Operation(summary ="刷新IP信息、物理地址")
|
||||
public R<Boolean> refreshIpInfo(@RequestBody RefreshIpInfo refreshIpInfo) {
|
||||
return R.success(userService.refreshIpInfo(refreshIpInfo.getUid(), refreshIpInfo.getIpInfo()));
|
||||
}
|
||||
|
||||
@GetMapping("/checkEmail")
|
||||
@Operation(summary ="绑定邮箱")
|
||||
@TenantIgnore
|
||||
@@ -74,6 +79,7 @@ public class UserController {
|
||||
|
||||
@PostMapping("/avatar")
|
||||
@Operation(summary ="修改头像")
|
||||
@Deprecated
|
||||
// @FrequencyControl(target = FrequencyControl.Target.UID, time = 30, unit = TimeUnit.DAYS)
|
||||
public R<Void> modifyAvatar(@Valid @RequestBody ModifyAvatarReq req) {
|
||||
userService.modifyAvatar(ContextUtil.getUid(), req);
|
||||
@@ -86,20 +92,14 @@ public class UserController {
|
||||
return R.success(userService.bindEmail(ContextUtil.getUid(), req));
|
||||
}
|
||||
|
||||
@PostMapping("/summary/userInfo/batch")
|
||||
@Operation(summary ="用户聚合信息-返回的代表需要刷新的")
|
||||
public R<List<SummeryInfoDTO>> getSummeryUserInfo(@Valid @RequestBody SummeryInfoReq req) {
|
||||
return R.success(userService.getSummeryUserInfo(req));
|
||||
}
|
||||
|
||||
@PostMapping("/badges/batch")
|
||||
@Operation(summary ="徽章聚合信息-返回的代表需要刷新的")
|
||||
public R<List<ItemInfoDTO>> getItemInfo(@Valid @RequestBody ItemInfoReq req) {
|
||||
return R.success(userService.getItemInfo(req));
|
||||
}
|
||||
|
||||
@PutMapping("/name")
|
||||
@Operation(summary ="修改用户名")
|
||||
@PutMapping("/info")
|
||||
@Operation(summary ="修改用户信息")
|
||||
public R<Void> modifyInfo(@Valid @RequestBody ModifyNameReq req) {
|
||||
userService.modifyInfo(ContextUtil.getUid(), req);
|
||||
return R.success();
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.luohuo.flex.im.domain.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 修改用户名
|
||||
*
|
||||
@@ -29,20 +29,32 @@ public class SummeryInfoDTO {
|
||||
private Boolean needRefresh = Boolean.TRUE;
|
||||
@Schema(description = "用户昵称")
|
||||
private String name;
|
||||
@Schema(description = "性别")
|
||||
private Integer sex;
|
||||
@Schema(description = "Hula号")
|
||||
private String account;
|
||||
@Schema(description = "用户头像")
|
||||
private String avatar;
|
||||
@Schema(description = "归属地")
|
||||
private String locPlace;
|
||||
@JsonIgnore
|
||||
@Schema(description = "微信openId")
|
||||
private String openId;
|
||||
@JsonIgnore
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
@Schema(description = "个人简介")
|
||||
private String resume;
|
||||
@Schema(description = "用户状态")
|
||||
private Long userStateId;
|
||||
@Schema(description = "佩戴的徽章id")
|
||||
private Long wearingItemId;
|
||||
@Schema(description = "用户类型")
|
||||
private Integer userType;
|
||||
@Schema(description = "最后一次上下线时间")
|
||||
private LocalDateTime lastOptTime;
|
||||
|
||||
public static SummeryInfoDTO skip(Long uid) {
|
||||
public static SummeryInfoDTO skip(Long uid) {
|
||||
SummeryInfoDTO dto = new SummeryInfoDTO();
|
||||
dto.setUid(uid);
|
||||
dto.setNeedRefresh(Boolean.FALSE);
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.luohuo.flex.im.domain.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* 用户ip信息
|
||||
* @author nyh
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IpDetail implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String area;
|
||||
//注册时的ip
|
||||
private String ip;
|
||||
//最新登录的ip
|
||||
private String isp;
|
||||
private String isp_id;
|
||||
private String city;
|
||||
private String city_id;
|
||||
private String country;
|
||||
private String country_id;
|
||||
private String region;
|
||||
private String region_id;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.luohuo.flex.im.domain.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
/**
|
||||
* 用户ip信息
|
||||
* @author nyh
|
||||
*/
|
||||
@Data
|
||||
public class IpInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
//注册时的ip
|
||||
private String createIp;
|
||||
//注册时的ip详情
|
||||
private IpDetail createIpDetail;
|
||||
//最新登录的ip
|
||||
private String updateIp;
|
||||
//最新登录的ip详情
|
||||
private IpDetail updateIpDetail;
|
||||
|
||||
public void refreshIp(String ip) {
|
||||
if (StringUtils.isEmpty(ip)) {
|
||||
return;
|
||||
}
|
||||
updateIp = ip;
|
||||
if (createIp == null) {
|
||||
createIp = ip;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要刷新的ip,这里判断更新ip就够,初始化的时候ip也是相同的,只需要设置的时候多设置进去就行
|
||||
*/
|
||||
public String needRefreshIp() {
|
||||
boolean notNeedRefresh = Optional.ofNullable(updateIpDetail)
|
||||
.map(IpDetail::getIp)
|
||||
.filter(ip -> Objects.equals(ip, updateIp))
|
||||
.isPresent();
|
||||
return notNeedRefresh ? null : updateIp;
|
||||
}
|
||||
|
||||
public void refreshIpDetail(IpDetail ipDetail) {
|
||||
if (Objects.equals(createIp, ipDetail.getIp())) {
|
||||
createIpDetail = ipDetail;
|
||||
}
|
||||
if (Objects.equals(updateIp, ipDetail.getIp())) {
|
||||
updateIpDetail = ipDetail;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.luohuo.basic.base.entity.Entity;
|
||||
import com.luohuo.flex.im.enums.UserTypeEnum;
|
||||
import com.luohuo.flex.model.entity.base.IpInfo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
|
||||
@@ -126,12 +127,4 @@ public class User extends Entity<Long> {
|
||||
*/
|
||||
@TableField(value = "ip_info", typeHandler = JacksonTypeHandler.class)
|
||||
private IpInfo ipInfo;
|
||||
|
||||
public void refreshIp(String ip) {
|
||||
if (ipInfo == null) {
|
||||
ipInfo = new IpInfo();
|
||||
}
|
||||
ipInfo.refreshIp(ip);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import java.io.Serializable;
|
||||
@NoArgsConstructor
|
||||
public class ReplyMsg implements Serializable {
|
||||
@Schema(description = "消息id")
|
||||
private Long id;
|
||||
private String id;
|
||||
@Schema(description = "用户uid")
|
||||
private Long uid;
|
||||
private String uid;
|
||||
@Schema(description = "用户名称")
|
||||
private String username;
|
||||
@Schema(description = "消息类型 1正常文本 2.撤回消息")
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package com.luohuo.flex.im.domain.vo.req.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* 修改用户名
|
||||
* @author nyh
|
||||
@@ -22,6 +21,16 @@ import java.io.Serializable;
|
||||
@NoArgsConstructor
|
||||
public class ModifyNameReq implements Serializable {
|
||||
|
||||
@Schema(description = "性别")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
@NotEmpty
|
||||
@Schema(description = "头像url")
|
||||
private String avatar;
|
||||
|
||||
@NotNull
|
||||
@Length(max = 8, message = "用户名可别取太长,不然我记不住噢")
|
||||
@Schema(description = "用户名")
|
||||
|
||||
@@ -5,6 +5,7 @@ import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author nyh
|
||||
@@ -15,6 +16,12 @@ public class UserInfoResp implements Serializable {
|
||||
@Schema(description = "用户id")
|
||||
private Long uid;
|
||||
|
||||
@Schema(description = "佩戴的徽章id")
|
||||
private Long wearingItemId;
|
||||
|
||||
@Schema(description = "用户拥有的徽章id列表")
|
||||
private List<Long> itemIds;
|
||||
|
||||
@Schema(description = "Hula号")
|
||||
private String account;
|
||||
|
||||
@@ -30,6 +37,9 @@ public class UserInfoResp implements Serializable {
|
||||
@Schema(description = "性别 1男 2女")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "个人简介")
|
||||
private String resume;
|
||||
|
||||
@Schema(description = "用户状态id")
|
||||
private Long userStateId;
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.luohuo.basic.base.R;
|
||||
import com.luohuo.flex.im.api.hystrix.ImUserApiFallback;
|
||||
import com.luohuo.flex.im.api.vo.UserRegisterVo;
|
||||
import com.luohuo.flex.im.domain.vo.resp.user.UserInfoResp;
|
||||
import com.luohuo.flex.model.entity.base.IpInfo;
|
||||
import com.luohuo.flex.model.entity.base.RefreshIpInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
@@ -47,4 +49,11 @@ public interface ImUserApi {
|
||||
*/
|
||||
@PostMapping("/user/register")
|
||||
R<Boolean> register(@Valid @RequestBody UserRegisterVo userRegisterVo);
|
||||
|
||||
/**
|
||||
* 刷新IP信息、物理地址
|
||||
* @param refreshIpInfo ip信息
|
||||
*/
|
||||
@PostMapping("/user/refreshIpInfo")
|
||||
R<Boolean> refreshIpInfo(@RequestBody RefreshIpInfo refreshIpInfo);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.luohuo.flex.im.api.hystrix;
|
||||
import com.luohuo.basic.exception.BizException;
|
||||
import com.luohuo.flex.im.api.vo.UserRegisterVo;
|
||||
import com.luohuo.flex.im.domain.vo.resp.user.UserInfoResp;
|
||||
import com.luohuo.flex.model.entity.base.RefreshIpInfo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.luohuo.basic.base.R;
|
||||
@@ -38,4 +39,9 @@ public class ImUserApiFallback implements ImUserApi {
|
||||
public R<Boolean> register(UserRegisterVo userRegisterVo) {
|
||||
throw BizException.wrap("注册失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Boolean> refreshIpInfo(RefreshIpInfo refreshIpInfo) {
|
||||
return R.success(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class UserOnlineListener {
|
||||
defUserService.updateById(defUser);
|
||||
|
||||
// 更新用户ip详情
|
||||
ipService.refreshIpDetailAsync(userId, ipInfo);
|
||||
ipService.refreshIpDetailAsync(event.getUid(), userId, ipInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import com.luohuo.flex.model.entity.base.IpInfo;
|
||||
public interface IpService {
|
||||
/**
|
||||
* 异步更新用户ip详情
|
||||
*
|
||||
* @param uid 用户id
|
||||
* @param uid Im用户id
|
||||
* @param userId 系统用户id
|
||||
* @param ipInfo IP信息
|
||||
*/
|
||||
void refreshIpDetailAsync(Long uid, IpInfo ipInfo);
|
||||
void refreshIpDetailAsync(Long uid, Long userId, IpInfo ipInfo);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ import com.luohuo.basic.utils.JsonUtils;
|
||||
import com.luohuo.basic.utils.TimeUtils;
|
||||
import com.luohuo.flex.base.entity.tenant.DefUser;
|
||||
import com.luohuo.flex.base.service.tenant.DefUserService;
|
||||
import com.luohuo.flex.im.api.ImUserApi;
|
||||
import com.luohuo.flex.model.entity.base.IpDetail;
|
||||
import com.luohuo.flex.model.entity.base.IpInfo;
|
||||
import com.luohuo.flex.model.entity.base.RefreshIpInfo;
|
||||
import com.luohuo.flex.oauth.service.IpService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -31,18 +33,18 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class IpServiceImpl implements IpService, DisposableBean {
|
||||
private static final ExecutorService EXECUTOR = new ThreadPoolExecutor(1, 1,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>(500),
|
||||
new NamedThreadFactory("refresh-ipDetail",false));
|
||||
|
||||
@Resource
|
||||
private DefUserService defUserService;
|
||||
|
||||
private final ImUserApi userApi;
|
||||
private final DefUserService defUserService;
|
||||
|
||||
@Override
|
||||
public void refreshIpDetailAsync(Long uid, IpInfo ipInfo) {
|
||||
public void refreshIpDetailAsync(Long uid, Long userId, IpInfo ipInfo) {
|
||||
EXECUTOR.execute(() -> {
|
||||
if (Objects.isNull(ipInfo)) {
|
||||
return;
|
||||
@@ -55,11 +57,13 @@ public class IpServiceImpl implements IpService, DisposableBean {
|
||||
if (Objects.nonNull(ipDetail)) {
|
||||
ipInfo.refreshIpDetail(ipDetail);
|
||||
DefUser update = new DefUser();
|
||||
update.setId(uid);
|
||||
update.setId(userId);
|
||||
update.setIpInfo(ipInfo);
|
||||
defUserService.updateById(update);
|
||||
|
||||
userApi.refreshIpInfo(new RefreshIpInfo(uid, ipInfo));
|
||||
} else {
|
||||
log.error("get ip detail fail ip:{},uid:{}", ip, uid);
|
||||
log.error("get ip detail fail ip:{},userId:{}", ip, userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.luohuo.flex.model.entity.base;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户ip信息
|
||||
* @author 乾乾
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class RefreshIpInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
//更新人id
|
||||
private Long uid;
|
||||
//ip信息
|
||||
private IpInfo ipInfo;
|
||||
|
||||
public RefreshIpInfo(Long uid, IpInfo ipInfo) {
|
||||
this.uid = uid;
|
||||
this.ipInfo = ipInfo;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 成员列表的成员信息
|
||||
@@ -53,4 +54,16 @@ public class ChatMemberResp implements Serializable {
|
||||
@Schema(description = "用户状态id")
|
||||
private Long userStateId;
|
||||
|
||||
@Schema(description = "用户拥有的徽章id列表")
|
||||
private List<Long> itemIds;
|
||||
|
||||
@Schema(description = "佩戴的徽章id")
|
||||
private Long wearingItemId;
|
||||
|
||||
/**
|
||||
* 用户类型 UserTypeEnum
|
||||
* 参见 {@link com.luohuo.flex.im.enums.UserTypeEnum}
|
||||
*/
|
||||
@Schema(description = "用户类型")
|
||||
private Integer userType;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ import lombok.NoArgsConstructor;
|
||||
@NoArgsConstructor
|
||||
public class WSOnlineNotify {
|
||||
@Schema(description = "新的上下线用户")
|
||||
private Long uid;
|
||||
private String uid;
|
||||
@Schema(description = "指纹")
|
||||
private String clientId;
|
||||
@Schema(description = "当前房间的在线人数信息")
|
||||
private Long roomId;
|
||||
private String roomId;
|
||||
@Schema(description = "最后一次上下线时间")
|
||||
private Long lastOptTime;
|
||||
@Schema(description = "在线人数")
|
||||
@@ -30,7 +30,7 @@ public class WSOnlineNotify {
|
||||
private Integer type;
|
||||
|
||||
public WSOnlineNotify(Long uid, String clientId, Long lastOptTime, Long onlineNum, Integer type) {
|
||||
this.uid = uid;
|
||||
this.uid = uid+"";
|
||||
this.clientId = clientId;
|
||||
this.lastOptTime = lastOptTime;
|
||||
this.onlineNum = onlineNum;
|
||||
@@ -38,8 +38,8 @@ public class WSOnlineNotify {
|
||||
}
|
||||
|
||||
public WSOnlineNotify(Long roomId, Long uid, String clientId, Long lastOptTime, Long onlineNum, Integer type) {
|
||||
this.roomId = roomId;
|
||||
this.uid = uid;
|
||||
this.roomId = roomId+"";
|
||||
this.uid = uid+"";
|
||||
this.clientId = clientId;
|
||||
this.lastOptTime = lastOptTime;
|
||||
this.onlineNum = onlineNum;
|
||||
|
||||
@@ -257,7 +257,7 @@ public class SessionManager {
|
||||
if (noOtherDevices) {
|
||||
cachePlusOps.zAdd(onlineUsersKey, uid, millis);
|
||||
updateGroupPresence(roomIds, uid, true);
|
||||
pushDeviceStatusChange(roomIds, uid, clientId, true, onlineUsersKey);
|
||||
pushDeviceStatusChange(roomIds, uid, clientId, WSRespTypeEnum.ONLINE.getType(), onlineUsersKey);
|
||||
}
|
||||
} else {
|
||||
// 4. 下线逻辑
|
||||
@@ -267,7 +267,7 @@ public class SessionManager {
|
||||
if (noOtherDevices) {
|
||||
cachePlusOps.zRemove(onlineUsersKey, uid);
|
||||
updateGroupPresence(roomIds, uid, false);
|
||||
pushDeviceStatusChange(roomIds, uid, clientId, false, onlineUsersKey);
|
||||
pushDeviceStatusChange(roomIds, uid, clientId, WSRespTypeEnum.OFFLINE.getType(), onlineUsersKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,12 +276,10 @@ public class SessionManager {
|
||||
* 通知所有与自己有关的所有人
|
||||
* @param uid 登录用户
|
||||
* @param clientId 登录设备
|
||||
* @param online 登录状态
|
||||
* @param type 登录状态
|
||||
* @param onlineKey 全局在线用户的key
|
||||
*/
|
||||
private void pushDeviceStatusChange(List<Long> roomIds, Long uid, String clientId, boolean online, String onlineKey) {
|
||||
String type = online ? WSRespTypeEnum.ONLINE.getType() : WSRespTypeEnum.OFFLINE.getType();
|
||||
|
||||
private void pushDeviceStatusChange(List<Long> roomIds, Long uid, String clientId, String type, String onlineKey) {
|
||||
// 1. 获取反向好友列表(需要知道该用户在线状态的uid) [推送数据各个不一致]
|
||||
CacheKey reverseFriendsKey = FriendCacheKeyBuilder.reverseFriendsKey(uid);
|
||||
Set<Long> friends = cachePlusOps.sMembers(reverseFriendsKey).stream().map(obj -> Long.parseLong(obj.toString())).collect(Collectors.toSet());
|
||||
|
||||
@@ -165,10 +165,10 @@ public final class ContextUtil {
|
||||
/**
|
||||
* 设置子系统id
|
||||
*
|
||||
* @param employeeId 员工id
|
||||
* @param uid 员工id
|
||||
*/
|
||||
public static void setUid(Object employeeId) {
|
||||
set(ContextConstants.U_ID_HEADER, employeeId);
|
||||
public static void setUid(Object uid) {
|
||||
set(ContextConstants.U_ID_HEADER, uid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||