Compare commits

...

23 Commits

Author SHA1 Message Date
easonzhu
1e51780331 完善monitor 2025-04-30 10:07:11 +08:00
easonzhu
45d58895cf 完善monitor 2025-04-28 16:18:10 +08:00
easonzhu
57baf86fbf 细节优化,添加工号字段 2025-04-27 10:29:28 +08:00
easonzhu
9c3877e937 修改鉴权接口 2025-04-14 15:07:14 +08:00
easonzhu
787f48fbdf 接口监控 2025-04-09 18:38:30 +08:00
easonzhu
7b52f60c9e 接口监控 2025-04-09 18:37:15 +08:00
easonzhu
776d22784b 补全字段 2025-04-09 10:51:41 +08:00
easonzhu
ba3728879c 调整生产定时任务运行时间 2025-04-07 10:37:09 +08:00
easonzhu
67ffe48eee 修复结束直播隐藏问题 2025-04-03 11:10:19 +08:00
easonzhu
601d339055 修正统计逻辑 2025-03-28 14:10:44 +08:00
easonzhu
3ca1fe23c1 数据清理脚本 2025-03-06 17:30:14 +08:00
easonzhu
7b9fa77249 修改统计SQL 2025-03-06 15:22:21 +08:00
easonzhu
9c174712d5 修改错误提示 2025-03-06 14:22:09 +08:00
easonzhu
6d22c8c3a1 修正接口字段遗漏 2025-03-04 13:51:24 +08:00
easonzhu
d3fadf8bbe 修正接口字段遗漏 2025-03-04 10:03:28 +08:00
easonzhu
049fba65a8 修正接口字段遗漏 2025-03-03 16:00:33 +08:00
easonzhu
651f1528f6 重构私聊列表和用户列表 2025-03-02 22:32:20 +08:00
easonzhu
a81705ba10 后台获取用户列表 2025-03-01 20:45:13 +08:00
easonzhu
a79e0ef66e 后台获取用户列表 2025-03-01 20:39:34 +08:00
easonzhu
0fbcb50b44 后台获取用户列表 2025-03-01 19:54:34 +08:00
easonzhu
ff52ad5806 合并OnlineUser逻辑 2025-02-28 17:12:33 +08:00
easonzhu
90ef2ed34f 日志脚本 2025-02-28 16:00:16 +08:00
easonzhu
47167a7489 状态机重构完毕 2025-02-28 15:07:35 +08:00
68 changed files with 2297 additions and 399 deletions

View File

@ -0,0 +1,30 @@
@echo off
:: 1. 设置动态参数
set REMOTE_IP=119.29.151.152
set REMOTE_PORT=22101
set REMOTE_PATH=/tmp
set REMOTE_USER=ubuntu
set DEPLOY_SCRIPT=/home/ubuntu/deploy_advisor.sh
:: 2. 将 JAR 包传输到 Linux 服务器
echo Copying JAR file to Linux server...
call scp -P %REMOTE_PORT% target\AdvisorServer-2.6.7.jar %REMOTE_USER%@%REMOTE_IP%:%REMOTE_PATH%
:: 检查 SCP 命令是否成功
IF %ERRORLEVEL% NEQ 0 (
echo File transfer failed, exiting...
exit /b %ERRORLEVEL%
)
:: 3. 通过 SSH 运行 deploy.sh 脚本
echo Running deploy_advisor.sh script on Linux server...
call ssh -p %REMOTE_PORT% %REMOTE_USER%@%REMOTE_IP% "bash %DEPLOY_SCRIPT%"
:: 检查 SSH 命令是否成功
IF %ERRORLEVEL% NEQ 0 (
echo Failed to start the application, exiting...
exit /b %ERRORLEVEL%
)
echo Deployment successful!

42
grep_log.bat Normal file
View File

@ -0,0 +1,42 @@
@echo off
:: 1. 设置动态参数
set REMOTE_IP=119.29.151.152
set REMOTE_PORT=22101
set REMOTE_USER=ubuntu
set SCRIPT_PATH=/home/ubuntu/grep_log.sh
REM 检查是否提供了参数
if "%~1"=="" (
echo Usage: grep_log.bat <KEYWORD> [-A <n>] [-B <n>] [-C <n>]
exit /b 1
)
REM 设置变量
set KEYWORD=%1
shift
REM 解析选项
set OPTIONS=
:parse_options
if "%~1"=="" goto done
if "%~1"=="-A" (
set OPTIONS=-A %2
shift
)
if "%~1"=="-B" (
set OPTIONS=-B %2
shift
)
if "%~1"=="-C" (
set OPTIONS=-C %2
shift
)
shift
goto parse_options
:done
REM 远程执行 Linux 脚本
call ssh -p %REMOTE_PORT% %REMOTE_USER%@%REMOTE_IP% "bash %SCRIPT_PATH% %KEYWORD% %OPTIONS%"

331
html/monitor.html Normal file
View File

@ -0,0 +1,331 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<link href="https://cdn.staticfile.org/element-ui/2.15.14/theme-chalk/index.min.css" rel="stylesheet">
<title>Interface Monitor</title>
<script src="https://cdn.staticfile.org/vue/2.7.14/vue.min.js"></script>
<script src="https://cdn.staticfile.org/blueimp-md5/2.19.0/js/md5.min.js"></script>
<script src="https://cdn.staticfile.org/axios/1.6.5/axios.js"></script>
<script src="https://cdn.staticfile.org/element-ui/2.15.14/index.js"></script>
<script src="https://cdn.staticfile.net/dayjs/1.11.10/dayjs.min.js"></script>
<script src="https://cdn.staticfile.net/echarts/5.4.3/echarts.min.js"></script>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
}
.el-form-item {
display: flex;
height: 50px;
width: 100%;
align-items: center;
justify-content: flex-start;
}
.el-form-item__content {
margin-left: 6px !important;
}
.resu {
margin-left: 360px;
width: 1152px;
min-height: 700px;
}
.line {
width: 1152px;
height: 300px;
}
</style>
</head>
<body>
<div id="app">
<div style="width:320px; position: fixed; padding: 20px 0 0 30px;">
<el-form label-width="80px">
<el-form-item label="Host:">
<div style="width: 108%;">
<el-select @change="init()" placeholder="请选择" v-model="query.host">
<el-option :key="item" :label="item" :value="item" v-for="item in data.host"></el-option>
</el-select>
</div>
</el-form-item>
<el-form-item label="开始时间:">
<el-date-picker @change="init()" placeholder="选择开始时间" type="datetime" v-model="query.startTime">
</el-date-picker>
</el-form-item>
<el-form-item label="结束时间:">
<el-date-picker @change="init()" placeholder="选择结束时间" type="datetime" v-model="query.endTime">
</el-date-picker>
</el-form-item>
<el-form-item label="IP:">
<el-select placeholder="请选择" v-model="query.ip">
<el-option :key="item" :label="item" :value="item" @change="init()" v-for="item in data.ip">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="接口:">
<el-select @change="init(false)" placeholder="请选择" v-model="query.name">
<el-option :key="item" :label="item" :value="item" v-for="item in data.name">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="分组方式:">
<el-radio :label="item" @change="groupData()" v-for="item in data.groupBy" v-model="query.groupBy">{{ item }}</el-radio>
</el-form-item>
<el-form-item>
<div style="margin-left: 20%; display: flex;">
<el-button @click="listData()" type="primary">查询曲线</el-button>
<el-button @click="groupData()" type="primary">查询分组</el-button>
</div>
</el-form-item>
</el-form>
</div>
<div class="resu">
<div id="line" v-show="data.showLine">
<div class="line" id="total"></div>
<div class="line" id="average"></div>
<div class="line" id="failure"></div>
<div class="line" id="timeout"></div>
</div>
<div id="table" v-show="data.showTable">
<el-table :data="data.groupData" :default-sort = "{prop: 'total', order: 'descending'}" @sort-change="sortChange" height="700" >
<el-table-column label="IP" prop="ip" v-if="query.groupBy === 'ip'" width="120"></el-table-column>
<el-table-column label="接口" prop="interfaceName" v-if="query.groupBy === 'name'" width="230"></el-table-column>
<el-table-column label="数量" prop="total" width="78" sortable='custom'></el-table-column>
<el-table-column label="失败" prop="failure" width="78" sortable='custom'></el-table-column>
<el-table-column label="平均" prop="averageTime" width="78" sortable='custom'></el-table-column>
<el-table-column label="最大" prop="maxTime" width="78" sortable='custom'></el-table-column>
<el-table-column label="最小" prop="minTime" width="78" sortable='custom'></el-table-column>
<el-table-column label="10ms" prop="over10ms" width="80" sortable='custom'></el-table-column>
<el-table-column label="50ms" prop="over50ms" width="80" sortable='custom'></el-table-column>
<el-table-column label="100ms" prop="over100ms" width="88" sortable='custom'></el-table-column>
<el-table-column label="500ms" prop="over500ms" width="88" sortable='custom'></el-table-column>
<el-table-column label="1000ms" prop="over1000ms" width="96" sortable='custom'></el-table-column>
<el-table-column label="5000ms" prop="over5000ms" width="96" sortable='custom'></el-table-column>
</el-table>
</div>
</div>
</div>
<script>
const HttpReq = axios.create({
// baseURL, // api的base_url
timeout: 10000, // 请求超时时间,
method: "post"
});
HttpReq.interceptors.request.use(config => {
config.data = config.data || {}
config.data.ticket = md5(`hello_syzb_${dayjs().format('YYYYMMDD')}`)
if(config.data.ip == '全部'){
config.data.ip = ''
}
if(config.data.name == '全部'){
config.data.name = ''
}
return config;
}, error => {
return Promise.reject(error);
});
HttpReq.interceptors.response.use(resp => {
if (resp.code) {
console.log({
title: "HttpRequestError-" + resp.code,
content: resp.message,
});
}
return resp.data.data;
})
function post(host, url, data) {
if (!host) {
return;
}
const targetUrl = (host + url)
return HttpReq({
url: targetUrl,
data,
})
}
function fillTimeData(startTime, endTime, data) {
const result = [];
const timeMap = new Map();
// 建立已有数据的映射key 是时间字符串
data.forEach(item => {
timeMap.set(item.time, item);
});
let current = dayjs(startTime).second(0);
console.log(current.minute());
while (current.minute() % 5 !== 0) {
current = current.add(1, 'minute');
}
const end = dayjs(endTime);
console.log(current.isSameOrBefore);
while (current.isBefore(end) || current.isSame(end)) {
const timeStr = current.format('YYYY-MM-DD HH:mm:ss');
if (timeMap.has(timeStr)) {
result.push(timeMap.get(timeStr));
} else {
result.push({
averageTime: 0,
failure: 0,
interfaceName: null,
ip: null,
maxTime: 0,
minTime: 0,
over1000ms: 0,
over100ms: 0,
over10ms: 0,
over5000ms: 0,
over500ms: 0,
over50ms: 0,
time: timeStr,
total: 0
});
}
current = current.add(5, 'minute');
}
return result;
}
function drawLine(data, title, div, field, filter) {
if (data && data.length) {
const xyData = data.map(v => [v.time, v[field]]);
const chart = echarts.init(document.getElementById(div));
chart.setOption({
title: {
text: title
},
xAxis: {
type: 'time',
// data: xDate
},
yAxis: {
type: 'value'
},
tooltip: {
trigger: 'axis'
},
series: [
{
type: 'line',
data: xyData,
showSymbol: false,
lineStyle: {
color: '#409eff' // 设置折线的颜色为红色
},
}
]
})
}
}
const API = {
listIP: function (host, data) {
return post(host, '/admin/monitor/listIP', data);
},
listInterface: function (host, data) {
return post(host, '/admin/monitor/listInterface', data);
},
listData: function (host, data) {
return post(host, '/admin/monitor/listData', data);
},
groupData: function (host, data) {
return post(host, '/admin/monitor/groupData', data);
},
}
new Vue({
el: '#app',
data() {
return {
query: {
host: "https://do.tgsys.sztg.com",
// startTime: dayjs().format('YYYY-MM-DD 00:00:00'),
// endTime: dayjs().add(1, 'day').format('YYYY-MM-DD 00:00:00'),
startTime: dayjs().format('YYYY-MM-DD 00:00:00'),
endTime: dayjs().add(1, 'day').format('YYYY-MM-DD 00:00:00'),
ip: "",
name: "",
groupBy: "name",
orderBy: "",
},
data: {
host: ["https://do.tgsys.sztg.com", "http://8.138.144.54:8080", "http://127.0.0.1:8080"],
ip: [],
name: [],
groupBy: ["name", "ip"],
groupData: [],
showLine: false,
showTable: false,
},
monitor: [],
interval: null,
};
},
methods: {
async init(loadName = true) {
const ip = await API.listIP(this.query.host, this.query)
if (ip) {
ip.unshift("全部")
this.data.ip = ip;
this.query.ip = ip[0];
}
if (loadName) {
const name = await API.listInterface(this.query.host, this.query)
if(name){
name.unshift("全部")
this.data.name = name;
this.query.name = name[0];
}
}
},
async listData() {
let data = await API.listData(this.query.host, this.query);
// console.log('before', data);
data = fillTimeData(this.query.startTime, this.query.endTime, data);
// console.log('after', data);
drawLine(data, '总流量', 'total', 'total');
drawLine(data, '平均耗时', 'average', 'averageTime');
drawLine(data, '失败流量', 'failure', 'failure');
drawLine(data, '超时流量', 'timeout', 'over5000ms');
this.data.showLine = true;
this.data.showTable = false;
},
async groupData() {
let groupData = await API.groupData(this.query.host, this.query);
groupData = groupData.filter(data => data.interfaceName && data.interfaceName.indexOf('/admin/monitor') === -1);
this.data.groupData = groupData;
this.data.showLine = false;
this.data.showTable = true;
},
async sortChange({column, prop, order}) {
console.log('sortChange', column, prop, order);
this.query.orderBy = prop;
this.query.order = order;
console.log(this.query);
this.groupData();
}
},
async mounted() {
this.init();
},
watch: {
},
});
</script>
</body>
</html>

View File

@ -0,0 +1,26 @@
#!/bin/bash
KEYWORD="3mc31t"
# 目标服务器信息格式IP地址:端口)
SERVERS=(
"172.26.1.3:22"
"172.26.1.7:22"
"172.26.1.9:22"
"172.26.1.15:22"
)
# 目标路径
LOG_DIR="/data/logs/advisor"
for SERVER in "${SERVERS[@]}"; do
# 获取 IP 和端口
IP=$(echo $SERVER | cut -d ':' -f 1)
PORT=$(echo $SERVER | cut -d ':' -f 2)
# 查询日期
echo "log on $IP:"
ssh -p $PORT ubuntu@$IP "grep -n $KEYWORD $LOG_DIR/*.log"
done
echo "All logs printed."

View File

@ -0,0 +1,42 @@
@echo off
:: 1. 设置动态参数
set REMOTE_IP=119.29.151.152
set REMOTE_PORT=22101
set REMOTE_USER=ubuntu
set SCRIPT_PATH=/home/ubuntu/grep_log.sh
REM 检查是否提供了参数
if "%~1"=="" (
echo Usage: grep_log.bat <KEYWORD> [-A <n>] [-B <n>] [-C <n>]
exit /b 1
)
REM 设置变量
set KEYWORD=%1
shift
REM 解析选项
set OPTIONS=
:parse_options
if "%~1"=="" goto done
if "%~1"=="-A" (
set OPTIONS=-A %2
shift
)
if "%~1"=="-B" (
set OPTIONS=-B %2
shift
)
if "%~1"=="-C" (
set OPTIONS=-C %2
shift
)
shift
goto parse_options
:done
REM 远程执行 Linux 脚本
call ssh -p %REMOTE_PORT% %REMOTE_USER%@%REMOTE_IP% "bash %SCRIPT_PATH% %KEYWORD% %OPTIONS%"

153
sql/clear_user.sql Normal file
View File

@ -0,0 +1,153 @@
-- 开始事务
START TRANSACTION;
-- 创建临时表
CREATE TEMPORARY TABLE IF NOT EXISTS temp_user_id AS (
SELECT user_id FROM user_dept WHERE user_id IN (0)
-- union 0 防止结果集为空
UNION ALL
SELECT 0 AS user_id FROM dual
);
CREATE TEMPORARY TABLE IF NOT EXISTS temp_login_id AS (
SELECT login_id FROM user_dept WHERE user_id IN (select user_id from temp_user_id)
-- union 0 防止结果集为空
UNION ALL
SELECT 0 AS login_id FROM dual
);
CREATE TEMPORARY TABLE IF NOT EXISTS temp_advisor_id AS (
SELECT id AS advisor_id FROM advisor_info WHERE user_id IN (select user_id from temp_user_id)
-- union 0 防止结果集为空
UNION ALL
SELECT 0 AS login_id FROM dual
);
CREATE TEMPORARY TABLE IF NOT EXISTS temp_video_id AS (
SELECT id AS video_id FROM video_live WHERE advisor_id IN (select advisor_id from temp_advisor_id)
-- union 0 防止结果集为空
UNION ALL
SELECT 0 AS video_id FROM dual
);
CREATE TEMPORARY TABLE IF NOT EXISTS temp_group_id AS (
SELECT id AS group_id FROM group_info WHERE advisor_id IN (select advisor_id from temp_advisor_id)
-- union 0 防止结果集为空
UNION ALL
SELECT 0 AS group_id FROM dual
);
CREATE TEMPORARY TABLE IF NOT EXISTS temp_group_message_id AS (
SELECT id AS message_id FROM group_message WHERE group_id IN (select group_id from temp_group_id)
-- union 0 防止结果集为空
UNION ALL
SELECT 0 AS message_id FROM dual
);
CREATE TEMPORARY TABLE IF NOT EXISTS temp_course_id AS (
SELECT id AS course_id FROM course WHERE advisor_id IN (select advisor_id from temp_advisor_id)
-- union 0 防止结果集为空
UNION ALL
SELECT 0 AS course_id FROM dual
);
CREATE TEMPORARY TABLE IF NOT EXISTS temp_serial_id AS (
SELECT id AS serial_id FROM serial WHERE advisor_id IN (select advisor_id from temp_advisor_id)
-- union 0 防止结果集为空
UNION ALL
SELECT 0 AS serial_id FROM dual
);
CREATE TEMPORARY TABLE IF NOT EXISTS temp_short_video_id AS (
SELECT id AS video_id FROM short_video WHERE advisor_id IN (select advisor_id from temp_advisor_id)
-- union 0 防止结果集为空
UNION ALL
SELECT 0 AS video_id FROM dual
);
-- 测试临时表
-- SELECT * FROM temp_user_id;
-- SELECT * FROM temp_login_id;
-- SELECT * FROM temp_advisor_id;
-- SELECT * FROM temp_video_id;
-- SELECT * FROM temp_group_id;
-- 清理直播
delete from video_user_watch_collect where video_id in (select video_id from temp_video_id);
delete from video_user_time_collect where video_id in (select video_id from temp_video_id);
delete from video_user_flow where video_id in (select video_id from temp_video_id);
delete from video_live_user where video_id in (select video_id from temp_video_id);
delete from video_live_tag where video_id in (select video_id from temp_video_id);
delete from video_live_risk where video_id in (select video_id from temp_video_id);
delete from video_live_push where video_id in (select video_id from temp_video_id);
delete from video_live_mix where video_id in (select video_id from temp_video_id);
delete from video_live_message where video_id in (select video_id from temp_video_id);
delete from video_live_library where video_id in (select video_id from temp_video_id);
delete from video_live_customer_sale where video_id in (select video_id from temp_video_id);
delete from video_live_column_video where video_id in (select video_id from temp_video_id);
delete from video_live_activity where advisor_id in (select advisor_id from temp_advisor_id);
delete from video_cart where video_id in (select video_id from temp_video_id);
delete from video_browse_detail where video_id in (select video_id from temp_video_id);
delete from video_behavior_notify where video_id in (select video_id from temp_video_id);
delete from video_live where id in (select video_id from temp_video_id);
-- 清理圈子
delete from group_user_flow where group_id in (select group_id from temp_group_id);
delete from group_user_collect where group_id in (select group_id from temp_group_id);
delete from group_message_read where message_id in (select message_id from temp_group_message_id);
delete from group_message where group_id in (select group_id from temp_group_id);
delete from group_collect where group_id in (select group_id from temp_group_id);
delete from group_info where id in (select group_id from temp_group_id);
-- 清理课程
delete from course_content where course_id in (select course_id from temp_course_id);
delete from course where id in (select course_id from temp_course_id);
-- 清理合集
delete from serial_content where serial_id in (select serial_id from temp_serial_id);
delete from serial where id in (select serial_id from temp_serial_id);
-- 清理短视频
delete from short_video_watch where video_id in (select video_id from temp_short_video_id);
delete from short_video_share where video_id in (select video_id from temp_short_video_id);
delete from short_video_sale where video_id in (select video_id from temp_short_video_id);
delete from short_video_favor where video_id in (select video_id from temp_short_video_id);
delete from short_video_cart_click where video_id in (select video_id from temp_short_video_id);
delete from short_video_cart where video_id in (select video_id from temp_short_video_id);
delete from short_video where id in (select video_id from temp_short_video_id);
-- 清理订单
delete from app_order where product_id in (select video_id from temp_video_id) and product_type = 3;
delete from app_order where product_id in (select group_id from temp_group_id) and product_type = 41;
delete from app_order where product_id in (select course_id from temp_course_id) and product_type = 32;
delete from app_order where product_id in (select serial_id from temp_serial_id) and product_type = 33;
delete from app_order where product_id in (select video_id from temp_short_video_id) and product_type = 35;
-- 清理RBAC
delete from user_dept where user_id in (select user_id from temp_user_id);
delete from user where id in (select user_id from temp_user_id);
delete from users_roles where user_id in (select user_id from temp_user_id);
delete from user_login where login_id in (select login_id from temp_login_id);
-- 清理投顾
delete from advisor_info where id in (select advisor_id from temp_advisor_id);
-- 清理临时表
DROP TEMPORARY TABLE temp_user_id;
DROP TEMPORARY TABLE temp_login_id;
DROP TEMPORARY TABLE temp_advisor_id;
DROP TEMPORARY TABLE temp_video_id;
DROP TEMPORARY TABLE temp_group_id;
DROP TEMPORARY TABLE temp_group_message_id;
DROP TEMPORARY TABLE temp_course_id;
DROP TEMPORARY TABLE temp_serial_id;
-- 提交事务
COMMIT;

View File

@ -1,11 +1,14 @@
package com.syzb.advisor.entity;
import com.syzb.rbac.entity.UserLogin;
import java.io.Serializable;
public class AdvisorBasic implements Serializable {
private static final long serialVersionUID = 1L;
public Integer id;
public Integer userId;
public String workNo;
public String name;
public String showName;
public String avatar;
@ -20,9 +23,12 @@ public class AdvisorBasic implements Serializable {
public AdvisorBasic() {
}
public AdvisorBasic(AdvisorInfo advisorInfo, String deptName) {
public AdvisorBasic(AdvisorInfo advisorInfo, String deptName, UserLogin user) {
this.id = advisorInfo.getId();
this.userId = advisorInfo.getUserId();
if (user != null) {
this.workNo = user.getUpId();
}
this.name = advisorInfo.getName();
this.showName = advisorInfo.getShowName();
this.avatar = advisorInfo.getAvatar();
@ -43,6 +49,14 @@ public class AdvisorBasic implements Serializable {
this.userId = userId;
}
public String getWorkNo() {
return workNo;
}
public void setWorkNo(String workNo) {
this.workNo = workNo;
}
public String getProfile() {
return profile;
}

View File

@ -42,7 +42,6 @@ import com.syzb.rbac.entity.UsersRoles;
import com.syzb.rbac.mapper.UsersRolesMapper;
import com.syzb.rbac.service.DeptService;
import com.syzb.rbac.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -98,6 +97,9 @@ public class AdvisorInfoService {
@Value("${advisor.roleId}")
private Integer advisorRoleId;
@Value("${user.admin.roles}")
private Integer adminRoleId;
public static final Set<Integer> TG_SHOW_STATUS = ImmutableSet.of(
AdvisorInfoStatus.PASS.value,
AdvisorInfoStatus.EVENT_SUBMIT.value,
@ -105,34 +107,39 @@ public class AdvisorInfoService {
);
public Map<Integer, AdvisorBasic> getAdvisorMap() {
Map<Integer, UserLogin> userIdLoginMap = userService.getUserUserIdLoginMap();
return cacheService.get(ADVISOR_INFO, AdvisorInfoKey.ADVISOR_MAP, () -> {
List<AdvisorInfo> advisorList = advisorInfoMapper.selectList(Wrappers.emptyWrapper());
return advisorList.stream().collect(Collectors.toMap(AdvisorInfo::getId, advisor ->
new AdvisorBasic(advisor, deptService.getDeptNameByAdvisorId(advisor.getId()))));
new AdvisorBasic(advisor, deptService.getDeptNameByAdvisorId(advisor.getId()), userIdLoginMap.get(advisor.getUserId()))));
});
}
public Map<Integer, AdvisorBasicVO> getAdvisorVoMap() {
Map<Integer, UserLogin> userIdLoginMap = userService.getUserUserIdLoginMap();
return cacheService.get(ADVISOR_INFO, AdvisorInfoKey.ADVISOR_VO_MAP, () -> {
List<AdvisorInfo> advisorList = advisorInfoMapper.selectList(Wrappers.emptyWrapper());
return advisorList.stream().collect(Collectors.toMap(AdvisorInfo::getId, advisor ->
new AdvisorBasicVO(new AdvisorBasic(advisor, deptService.getDeptNameByAdvisorId(advisor.getId())))));
new AdvisorBasicVO(new AdvisorBasic(advisor, deptService.getDeptNameByAdvisorId(advisor.getId()), userIdLoginMap.get(advisor.getUserId())))));
});
}
public Map<Integer, AdvisorBasic> getUserIdAdvisorMap() {
Map<Integer, UserLogin> userIdLoginMap = userService.getUserUserIdLoginMap();
return cacheService.get(ADVISOR_INFO, AdvisorInfoKey.USER_ADVISOR_MAP, () -> {
List<AdvisorInfo> advisorList = advisorInfoMapper.selectList(Wrappers.emptyWrapper());
return advisorList.stream().filter(advisor -> advisor.getUserId() != null).collect(Collectors.toMap(AdvisorInfo::getUserId, advisor ->
new AdvisorBasic(advisor, deptService.getDeptNameByAdvisorId(advisor.getId()))));
new AdvisorBasic(advisor, deptService.getDeptNameByAdvisorId(advisor.getId()), userIdLoginMap.get(advisor.getUserId()))));
});
}
public Map<String, List<AdvisorBasic>> getDeptIdAdvisorMap() {
Map<Integer, UserLogin> userIdLoginMap = userService.getUserUserIdLoginMap();
return cacheService.get(ADVISOR_INFO, AdvisorInfoKey.USER_ADVISOR_DEPT_MAP, () -> {
List<AdvisorInfo> advisorList = advisorInfoMapper.selectList(Wrappers.emptyWrapper());
return advisorList.stream().filter(advisor -> StrUtil.isNotBlank(advisor.getDeptId())).map(advisor ->
new AdvisorBasic(advisor, deptService.getDeptNameByAdvisorId(advisor.getId()))).collect(Collectors.groupingBy(AdvisorBasic::getDeptId));
new AdvisorBasic(advisor, deptService.getDeptNameByAdvisorId(advisor.getId()), userIdLoginMap.get(advisor.getUserId())))
.collect(Collectors.groupingBy(AdvisorBasic::getDeptId));
});
}
@ -150,7 +157,7 @@ public class AdvisorInfoService {
if (dept != null && DeptType.HEAD.value.equals(dept.getType())) {
deptId = null;
}
if (backendUserVO.getRoles().contains(StateMachine.ROLE.ADMIN)) {
if (backendUserVO.getRoles().contains(adminRoleId)) {
deptId = null;
}
Map<Integer, UserLogin> userLoginMap = userService.getUserLoginMap();

View File

@ -13,6 +13,12 @@ public class AdvisorBasicVO implements Serializable {
@ApiModelProperty("用户id")
private Integer userId;
@ApiModelProperty("用户工号")
private String staffNo;
@ApiModelProperty("工号")
private String workNo;
@ApiModelProperty("投顾名称")
private String name;
@ -37,6 +43,8 @@ public class AdvisorBasicVO implements Serializable {
public AdvisorBasicVO(AdvisorBasic advisorBasic) {
this.id = advisorBasic.id;
this.userId = advisorBasic.userId;
this.staffNo = advisorBasic.staffNo;
this.workNo = advisorBasic.workNo;
this.name = advisorBasic.name;
this.showName = advisorBasic.showName;
this.avatar = advisorBasic.avatar;
@ -61,6 +69,23 @@ public class AdvisorBasicVO implements Serializable {
this.userId = userId;
}
public String getStaffNo() {
return staffNo;
}
public void setStaffNo(String staffNo) {
this.staffNo = staffNo;
}
public String getWorkNo() {
return workNo;
}
public void setWorkNo(String workNo) {
this.workNo = workNo;
}
public String getName() {
return name;
}
@ -108,4 +133,5 @@ public class AdvisorBasicVO implements Serializable {
public void setProfile(String profile) {
this.profile = profile;
}
}

View File

@ -1,33 +1,71 @@
package com.syzb.app.controller;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.syzb.common.handler.BizException;
import com.syzb.common.query.AppUserInfoQuery;
import com.syzb.common.query.CheckAuthQuery;
import com.syzb.common.result.CommonResult;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.service.AppUserService;
import com.syzb.common.util.JwtUtil;
import com.syzb.common.util.logger.LoggerUtil;
import com.syzb.common.vo.AppCUserInfoVO;
import com.syzb.common.vo.AuthResultVO;
import com.syzb.common.vo.FrontUserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@Api(tags = "App用户信息")
@RestController
@RequestMapping("/app/user/")
@Validated
public class AppUserController {
@Value("${jwt.key}")
private String jwtKey;
@Value("${jwt.secret}")
private String jwtSecret;
@Resource
private AppUserService appUserService;
@ApiOperation("获取C端用户信息")
@PostMapping("getUserInfo")
@CrossOrigin(origins = "*", allowedHeaders = "*", maxAge = 3600)
public CommonResult<AppCUserInfoVO> getUserInfo(@Validated @RequestBody AppUserInfoQuery query) {
AppCUserInfoVO vo = appUserService.getUserInfo(query);
return CommonResult.success(vo);
}
@ApiOperation("C端用户鉴权")
@PostMapping("checkAuth")
@CrossOrigin(origins = "*", allowedHeaders = "*", maxAge = 3600)
public CommonResult<AuthResultVO> checkAuth(@Validated @RequestBody CheckAuthQuery query) {
String token = query.getToken();
if (StrUtil.isBlank(token) || "undefined".equals(token)) {
throw new BizException(ResponseStatus.SESSION_EXPIRY);
}
FrontUserVO frontUserVO = null;
try {
String decodeStr = JwtUtil.verify(jwtSecret, jwtKey, token);
if (StrUtil.isNotBlank(decodeStr)) {
frontUserVO = JSONObject.parseObject(decodeStr, FrontUserVO.class);
}
} catch (Exception e) {
LoggerUtil.error(e);
throw new BizException(ResponseStatus.SESSION_EXPIRY);
}
if (frontUserVO == null) {
throw new BizException(ResponseStatus.SESSION_EXPIRY);
}
AuthResultVO vo = appUserService.checkAuth(query.getAuth(), frontUserVO);
return CommonResult.success(vo);
}
}

View File

@ -12,11 +12,9 @@ import com.syzb.common.util.logger.LoggerUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;

View File

@ -11,7 +11,6 @@ import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Date;
@Aspect
@Component
@ -36,7 +35,7 @@ public class TaskAspect {
@AfterReturning(pointcut = "scheduledPointcut()")
public void afterReturningCall(JoinPoint joinPoint) {
long start = RequestIdUtil.getTime();
long time = new Date().getTime() - start;
long time = System.currentTimeMillis() - start;
// 获取注解中的参数值
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();

View File

@ -1,8 +1,11 @@
package com.syzb.common.aspect;
import com.alibaba.fastjson.JSONObject;
import com.syzb.common.handler.BizException;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.util.RequestIdUtil;
import com.syzb.common.util.logger.LoggerUtil;
import com.syzb.monitor.service.MonitorService;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
@ -12,17 +15,20 @@ import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Aspect
@Component
public class WebLogAspect {
@Resource
private MonitorService monitorService;
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
private void requestPointcut() {
}
@ -51,8 +57,9 @@ public class WebLogAspect {
}
arguments.add(arg);
}
if (!request.getRequestURI().contains("/swagger") && !request.getRequestURI().contains("/v3/api-docs")) {
LoggerUtil.data.info(String.format("%s:param:%s", request.getRequestURI(), JSONObject.toJSONString(arguments)));
String uri = request.getRequestURI();
if (!uri.contains("/swagger") && !uri.contains("/v3/api-docs")) {
LoggerUtil.data.info(String.format("%s:param:%s", uri, JSONObject.toJSONString(arguments)));
}
}
@ -60,22 +67,29 @@ public class WebLogAspect {
@AfterReturning(returning = "returnOb", pointcut = "requestPointcut() || getPointcut() || postPointcut())")
public void afterReturningCall(JoinPoint joinPoint, Object returnOb) {
long start = RequestIdUtil.getTime();
long time = new Date().getTime() - start;
long time = System.currentTimeMillis() - start;
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
if (!request.getRequestURI().contains("/swagger") && !request.getRequestURI().contains("/v3/api-docs") && !(returnOb instanceof HttpEntity)) {
LoggerUtil.data.info(String.format("%s:耗时:%d", request.getRequestURI(), time));
LoggerUtil.data.info(String.format("%s:result:%s", request.getRequestURI(), JSONObject.toJSONString(returnOb)));
String uri = request.getRequestURI();
if (!uri.contains("/swagger") && !uri.contains("/v3/api-docs") && !(returnOb instanceof HttpEntity)) {
LoggerUtil.data.info(String.format("%s:耗时:%d", uri, time));
LoggerUtil.data.info(String.format("%s:result:%s", uri, JSONObject.toJSONString(returnOb)));
monitorService.add(uri, (int) time);
}
}
// 异常通知
@AfterThrowing(value = "requestPointcut() || getPointcut() || postPointcut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
long start = RequestIdUtil.getTime();
long time = System.currentTimeMillis() - start;
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
if (!request.getRequestURI().contains("/swagger") && !request.getRequestURI().contains("/v3/api-docs")) {
LoggerUtil.data.info(String.format("%s:exception:%s", request.getRequestURI(), ExceptionUtils.getStackTrace(ex)));
String uri = request.getRequestURI();
if (!uri.contains("/swagger") && !uri.contains("/v3/api-docs")) {
LoggerUtil.data.info(String.format("%s:exception:%s", uri, ExceptionUtils.getStackTrace(ex)));
Integer code = ex instanceof BizException ? ((BizException)ex).getErrorCode() : ResponseStatus.SYS_BUSY.code;
monitorService.add(uri, (int) time, code);
}
}

View File

@ -169,6 +169,9 @@ public class TencentCloudConfig {
// 实例化一个http选项可选的没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint(this.vodEndpoint);
httpProfile.setConnTimeout(60);
httpProfile.setReadTimeout(60);
httpProfile.setWriteTimeout(60);
// 实例化一个client选项可选的没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
@ -186,6 +189,9 @@ public class TencentCloudConfig {
// 实例化一个http选项可选的没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint(this.liveEndpoint);
httpProfile.setConnTimeout(60);
httpProfile.setReadTimeout(60);
httpProfile.setWriteTimeout(60);
// 实例化一个client选项可选的没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);

View File

@ -66,6 +66,11 @@ public class CacheConfig {
configMap.put(COURSE, new LocalMapConfig(10000, 300));
configMap.put(GROUP, new LocalMapConfig(10000, 300));
configMap.put(WX_USER, new LocalMapConfig(10000, 3600));
configMap.put(VIDEO_ONLINE_USER, new LocalMapConfig(1000000, 172800, InMemoryFormat.OBJECT));
configMap.put(GROUP_ONLINE_USER, new LocalMapConfig(1000000, 172800, InMemoryFormat.OBJECT));
return configMap;
}
}

View File

@ -305,6 +305,12 @@ public class CacheKey {
public static final String CUSTOMER_SALE = "customer_sale|";
}
public static final String WX_USER = "wx_user";
public static final class WxUserKey {
public static final String USER = "user|";
}
public static final String SCREEN = "screen";
public static final class ScreenKey {

View File

@ -10,9 +10,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;
@Configuration
public class HazelcastConfiguration {
@ -23,6 +21,8 @@ public class HazelcastConfiguration {
@Value("${hazelcast.serverPort}")
private Integer serverPort;
private Set<String> cacheNameSet = new HashSet<>();
@Bean
public HazelcastInstance hazelcastInstance() {
List<String> memberList = Arrays.asList(members.split(","));
@ -39,8 +39,10 @@ public class HazelcastConfiguration {
config.setInstanceName(instanceName);
config.setClusterName(clusterName);
for (Map.Entry<String, CacheConfig.LocalMapConfig> entry : CacheConfig.getConfigMap().entrySet()) {
String cacheName = entry.getKey();
cacheNameSet.add(cacheName);
config.addMapConfig(new MapConfig()
.setName(entry.getKey())
.setName(cacheName)
.setEvictionConfig(new EvictionConfig()
.setEvictionPolicy(EvictionPolicy.LRU)
.setSize(entry.getValue().maxSize)
@ -79,6 +81,12 @@ public class HazelcastConfiguration {
return instance;
}
public void cleanCache() {
for (String cacheName : cacheNameSet) {
hazelcastInstance().getMap(cacheName).clear();
}
}
private static void createMapping(HazelcastInstance instance) {
SqlService sqlService = instance.getSql();
sqlService.execute("CREATE OR REPLACE MAPPING " + CacheKey.VIDEO_ONLINE_USER + " TYPE IMap OPTIONS (" +

View File

@ -0,0 +1,30 @@
package com.syzb.common.controller;
import com.syzb.common.config.cache.HazelcastConfiguration;
import com.syzb.common.result.CommonResult;
import com.syzb.common.result.ResponseStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
@RestController
@Validated
public class CacheController {
@Resource
private HazelcastConfiguration hazelcastConfiguration;
@GetMapping("/admin/cache/clean")
public CommonResult<Void> clean(@Validated @RequestParam("code") @NotBlank String code) {
if (!code.equals("hello_syzb")) {
return CommonResult.error(ResponseStatus.PERMISSION_ERROR);
}
hazelcastConfiguration.cleanCache();
return CommonResult.success();
}
}

View File

@ -4,11 +4,10 @@ import com.hazelcast.map.IMap;
import com.syzb.common.constant.IsOrNot;
import com.syzb.common.constant.ProductType;
import com.syzb.common.entity.OnlineUser;
import com.syzb.common.service.OnlineUserService;
import com.syzb.common.vo.FrontUserVO;
import com.syzb.group.service.common.GroupCacheService;
import com.syzb.group.service.common.GroupMessageService;
import com.syzb.video.helper.VideoHelper;
import com.syzb.video.service.common.VideoCacheService;
import com.syzb.video.service.common.VideoMessageService;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
@ -20,17 +19,14 @@ import java.util.Map;
@Component
public class WebSocketSessionHandler {
@Resource
private VideoCacheService videoCacheService;
@Resource
private VideoMessageService videoMessageService;
@Resource
private GroupCacheService groupCacheService;
private GroupMessageService groupMessageService;
@Resource
private GroupMessageService groupMessageService;
private OnlineUserService onlineUserService;
public void handleConnect(StompHeaderAccessor header) {
Map<String, Object> attributes = header.getSessionAttributes();
@ -88,7 +84,7 @@ public class WebSocketSessionHandler {
}
private void updateOnlineStatus(Integer productType, Integer productId, String sessionKey, OnlineUser onlineUser) {
IMap<String, OnlineUser> totalOnlineMap = getTotalOnlineMap(productType, productId);
IMap<String, OnlineUser> totalOnlineMap = onlineUserService.getTotalOnlineMap(productType);
String cacheKey = VideoHelper.buildOnlineUserCacheKey(productId, sessionKey);
if (totalOnlineMap != null) {
totalOnlineMap.put(cacheKey, onlineUser);
@ -104,7 +100,7 @@ public class WebSocketSessionHandler {
Integer productType = sessionInfo.getProductType();
Integer productId = sessionInfo.getProductId();
String sessionKey = sessionInfo.getSessionKey();
IMap<String, OnlineUser> totalOnlineMap = getTotalOnlineMap(productType, productId);
IMap<String, OnlineUser> totalOnlineMap = onlineUserService.getTotalOnlineMap(productType);
String cacheKey = VideoHelper.buildOnlineUserCacheKey(productId, sessionKey);
OnlineUser onlineUser = totalOnlineMap.get(cacheKey);
if (onlineUser != null) {
@ -120,15 +116,6 @@ public class WebSocketSessionHandler {
onlineUser.setExitTime(LocalDateTime.now());
}
private IMap<String, OnlineUser> getTotalOnlineMap(Integer productType, Integer productId) {
if (ProductType.VIDEO_SINGLE.value.equals(productType)) {
return videoCacheService.getTotalOnlineMap();
} else if (ProductType.GROUP.value.equals(productType)) {
return groupCacheService.getTotalOnlineMap();
}
return null;
}
private void memberNotify(Integer productType, Integer productId, OnlineUser onlineUser) {
if (ProductType.VIDEO_SINGLE.value.equals(productType)) {
videoMessageService.memberNotify(productId, onlineUser);

View File

@ -0,0 +1,24 @@
package com.syzb.common.query;
public class CheckAuthQuery {
private String auth;
private String token;
public String getAuth() {
return auth;
}
public void setAuth(String auth) {
this.auth = auth;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}

View File

@ -131,7 +131,7 @@ public enum ResponseStatus {
STATUS_ERROR(6006, "操作状态不对,不允许操作"),
TAF_ERROR(6007, "TAF接口异常"),
CURRENT_ORDER_ERROR(6008, "限价委托异常"),
STAFF_NO_EXIST(6009, "登录账号或者UP号已存在"),
STAFF_NO_EXIST(6009, "登录账号或者登录账号已存在"),
VIEW_PACKAGE_NAME_EXIST(6010, "观点包名称已存在"),
DEPT_EXIST(6011, "用户部门已存在"),
COUNTER_ERROR(6011, "柜台接口异常"),

View File

@ -28,6 +28,7 @@ import com.syzb.rbac.entity.Dept;
import com.syzb.rbac.entity.UserDept;
import com.syzb.rbac.service.DeptService;
import com.syzb.rbac.service.UserService;
import com.syzb.rbac.service.WxUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
@ -63,6 +64,9 @@ public class CommentBlackService {
@Resource
private DeptService deptService;
@Resource
private WxUserService wxUserService;
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Transactional(rollbackFor = Exception.class)
@ -198,6 +202,7 @@ public class CommentBlackService {
commentBlackVO.setUserOrgName(dept.getName());
}
}
commentBlackVO.setUserHeadPic(wxUserService.getHeadPic(commentBlackVO.getPhone()));
}
return new Pager<>(voList, page.getTotal());
}

View File

@ -0,0 +1,134 @@
package com.syzb.common.service;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.sql.SqlResult;
import com.hazelcast.sql.SqlRow;
import com.syzb.common.config.cache.CacheKey;
import com.syzb.common.constant.ProductType;
import com.syzb.common.entity.OnlineUser;
import com.syzb.common.handler.BizException;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.util.logger.LoggerUtil;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static com.syzb.common.config.cache.CacheKey.*;
@Service
public class OnlineUserService {
@Resource
private HazelcastInstance hazelcastInstance;
/**
* 获取总在线用户映射
*
* @return 在线用户映射
*/
public IMap<String, OnlineUser> getTotalOnlineMap(Integer productType) {
String cacheName = getOnlineCacheName(productType);
return hazelcastInstance.getMap(cacheName);
}
public List<OnlineUser> getTotalOnlineList(Integer productType, Integer productId) {
String cacheName = getOnlineCacheName(productType);
String sql = "SELECT * FROM " + cacheName + " WHERE productId = " + productId;
SqlResult result = hazelcastInstance.getSql().execute(sql);
List<OnlineUser> list = new ArrayList<>();
for (SqlRow row : result) {
OnlineUser user = new OnlineUser();
user.setProductType(row.getObject("productType"));
user.setProductId(row.getObject("productId"));
user.setUserId(row.getObject("userId"));
user.setUserName(row.getObject("userName"));
user.setImg(row.getObject("img"));
user.setSessionId(row.getObject("sessionId"));
user.setIsOnline(row.getObject("isOnline"));
user.setIsPlay(row.getObject("isPlay"));
user.setCreateTime(row.getObject("createTime"));
user.setExitTime(row.getObject("exitTime"));
list.add(user);
}
return list;
}
public Set<String> getOnlineUserIds(Integer productType, Integer productId) {
String cacheName = getOnlineCacheName(productType);
String sql = "SELECT distinct userId FROM " + cacheName + " WHERE productId = " + productId + " and isOnline = 1";
SqlResult result = hazelcastInstance.getSql().execute(sql);
Set<String> set = new HashSet<>();
for (SqlRow row : result) {
set.add(row.getObject("userId"));
}
return set;
}
/**
* 获取在线人数
*
* @return 在线人数
*/
public int getOnlineCount(Integer productType, Integer productId) {
String mainCacheName = getMainCacheName(productType);
IMap<String, Object> map = hazelcastInstance.getMap(mainCacheName);
String cacheKey = getOnlineCountCacheKey(productType, productId);
Integer onlineCount = (Integer) map.get(cacheKey);
if (onlineCount != null) {
return onlineCount;
}
synchronized (this) {
onlineCount = (Integer) map.get(cacheKey);
if (onlineCount != null) {
return onlineCount;
}
long startTime = System.currentTimeMillis();
onlineCount = getOnlineCountWithoutCache(productType, productId);
LoggerUtil.websocket.info("getOnlineCount-" + productType + "-" + productId + ":" + (System.currentTimeMillis() - startTime) + "ms");
map.put(cacheKey, onlineCount, 2, TimeUnit.SECONDS);
}
return onlineCount;
}
public int getOnlineCountWithoutCache(Integer productType, Integer productId) {
String cacheName = getOnlineCacheName(productType);
String sql = "SELECT COUNT(*) FROM " + cacheName + " WHERE productId = " + productId + " and isOnline = 1";
SqlResult result = hazelcastInstance.getSql().execute(sql);
Iterator<SqlRow> iter = result.iterator();
if (iter.hasNext()) {
return ((Long)iter.next().getObject(0)).intValue();
}
return 0;
}
private String getMainCacheName(Integer productType) {
if (ProductType.VIDEO_SINGLE.value.equals(productType)) {
return VIDEO_LIVE;
} else if (ProductType.GROUP.value.equals(productType)) {
return GROUP;
}
throw new BizException(ResponseStatus.SYS_BUSY, "productType错误");
}
private String getOnlineCacheName(Integer productType) {
if (ProductType.VIDEO_SINGLE.value.equals(productType)) {
return VIDEO_ONLINE_USER;
} else if (ProductType.GROUP.value.equals(productType)) {
return GROUP_ONLINE_USER;
}
throw new BizException(ResponseStatus.SYS_BUSY, "productType错误");
}
private String getOnlineCountCacheKey(Integer productType, Integer productId) {
if (ProductType.VIDEO_SINGLE.value.equals(productType)) {
return CacheKey.VideoLiveKey.ONLINE_COUNT + productId;
} else if (ProductType.GROUP.value.equals(productType)) {
return CacheKey.GroupKey.ONLINE_COUNT + productId;
}
throw new BizException(ResponseStatus.SYS_BUSY, "productType错误");
}
}

View File

@ -18,14 +18,14 @@ public class AdvisorInfoStateMachine {
advisorInfoSM.add(AdvisorInfoStatus.INIT, AdvisorInfoStatus.EVENT_UPDATE, StateMachine.ROLE.ADVISOR, AdvisorInfoStatus.INIT);
// 待审核 -> 管理员[驳回] -> 已驳回
advisorInfoSM.add(AdvisorInfoStatus.TO_AUDIT, AdvisorInfoStatus.EVENT_REJECT, StateMachine.ROLE.ADMIN, AdvisorInfoStatus.REJECTED);
advisorInfoSM.add(AdvisorInfoStatus.TO_AUDIT, AdvisorInfoStatus.EVENT_REJECT, StateMachine.ROLE.NOT_ADVISOR, AdvisorInfoStatus.REJECTED);
// 待审核 -> 管理员[通过] -> 一级审核通过
advisorInfoSM.add(AdvisorInfoStatus.TO_AUDIT, AdvisorInfoStatus.EVENT_PASS, StateMachine.ROLE.ADMIN, AdvisorInfoStatus.PASS);
advisorInfoSM.add(AdvisorInfoStatus.TO_AUDIT, AdvisorInfoStatus.EVENT_PASS, StateMachine.ROLE.NOT_ADVISOR, AdvisorInfoStatus.PASS);
// 已上架 -> 管理员[下架] -> 已下架
advisorInfoSM.add(AdvisorInfoStatus.PASS, AdvisorInfoStatus.EVENT_SOLD_OUT, StateMachine.ROLE.ADMIN, AdvisorInfoStatus.SOLD_OUT);
advisorInfoSM.add(AdvisorInfoStatus.PASS, AdvisorInfoStatus.EVENT_SOLD_OUT, StateMachine.ROLE.NOT_ADVISOR, AdvisorInfoStatus.SOLD_OUT);
// 已上架 -> 管理员[编辑] -> 已上架
advisorInfoSM.add(AdvisorInfoStatus.PASS, AdvisorInfoStatus.EVENT_UPDATE, StateMachine.ROLE.ADMIN, AdvisorInfoStatus.PASS);
advisorInfoSM.add(AdvisorInfoStatus.PASS, AdvisorInfoStatus.EVENT_UPDATE, StateMachine.ROLE.NOT_ADVISOR, AdvisorInfoStatus.PASS);
// 已驳回 -> 投顾[编辑] -> 已驳回
advisorInfoSM.add(AdvisorInfoStatus.REJECTED, AdvisorInfoStatus.EVENT_UPDATE, StateMachine.ROLE.ADVISOR, AdvisorInfoStatus.REJECTED);
@ -37,9 +37,9 @@ public class AdvisorInfoStateMachine {
// 已下架 > 投顾[发布] -> 待审核
advisorInfoSM.add(AdvisorInfoStatus.SOLD_OUT, AdvisorInfoStatus.EVENT_UPDATE, StateMachine.ROLE.ADVISOR, AdvisorInfoStatus.TO_AUDIT);
// 已下架 -> 管理员[上架] -> 已上架
advisorInfoSM.add(AdvisorInfoStatus.SOLD_OUT, AdvisorInfoStatus.EVENT_PUT_ON, StateMachine.ROLE.ADMIN, AdvisorInfoStatus.PASS);
advisorInfoSM.add(AdvisorInfoStatus.SOLD_OUT, AdvisorInfoStatus.EVENT_PUT_ON, StateMachine.ROLE.NOT_ADVISOR, AdvisorInfoStatus.PASS);
// 已下架 -> 管理员[编辑] -> 已下架
advisorInfoSM.add(AdvisorInfoStatus.SOLD_OUT, AdvisorInfoStatus.EVENT_UPDATE, StateMachine.ROLE.ADMIN, AdvisorInfoStatus.SOLD_OUT);
advisorInfoSM.add(AdvisorInfoStatus.SOLD_OUT, AdvisorInfoStatus.EVENT_UPDATE, StateMachine.ROLE.NOT_ADVISOR, AdvisorInfoStatus.SOLD_OUT);
return advisorInfoSM;
}

View File

@ -6,9 +6,7 @@ import com.google.common.collect.Table;
import com.syzb.common.handler.BizException;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.vo.BackendUserVO;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
/**
@ -20,20 +18,14 @@ import java.util.Map;
public class StateMachine<T> {
public static class ROLE {
public static final int ADMIN = 1;
public static final int ADVISOR = 2;
public static final int COMPLIANCE = 3; // 一级审核
public static final int BUSINESS = 4; // 二级审核
public static final int OPERATION = 5; // 三级审核
public static final int ADVISOR = 1;
public static final int NOT_ADVISOR = 2;
private static final Map<Integer, String> MAP = MapUtil.newHashMap();
static {
MAP.put(ADMIN, "管理员");
MAP.put(ADVISOR, "投顾");
MAP.put(COMPLIANCE, "合规");
MAP.put(BUSINESS, "业务负责人");
MAP.put(OPERATION, "运营");
MAP.put(NOT_ADVISOR, "非投顾");
}
}
@ -53,13 +45,6 @@ public class StateMachine<T> {
table.put(source, event + "|" + role, target);
}
// 添加状态事件(不同角色特殊处理比如下架产品投顾编辑后为待提交状态运营人员编辑后仍未下架状态)
public void add(T source, T event, List<Integer> roles, T target) {
for (int role : roles) {
table.put(source, event + "|" + role, target);
}
}
// 添加状态事件(事件和目标相同,省略参数)
public void add(T source, T target) {
table.put(source, target, target);
@ -87,34 +72,13 @@ public class StateMachine<T> {
return target;
}
String statusMsg = "原状态:[" + source + "]";
String roleMsg = role == null ? "" : ",角色:[" + (ROLE.ADMIN == role ? "管理员" : "投顾") + "]";
String roleMsg = role == null ? "" : ",角色:[" + (ROLE.MAP.getOrDefault(role, role.toString())) + "]";
String eventMsg = ",不能执行:[" + event + "]动作";
throw new BizException(ResponseStatus.STATUS_ERROR, statusMsg + roleMsg + eventMsg);
}
public T send(T source, T event, List<Integer> roles) {
T target = null;
for (Integer role : roles) {
target = this.send(source, event, role);
}
if (target == null) {
String statusMsg = "原状态:[" + source + "]";
String roleMsg = "";
if (!CollectionUtils.isEmpty(roles)) {
roleMsg = " 角色:[";
for (Integer role : roles) {
roleMsg += "," + ROLE.MAP.get(role);
}
roleMsg += "]";
}
String eventMsg = ",不能执行:[" + event + "]动作";
throw new BizException(ResponseStatus.STATUS_ERROR, statusMsg + roleMsg + eventMsg);
}
return target;
}
public T send(T source, T event, BackendUserVO userVO) {
int role = userVO.getAdvisorId() == null ? StateMachine.ROLE.ADMIN : StateMachine.ROLE.ADVISOR;
int role = userVO.getAdvisorId() == null ? ROLE.NOT_ADVISOR : StateMachine.ROLE.ADVISOR;
return this.send(source, event, role);
}

View File

@ -16,15 +16,15 @@ public class VideoActivityStateMachine {
public StateMachine<VideoActivityStatus> videoActivityStatusSm() {
StateMachine<VideoActivityStatus> activityStatusSm = new StateMachine<>();
// 待审核 [运营人员]审核 通过 上架
activityStatusSm.add(VideoActivityStatus.TO_AUDIT, VideoActivityStatus.EVENT_PASS, StateMachine.ROLE.ADMIN, VideoActivityStatus.PASS);
activityStatusSm.add(VideoActivityStatus.TO_AUDIT, VideoActivityStatus.EVENT_PASS, StateMachine.ROLE.NOT_ADVISOR, VideoActivityStatus.PASS);
// 待审核 [运营人员]审核 驳回 已驳回
activityStatusSm.add(VideoActivityStatus.TO_AUDIT, VideoActivityStatus.EVENT_REJECT, StateMachine.ROLE.ADMIN, VideoActivityStatus.REJECTED);
activityStatusSm.add(VideoActivityStatus.TO_AUDIT, VideoActivityStatus.EVENT_REJECT, StateMachine.ROLE.NOT_ADVISOR, VideoActivityStatus.REJECTED);
// 已驳回 [投顾]编辑 待审核
activityStatusSm.add(VideoActivityStatus.REJECTED, VideoActivityStatus.EVENT_UPDATE, StateMachine.ROLE.ADMIN, VideoActivityStatus.TO_AUDIT);
activityStatusSm.add(VideoActivityStatus.REJECTED, VideoActivityStatus.EVENT_UPDATE, StateMachine.ROLE.ADVISOR, VideoActivityStatus.TO_AUDIT);
// 已上架 [运营人员] 下架
activityStatusSm.add(VideoActivityStatus.PASS, VideoActivityStatus.EVENT_SOLD_OUT, StateMachine.ROLE.ADMIN, VideoActivityStatus.SOLD_OUT);
activityStatusSm.add(VideoActivityStatus.PASS, VideoActivityStatus.EVENT_SOLD_OUT, StateMachine.ROLE.NOT_ADVISOR, VideoActivityStatus.SOLD_OUT);
// 已下架 [运营人员] 上架
activityStatusSm.add(VideoActivityStatus.SOLD_OUT, VideoActivityStatus.EVENT_PUT_ON, StateMachine.ROLE.ADMIN, VideoActivityStatus.PASS);
activityStatusSm.add(VideoActivityStatus.SOLD_OUT, VideoActivityStatus.EVENT_PUT_ON, StateMachine.ROLE.NOT_ADVISOR, VideoActivityStatus.PASS);
return activityStatusSm;
}
}
}

View File

@ -4,47 +4,40 @@ import com.syzb.video.constant.VideoLiveColumnStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.Arrays;
@Configuration
public class VideoLiveColumnStateMachine {
@Bean
public StateMachine<VideoLiveColumnStatus> videoLiveColumnSM() {
StateMachine<VideoLiveColumnStatus> videoLiveColumnSM = new StateMachine<>();
// 待提交>待提交 (券商投顾操作)(投顾编辑)
videoLiveColumnSM.add(VideoLiveColumnStatus.UN_SUBMIT, VideoLiveColumnStatus.EVENT_EDIT, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UN_SUBMIT);
// 待提交>上架待审核 (券商投顾操作)(投顾申请上架)
videoLiveColumnSM.add(VideoLiveColumnStatus.UN_SUBMIT, VideoLiveColumnStatus.EVENT_SUBMIT, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UP_UN_AUDIT);
// 待提交>待提交
videoLiveColumnSM.add(VideoLiveColumnStatus.UN_SUBMIT, VideoLiveColumnStatus.EVENT_EDIT, VideoLiveColumnStatus.UN_SUBMIT);
// 待提交>上架待审核
videoLiveColumnSM.add(VideoLiveColumnStatus.UN_SUBMIT, VideoLiveColumnStatus.EVENT_SUBMIT, VideoLiveColumnStatus.UP_UN_AUDIT);
// 待提交>上架
videoLiveColumnSM.add(VideoLiveColumnStatus.UN_SUBMIT, VideoLiveColumnStatus.EVENT_PUT_ON, VideoLiveColumnStatus.UP);
// 上架待审核>待提交 (券商投顾操作)(投顾撤回)
videoLiveColumnSM.add(VideoLiveColumnStatus.UP_UN_AUDIT, VideoLiveColumnStatus.EVENT_WITHDRAW, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UN_SUBMIT);
// 上架待审核>通过上架 (一级投顾管理岗操作)
videoLiveColumnSM.add(VideoLiveColumnStatus.UP_UN_AUDIT, VideoLiveColumnStatus.EVENT_PASS, new ArrayList<>(Arrays.asList(StateMachine.ROLE.COMPLIANCE, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UP);
// 上架待审核>驳回 (一级投顾管理岗操作)
videoLiveColumnSM.add(VideoLiveColumnStatus.UP_UN_AUDIT, VideoLiveColumnStatus.EVENT_REJECT, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.REJECT);
// 上架待审核>待提交
videoLiveColumnSM.add(VideoLiveColumnStatus.UP_UN_AUDIT, VideoLiveColumnStatus.EVENT_WITHDRAW, VideoLiveColumnStatus.UN_SUBMIT);
// 上架待审核>通过上架
videoLiveColumnSM.add(VideoLiveColumnStatus.UP_UN_AUDIT, VideoLiveColumnStatus.EVENT_PASS, VideoLiveColumnStatus.UP);
// 上架待审核>驳回
videoLiveColumnSM.add(VideoLiveColumnStatus.UP_UN_AUDIT, VideoLiveColumnStatus.EVENT_REJECT, VideoLiveColumnStatus.REJECT);
// 驳回>上架待审核 (券商投顾操作)
videoLiveColumnSM.add(VideoLiveColumnStatus.REJECT, VideoLiveColumnStatus.EVENT_EDIT, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UP_UN_AUDIT);
// 一审驳回>上架待审核/三审驳回>上架待审核/三审驳回>上架待审核 (申请上架)
videoLiveColumnSM.add(VideoLiveColumnStatus.REJECT, VideoLiveColumnStatus.EVENT_SUBMIT, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UP_UN_AUDIT);
// 驳回>上架待审核
videoLiveColumnSM.add(VideoLiveColumnStatus.REJECT, VideoLiveColumnStatus.EVENT_EDIT, VideoLiveColumnStatus.UP_UN_AUDIT);
// 驳回>申请上架
videoLiveColumnSM.add(VideoLiveColumnStatus.REJECT, VideoLiveColumnStatus.EVENT_SUBMIT, VideoLiveColumnStatus.UP_UN_AUDIT);
// 上架>下架
videoLiveColumnSM.add(VideoLiveColumnStatus.UP, VideoLiveColumnStatus.EVENT_PUT_OFF, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.DOWN);
// 待审核>上架
videoLiveColumnSM.add(VideoLiveColumnStatus.UP_UN_AUDIT, VideoLiveColumnStatus.EVENT_PASS, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UP);
videoLiveColumnSM.add(VideoLiveColumnStatus.UP, VideoLiveColumnStatus.EVENT_PUT_OFF, VideoLiveColumnStatus.DOWN);
// 下架>上架
videoLiveColumnSM.add(VideoLiveColumnStatus.DOWN, VideoLiveColumnStatus.EVENT_PUT_ON, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UP);
videoLiveColumnSM.add(VideoLiveColumnStatus.UN_SUBMIT, VideoLiveColumnStatus.EVENT_PUT_ON, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UP);
// 下架>下架 (投顾编辑/管理员编辑)
videoLiveColumnSM.add(VideoLiveColumnStatus.DOWN, VideoLiveColumnStatus.EVENT_EDIT, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.DOWN);
// 下架>上架待审核 (申请上架)
videoLiveColumnSM.add(VideoLiveColumnStatus.DOWN, VideoLiveColumnStatus.EVENT_SUBMIT, new ArrayList<>(Arrays.asList(StateMachine.ROLE.ADVISOR, StateMachine.ROLE.ADMIN)), VideoLiveColumnStatus.UP_UN_AUDIT);
videoLiveColumnSM.add(VideoLiveColumnStatus.DOWN, VideoLiveColumnStatus.EVENT_PUT_ON, VideoLiveColumnStatus.UP);
// 下架>下架
videoLiveColumnSM.add(VideoLiveColumnStatus.DOWN, VideoLiveColumnStatus.EVENT_EDIT, VideoLiveColumnStatus.DOWN);
// 下架>上架待审核
videoLiveColumnSM.add(VideoLiveColumnStatus.DOWN, VideoLiveColumnStatus.EVENT_SUBMIT, VideoLiveColumnStatus.UP_UN_AUDIT);
return videoLiveColumnSM;
}

View File

@ -4,8 +4,6 @@ import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import org.slf4j.MDC;
import java.util.Date;
public class RequestIdUtil {
public static void setValue(String str) {
@ -28,7 +26,7 @@ public class RequestIdUtil {
}
public static void setTime() {
MDC.put("requestTime", String.valueOf(new Date().getTime()));
MDC.put("requestTime", String.valueOf(System.currentTimeMillis()));
}
public static long getTime() {

View File

@ -13,6 +13,9 @@ public class CommentBlackVO extends CommonPhoneVO {
@ApiModelProperty("用户姓名")
private String userName;
@ApiModelProperty("用户头像")
private String userHeadPic;
@ApiModelProperty("产品id")
private Integer productId;
@ -102,6 +105,14 @@ public class CommentBlackVO extends CommonPhoneVO {
this.userName = userName;
}
public String getUserHeadPic() {
return userHeadPic;
}
public void setUserHeadPic(String userHeadPic) {
this.userHeadPic = userHeadPic;
}
public Integer getProductId() {
return productId;
}

View File

@ -1,14 +1,16 @@
package com.syzb.group.controller.admin;
import com.syzb.common.handler.BizException;
import com.syzb.common.query.OnlyIdQuery;
import com.syzb.common.result.AppPager;
import com.syzb.common.result.CommonResult;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.vo.BackendUserVO;
import com.syzb.common.vo.OnlyIdVO;
import com.syzb.group.query.PageCustomerQuery;
import com.syzb.group.query.message.*;
import com.syzb.group.service.admin.AdminGroupMessageService;
import com.syzb.group.vo.GroupCustomerVO;
import com.syzb.group.vo.PagePrivateChatQuery;
import com.syzb.group.vo.message.GroupMessageVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@ -20,7 +22,6 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@Api(tags = "交易圈admin消息接口")
@RestController
@ -58,10 +59,18 @@ public class AdminGroupMessageController {
@ApiOperation("后台获取私聊列表")
@PostMapping("/admin/group/message/getPrivateChatList")
public CommonResult<List<GroupMessageVO>> getPrivateChatList(@Validated @RequestBody @ApiParam(required = true) OnlyIdQuery query,
@RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) {
List<GroupMessageVO> list = adminGroupMessageService.getPrivateChatList(query, backendUserVO);
return CommonResult.success(list);
public CommonResult<AppPager<GroupMessageVO>> getPrivateChatList(@Validated @RequestBody @ApiParam(required = true) PagePrivateChatQuery query,
@RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) {
AppPager<GroupMessageVO> page = adminGroupMessageService.getPrivateChatList(query, backendUserVO);
return CommonResult.success(page);
}
@ApiOperation("后台获取用户列表")
@PostMapping("/admin/group/message/getCustomerList")
public CommonResult<AppPager<GroupCustomerVO>> getCustomerList(@Validated @RequestBody @ApiParam(required = true) PageCustomerQuery query,
@RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) {
AppPager<GroupCustomerVO> page = adminGroupMessageService.getCustomerList(query, backendUserVO);
return CommonResult.success(page);
}
@ApiOperation("后台推荐产品消息")

View File

@ -21,14 +21,14 @@ import java.util.List;
*/
public interface GroupMessageMapper extends BaseMapper<GroupMessage> {
@Select("SELECT id \n" +
@Select("SELECT id, private_user_id \n" +
"FROM ( \n" +
" SELECT id, ROW_NUMBER() OVER (PARTITION BY private_user_id ORDER BY id DESC) as rn \n" +
" SELECT id, private_user_id, ROW_NUMBER() OVER (PARTITION BY private_user_id ORDER BY id DESC) as rn \n" +
" FROM group_message \n" +
" WHERE group_id = #{groupId} AND interactive_type = 2 and private_user_id is not null \n" +
") t \n" +
"WHERE rn = 1")
List<GroupMessage> selectPrivateChatList(@Param("groupId") Integer groupId);
"${ew.customSqlSegment}")
List<GroupMessage> selectPrivateChatList(@Param("groupId") Integer groupId, @Param(Constants.WRAPPER) Wrapper<?> wrapper);
@Select("SELECT group_id, interactive_type, user_type, COUNT(0) AS id \n" +
"FROM group_message \n" +

View File

@ -24,7 +24,7 @@ public class ListGroupCustomerQuery extends PageQuery {
private Integer customerStatus;
@ApiModelProperty("禁言状态 1:已禁言 2:未禁言")
private Integer commentBlackStatus;
private Integer isForbidden;
public Integer getGroupId() {
return groupId;
@ -66,11 +66,11 @@ public class ListGroupCustomerQuery extends PageQuery {
this.customerStatus = customerStatus;
}
public Integer getCommentBlackStatus() {
return commentBlackStatus;
public Integer getIsForbidden() {
return isForbidden;
}
public void setCommentBlackStatus(Integer commentBlackStatus) {
this.commentBlackStatus = commentBlackStatus;
public void setIsForbidden(Integer isForbidden) {
this.isForbidden = isForbidden;
}
}

View File

@ -0,0 +1,77 @@
package com.syzb.group.query;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
public class PageCustomerQuery {
@ApiModelProperty("交易圈ID")
@NotNull
private Integer groupId;
@ApiModelProperty("数量")
@NotNull
private Integer size;
@ApiModelProperty("用户ID")
private String userId;
@ApiModelProperty("昵称")
private String nickName;
@ApiModelProperty("上一页最后加入时间")
private LocalDateTime lastJoinTime;
@ApiModelProperty("上一页最后ID")
private Integer lastId;
public Integer getGroupId() {
return groupId;
}
public void setGroupId(Integer groupId) {
this.groupId = groupId;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public LocalDateTime getLastJoinTime() {
return lastJoinTime;
}
public void setLastJoinTime(LocalDateTime lastJoinTime) {
this.lastJoinTime = lastJoinTime;
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}

View File

@ -3,7 +3,6 @@ package com.syzb.group.query.info;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.syzb.common.constant.IsDisplay;
import com.syzb.common.constant.IsOrNot;
import com.syzb.common.vo.BackendUserVO;
import com.syzb.group.constant.GroupInfoStatus;
import com.syzb.group.entity.GroupInfo;

View File

@ -13,6 +13,7 @@ import com.syzb.common.constant.ProductType;
import com.syzb.common.query.OnlyIdQuery;
import com.syzb.common.result.Pager;
import com.syzb.common.service.CommentBlackService;
import com.syzb.common.service.OnlineUserService;
import com.syzb.common.vo.BackendUserVO;
import com.syzb.common.vo.IdCountVO;
import com.syzb.group.constant.GroupCustomerStatus;
@ -25,7 +26,6 @@ import com.syzb.group.entity.GroupUserCollect;
import com.syzb.group.mapper.*;
import com.syzb.group.query.ListGroupCustomerQuery;
import com.syzb.group.query.QueryGroupCollectQuery;
import com.syzb.group.service.common.GroupCacheService;
import com.syzb.group.service.common.GroupCommonService;
import com.syzb.group.vo.GroupCollectVO;
import com.syzb.group.vo.GroupCustomerVO;
@ -69,7 +69,7 @@ public class AdminGroupCollectService {
private CommentBlackService commentBlackService;
@Resource
private GroupCacheService groupCacheService;
private OnlineUserService onlineUserService;
@Resource
private GroupUserFlowMapper groupUserFlowMapper;
@ -226,7 +226,7 @@ public class AdminGroupCollectService {
String nickName = query.getNickName();
Integer isOnline = query.getIsOnline();
Integer customerStatus = query.getCustomerStatus();
Integer commentBlackStatus = query.getCommentBlackStatus();
Integer isForbidden = query.getIsForbidden();
Set<Integer> moduleIdSet = groupCommonService.getModuleIds(groupId);
if (CollUtil.isEmpty(moduleIdSet)) {
return Pager.emptyPager();
@ -242,7 +242,7 @@ public class AdminGroupCollectService {
.in(WxUser::getId, userIdSet));
Map<String, WxUser> wxUserMap = wxUserList.stream().collect(Collectors.toMap(WxUser::getId, Function.identity()));
Set<String> blackUserIds = commentBlackService.getBlackUserIds(groupId, ProductType.GROUP.value);
Set<String> onlineUserIds = groupCacheService.getOnlineUserIds(groupId);
Set<String> onlineUserIds = onlineUserService.getOnlineUserIds(ProductType.GROUP.value, groupId);
Map<String, LocalDateTime> lastVisitTimeMap = queryLastVisitTime(groupId);
Map<String, LocalDateTime> lastChatTimeMap = queryLastChatTime(groupId);
Stream<ModuleUser> stream = moduleUserList.stream();
@ -254,17 +254,17 @@ public class AdminGroupCollectService {
WxUser wxUser = wxUserMap.get(moduleUser.getUserId());
if (wxUser != null) {
vo.setNickName(wxUser.getNickName());
vo.setHeadPicUrl(wxUser.getImgUrl());
vo.setUserHeadPicUrl(wxUser.getImgUrl());
}
vo.setIsOnline(onlineUserIds.contains(moduleUser.getUserId()) ? IsOrNot.IS.value : IsOrNot.NOT.value);
vo.setCommentBlackStatus(blackUserIds.contains(moduleUser.getUserId()) ? IsOrNot.IS.value : IsOrNot.NOT.value);
vo.setIsForbidden(blackUserIds.contains(moduleUser.getUserId()) ? IsOrNot.IS.value : IsOrNot.NOT.value);
vo.setCustomerStatus(calCustomerStatus(moduleUser).value);
vo.setLastVisitTime(lastVisitTimeMap.get(moduleUser.getUserId()));
vo.setLastChatTime(lastChatTimeMap.get(moduleUser.getUserId()));
return vo;
}).collect(Collectors.toList());
Stream<GroupCustomerVO> voStream = list.stream();
if (nickName != null) {
if (StrUtil.isNotEmpty(nickName)) {
voStream = voStream.filter(vo -> nickName.equals(vo.getNickName()));
}
if (isOnline != null) {
@ -273,8 +273,8 @@ public class AdminGroupCollectService {
if (customerStatus != null) {
voStream = voStream.filter(vo -> customerStatus.equals(vo.getCustomerStatus()));
}
if (commentBlackStatus != null) {
voStream = voStream.filter(vo -> commentBlackStatus.equals(vo.getCommentBlackStatus()));
if (isForbidden != null) {
voStream = voStream.filter(vo -> isForbidden.equals(vo.getIsForbidden()));
}
List<GroupCustomerVO> voList = voStream.collect(Collectors.toList());
List<GroupCustomerVO> pageList = CollUtil.page(query.getCurrent() - 1, query.getSize(), voList);

View File

@ -21,7 +21,6 @@ import com.syzb.common.vo.AuthResultVO;
import com.syzb.common.vo.BackendUserVO;
import com.syzb.common.vo.FrontUserVO;
import com.syzb.common.vo.InsertIdVO;
import com.syzb.course.entity.ShortVideo;
import com.syzb.course.query.SetMainPageQuery;
import com.syzb.course.service.PageService;
import com.syzb.group.constant.GroupInfoStatus;

View File

@ -10,11 +10,12 @@ import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.syzb.advisor.service.AdvisorInfoService;
import com.syzb.advisor.vo.AdvisorBasicVO;
import com.syzb.business.entity.ModuleUser;
import com.syzb.business.mapper.ModuleUserMapper;
import com.syzb.common.config.cache.CacheKey;
import com.syzb.common.constant.IsOrNot;
import com.syzb.common.constant.ProductType;
import com.syzb.common.handler.BizException;
import com.syzb.common.query.OnlyIdQuery;
import com.syzb.common.result.AppPager;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.service.CommentBlackService;
@ -28,17 +29,25 @@ import com.syzb.group.entity.GroupMessageRead;
import com.syzb.group.mapper.GroupInfoMapper;
import com.syzb.group.mapper.GroupMessageMapper;
import com.syzb.group.mapper.GroupMessageReadMapper;
import com.syzb.group.query.PageCustomerQuery;
import com.syzb.group.query.message.*;
import com.syzb.group.service.common.GroupCacheService;
import com.syzb.group.service.common.GroupCommonService;
import com.syzb.group.service.common.GroupMessageService;
import com.syzb.group.vo.GroupCustomerVO;
import com.syzb.group.vo.PagePrivateChatQuery;
import com.syzb.group.vo.message.GroupMessageReadVO;
import com.syzb.group.vo.message.GroupMessageVO;
import com.syzb.rbac.entity.WxUser;
import com.syzb.rbac.mapper.WxUserMapper;
import com.syzb.rbac.service.WxUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
@ -77,6 +86,18 @@ public class AdminGroupMessageService {
@Resource
private GroupMessageReadMapper groupMessageReadMapper;
@Resource
private GroupCommonService groupCommonService;
@Resource
private ModuleUserMapper moduleUserMapper;
@Resource
private WxUserMapper wxUserMapper;
@Resource
private WxUserService wxUserService;
@Transactional(rollbackFor = Exception.class)
public OnlyIdVO sendAdvisorMessage(SendGroupMessageAdminQuery query, BackendUserVO backendUser) {
GroupInfo group = groupInfoMapper.selectById(query.getGroupId());
@ -167,16 +188,126 @@ public class AdminGroupMessageService {
groupCacheService.queryMessageReadCount(vo);
}
}
if (GroupMessageUserType.CUSTOMER.value.equals(vo.getUserType())) {
vo.setUserHeadPicUrl(wxUserService.getHeadPic(vo.getUserId()));
}
if (StrUtil.isNotEmpty(vo.getPrivateUserId())) {
vo.setPrivateUserHeadPicUrl(wxUserService.getHeadPic(vo.getPrivateUserId()));
}
return vo;
}).collect(Collectors.toList());
return new AppPager<>(voList, hasNext);
}
public List<GroupMessageVO> getPrivateChatList(OnlyIdQuery query, BackendUserVO backendUserVO) {
Integer groupId = query.getId();
List<GroupMessage> list = groupMessageMapper.selectPrivateChatList(groupId);
// private Map<String, WxUser> getWxUserMap(List<GroupMessage> list) {
// Set<String> userIds = new HashSet<>();
// for (GroupMessage message : list) {
// if (GroupMessageUserType.CUSTOMER.value.equals(message.getUserType())) {
// userIds.add(message.getUserId());
// }
// if (message.getPrivateUserId() != null) {
// userIds.add(message.getPrivateUserId());
// }
// }
// Map<String, WxUser> queryUserIdSet = new HashMap<>(userIds.size());
// if (CollUtil.isNotEmpty(userIds)) {
// LambdaQueryWrapper<WxUser> userWrapper = Wrappers.<WxUser>lambdaQuery()
// .in(WxUser::getId, userIds);
// List<WxUser> wxUserList = wxUserMapper.selectList(userWrapper);
// wxUserList.forEach(user -> queryUserIdSet.put(user.getId(), user));
// }
// return queryUserIdSet;
// }
public AppPager<GroupMessageVO> getPrivateChatList(PagePrivateChatQuery query, BackendUserVO backendUserVO) {
Integer groupId = query.getGroupId();
Integer size = query.getSize();
String userId = query.getUserId();
String nickName = query.getNickName();
Integer lastId = query.getLastId();
Set<String> queryUserIdSet = null;
if (StrUtil.isNotEmpty(userId) || StrUtil.isNotEmpty(nickName)) {
LambdaQueryWrapper<WxUser> userWrapper = Wrappers.<WxUser>lambdaQuery()
.select(WxUser::getId)
.eq(StrUtil.isNotEmpty(userId), WxUser::getId, userId)
.like(StrUtil.isNotEmpty(nickName), WxUser::getNickName, nickName);
List<WxUser> wxUserList = wxUserMapper.selectList(userWrapper);
queryUserIdSet = wxUserList.stream().map(WxUser::getId).collect(Collectors.toSet());
if (CollUtil.isEmpty(queryUserIdSet)) {
return AppPager.emptyPager();
}
}
QueryWrapper<GroupMessage> wrapper = Wrappers.<GroupMessage>query()
.eq("rn", 1)
.in(queryUserIdSet != null, "private_user_id", queryUserIdSet)
.lt(lastId != null, "id", lastId)
.orderByDesc("id")
.last("limit " + (size + 1));
List<GroupMessage> list = groupMessageMapper.selectPrivateChatList(groupId, wrapper);
boolean hasNext = list.size() > size;
if (hasNext) {
list = list.subList(0, size);
}
Map<Integer, AdvisorBasicVO> advisorMap = advisorInfoService.getAdvisorVoMap();
return list.stream().map(GroupMessage::getId).map(id -> groupCacheService.getMessage(id, advisorMap)).collect(Collectors.toList());
List<GroupMessageVO> voList = list.stream().map(GroupMessage::getId).map(id -> groupCacheService.getMessage(id, advisorMap)).collect(Collectors.toList());
return new AppPager<>(voList, hasNext);
}
public AppPager<GroupCustomerVO> getCustomerList(PageCustomerQuery query, BackendUserVO backendUserVO) {
Integer groupId = query.getGroupId();
Integer size = query.getSize();
String userId = query.getUserId();
String nickName = query.getNickName();
LocalDateTime lastJoinTime = query.getLastJoinTime();
Integer lastId = query.getLastId();
Set<Integer> moduleIdSet = groupCommonService.getModuleIds(groupId);
if (CollUtil.isEmpty(moduleIdSet)) {
return AppPager.emptyPager();
}
Set<String> queryUserIdSet = null;
if (StrUtil.isNotEmpty(userId) || StrUtil.isNotEmpty(nickName)) {
LambdaQueryWrapper<WxUser> userWrapper = Wrappers.<WxUser>lambdaQuery()
.select(WxUser::getId)
.eq(StrUtil.isNotEmpty(userId), WxUser::getId, userId)
.like(StrUtil.isNotEmpty(nickName), WxUser::getNickName, nickName);
List<WxUser> wxUserList = wxUserMapper.selectList(userWrapper);
queryUserIdSet = wxUserList.stream().map(WxUser::getId).collect(Collectors.toSet());
if (CollUtil.isEmpty(queryUserIdSet)) {
return AppPager.emptyPager();
}
}
LambdaQueryWrapper<ModuleUser> muWrapper = Wrappers.<ModuleUser>lambdaQuery()
.in(ModuleUser::getModuleId, moduleIdSet)
.in(queryUserIdSet != null, ModuleUser::getUserId, queryUserIdSet)
.orderByDesc(ModuleUser::getCreateTime, ModuleUser::getId);
if (lastJoinTime != null && lastId != null) {
muWrapper.and(w1 -> w1.lt(ModuleUser::getCreateTime, lastJoinTime).or(w2 -> w2.eq(ModuleUser::getCreateTime, lastJoinTime).lt(ModuleUser::getId, lastId)));
}
muWrapper.last("limit " + (size + 1));
List<ModuleUser> moduleUserList = moduleUserMapper.selectList(muWrapper);
if (CollUtil.isEmpty(moduleUserList)) {
return AppPager.emptyPager();
}
boolean hasNext = moduleUserList.size() > size;
if (hasNext) {
moduleUserList = moduleUserList.subList(0, size);
}
Set<String> userIdSet = moduleUserList.stream().map(ModuleUser::getUserId).collect(Collectors.toSet());
List<WxUser> wxUserList = wxUserMapper.selectList(Wrappers.<WxUser>lambdaQuery()
.in(WxUser::getId, userIdSet));
Map<String, WxUser> wxUserMap = wxUserList.stream().collect(Collectors.toMap(WxUser::getId, Function.identity()));
Set<String> blackUserIds = commentBlackService.getBlackUserIds(groupId, ProductType.GROUP.value);
List<GroupCustomerVO> list = moduleUserList.stream().map(moduleUser -> {
GroupCustomerVO vo = new GroupCustomerVO(moduleUser);
WxUser wxUser = wxUserMap.get(moduleUser.getUserId());
if (wxUser != null) {
vo.setNickName(wxUser.getNickName());
vo.setUserHeadPicUrl(wxUser.getImgUrl());
}
vo.setIsForbidden(blackUserIds.contains(moduleUser.getUserId()) ? IsOrNot.IS.value : IsOrNot.NOT.value);
return vo;
}).collect(Collectors.toList());
return new AppPager<>(list, hasNext);
}
@Transactional(rollbackFor = Exception.class)

View File

@ -19,7 +19,6 @@ import com.syzb.group.mapper.GroupInfoMapper;
import com.syzb.group.query.info.ListGroupAppQuery;
import com.syzb.group.service.admin.AdminGroupInfoService;
import com.syzb.group.vo.GroupVO;
import com.syzb.rbac.service.AuthService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

View File

@ -1,17 +1,14 @@
package com.syzb.group.service.common;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.sql.SqlResult;
import com.hazelcast.sql.SqlRow;
import com.syzb.advisor.vo.AdvisorBasicVO;
import com.syzb.common.config.cache.CacheKey;
import com.syzb.common.constant.IsOrNot;
import com.syzb.common.entity.OnlineUser;
import com.syzb.common.service.CacheService;
import com.syzb.common.util.logger.LoggerUtil;
import com.syzb.common.vo.DateIdVO;
import com.syzb.group.constant.GroupInteractiveType;
import com.syzb.group.constant.GroupMessageUserType;
@ -22,15 +19,12 @@ import com.syzb.group.mapper.GroupMessageMapper;
import com.syzb.group.mapper.GroupMessageReadMapper;
import com.syzb.group.vo.message.GroupMessageReadVO;
import com.syzb.group.vo.message.GroupMessageVO;
import com.syzb.rbac.service.WxUserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static com.syzb.common.config.cache.CacheKey.GROUP;
import static com.syzb.common.config.cache.CacheKey.GROUP_ONLINE_USER;
@Service
public class GroupCacheService {
@ -47,6 +41,9 @@ public class GroupCacheService {
@Resource
private GroupMessageMapper groupMessageMapper;
@Resource
private WxUserService wxUserService;
@Resource
private IMap<String, Object> groupCache;
@ -142,7 +139,7 @@ public class GroupCacheService {
}
public GroupMessageVO getMessage(Integer messageId, Map<Integer, AdvisorBasicVO> advisorMap) {
return cacheService.get(groupCache,
GroupMessageVO vo = cacheService.get(groupCache,
CacheKey.GroupKey.GROUP_MESSAGE_DETAIL + messageId, () -> {
GroupMessage message = groupMessageMapper.selectById(messageId);
if (message == null) {
@ -150,6 +147,16 @@ public class GroupCacheService {
}
return getMessage(message, advisorMap);
});
if (vo == null) {
return null;
}
if (GroupMessageUserType.CUSTOMER.value.equals(vo.getUserType())) {
vo.setUserHeadPicUrl(wxUserService.getHeadPic(vo.getUserId()));
}
if (StrUtil.isNotEmpty(vo.getPrivateUserId())) {
vo.setPrivateUserHeadPicUrl(wxUserService.getHeadPic(vo.getPrivateUserId()));
}
return vo;
}
public GroupMessageVO getMessage(GroupMessage message, Map<Integer, AdvisorBasicVO> advisorMap) {
@ -159,82 +166,6 @@ public class GroupCacheService {
return vo;
}
/**
* 获取总在线用户映射
*
* @return 在线用户映射
*/
public IMap<String, OnlineUser> getTotalOnlineMap() {
return hazelcastInstance.getMap(GROUP_ONLINE_USER);
}
public List<OnlineUser> getTotalOnlineList(Integer groupId) {
String sql = "SELECT * FROM " + GROUP_ONLINE_USER + " WHERE productId = " + groupId;
SqlResult result = hazelcastInstance.getSql().execute(sql);
List<OnlineUser> list = new ArrayList<>();
for (SqlRow row : result) {
OnlineUser user = new OnlineUser();
user.setProductType(row.getObject("productType"));
user.setProductId(row.getObject("productId"));
user.setUserId(row.getObject("userId"));
user.setUserName(row.getObject("userName"));
user.setImg(row.getObject("img"));
user.setSessionId(row.getObject("sessionId"));
user.setIsOnline(row.getObject("isOnline"));
user.setIsPlay(row.getObject("isPlay"));
user.setCreateTime(row.getObject("createTime"));
user.setExitTime(row.getObject("exitTime"));
list.add(user);
}
return list;
}
public Set<String> getOnlineUserIds(Integer groupId) {
String sql = "SELECT distinct userId FROM " + GROUP_ONLINE_USER + " WHERE productId = " + groupId;
SqlResult result = hazelcastInstance.getSql().execute(sql);
Set<String> set = new HashSet<>();
for (SqlRow row : result) {
set.add(row.getObject("userId"));
}
return set;
}
/**
* 获取在线人数
*
* @param groupId 视频ID
* @return 在线人数
*/
public int getOnlineCount(Integer groupId) {
IMap<String, Object> map = hazelcastInstance.getMap(GROUP);
String cacheKey = CacheKey.GroupKey.ONLINE_COUNT + groupId;
Integer onlineCount = (Integer) map.get(cacheKey);
if (onlineCount != null) {
return onlineCount;
}
synchronized (this) {
onlineCount = (Integer) map.get(cacheKey);
if (onlineCount != null) {
return onlineCount;
}
long startTime = System.currentTimeMillis();
onlineCount = getOnlineCountWithoutCache(groupId);
LoggerUtil.websocket.info("getOnlineCount-" + groupId + ":" + (System.currentTimeMillis() - startTime) + "ms");
map.put(cacheKey, onlineCount, 2, TimeUnit.SECONDS);
}
return onlineCount;
}
public int getOnlineCountWithoutCache(Integer groupId) {
String sql = "SELECT COUNT(*) FROM " + GROUP_ONLINE_USER + " WHERE productId = " + groupId + " and isOnline = 1";
SqlResult result = hazelcastInstance.getSql().execute(sql);
Iterator<SqlRow> iter = result.iterator();
if (iter.hasNext()) {
return ((Long)iter.next().getObject(0)).intValue();
}
return 0;
}
private static String buildMessageIdSetKey(String prefix, Integer groupId, String userId, QueryGroupMessageType type) {
String cacheKey = prefix + groupId + "|" + type.value;
if (QueryGroupMessageType.PRIVATE.equals(type)) {

View File

@ -9,7 +9,9 @@ import com.hazelcast.map.IMap;
import com.syzb.business.entity.ModuleUser;
import com.syzb.business.mapper.ModuleUserMapper;
import com.syzb.common.constant.IsOrNot;
import com.syzb.common.constant.ProductType;
import com.syzb.common.entity.OnlineUser;
import com.syzb.common.service.OnlineUserService;
import com.syzb.common.util.logger.LoggerUtil;
import com.syzb.group.entity.GroupInfo;
import com.syzb.group.entity.GroupUserFlow;
@ -28,7 +30,7 @@ import java.util.stream.Collectors;
public class GroupCommonService {
@Resource
private GroupCacheService groupCacheService;
private OnlineUserService onlineUserService;
@Resource
private GroupInfoMapper groupInfoMapper;
@ -44,7 +46,7 @@ public class GroupCommonService {
*/
@Transactional(rollbackFor = Exception.class)
public void saveGroupUser() {
IMap<String, OnlineUser> onlineMap = groupCacheService.getTotalOnlineMap();
IMap<String, OnlineUser> onlineMap = onlineUserService.getTotalOnlineMap(ProductType.GROUP.value);
Collection<OnlineUser> onlineUsers = onlineMap.values();
if (CollUtil.isEmpty(onlineUsers)) {
return;

View File

@ -7,6 +7,9 @@ import java.time.LocalDateTime;
public class GroupCustomerVO {
@ApiModelProperty("ID")
private Integer id;
@ApiModelProperty("用户ID")
private String userId;
@ -14,7 +17,7 @@ public class GroupCustomerVO {
private String nickName;
@ApiModelProperty("头像")
private String headPicUrl;
private String userHeadPicUrl;
@ApiModelProperty("加入时间")
private LocalDateTime joinTime;
@ -28,8 +31,8 @@ public class GroupCustomerVO {
@ApiModelProperty("客户状态 1:在期 2:已到期 3:即将到期 4:新学员")
private Integer customerStatus;
@ApiModelProperty("禁言状态 0生效中 1已解除 2自然过期")
private Integer commentBlackStatus;
@ApiModelProperty("禁言状态 1:已禁言 2:未禁言")
private Integer isForbidden;
@ApiModelProperty("最近一次访问时间")
private LocalDateTime lastVisitTime;
@ -38,11 +41,20 @@ public class GroupCustomerVO {
private LocalDateTime lastChatTime;
public GroupCustomerVO(ModuleUser moduleUser) {
this.id = moduleUser.getId();
this.userId = moduleUser.getUserId();
this.joinTime = moduleUser.getCreateTime();
this.expireTime = moduleUser.getEndTime();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserId() {
return userId;
}
@ -59,12 +71,12 @@ public class GroupCustomerVO {
this.nickName = nickName;
}
public String getHeadPicUrl() {
return headPicUrl;
public String getUserHeadPicUrl() {
return userHeadPicUrl;
}
public void setHeadPicUrl(String headPicUrl) {
this.headPicUrl = headPicUrl;
public void setUserHeadPicUrl(String userHeadPicUrl) {
this.userHeadPicUrl = userHeadPicUrl;
}
public LocalDateTime getJoinTime() {
@ -99,12 +111,12 @@ public class GroupCustomerVO {
this.customerStatus = customerStatus;
}
public Integer getCommentBlackStatus() {
return commentBlackStatus;
public Integer getIsForbidden() {
return isForbidden;
}
public void setCommentBlackStatus(Integer commentBlackStatus) {
this.commentBlackStatus = commentBlackStatus;
public void setIsForbidden(Integer isForbidden) {
this.isForbidden = isForbidden;
}
public LocalDateTime getLastVisitTime() {

View File

@ -0,0 +1,65 @@
package com.syzb.group.vo;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.NotNull;
public class PagePrivateChatQuery {
@ApiModelProperty("交易圈ID")
@NotNull
private Integer groupId;
@ApiModelProperty("数量")
@NotNull
private Integer size;
@ApiModelProperty("用户ID")
private String userId;
@ApiModelProperty("昵称")
private String nickName;
@ApiModelProperty("上一页最后ID")
private Integer lastId;
public Integer getGroupId() {
return groupId;
}
public void setGroupId(Integer groupId) {
this.groupId = groupId;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}

View File

@ -30,6 +30,9 @@ public class GroupMessageVO implements Serializable {
@ApiModelProperty("用户名称")
private String userName;
@ApiModelProperty("用户头像")
private String userHeadPicUrl;
@ApiModelProperty("接受用户ID")
private String toUserId;
@ -42,6 +45,9 @@ public class GroupMessageVO implements Serializable {
@ApiModelProperty("私聊用户名称")
private String privateUserName;
@ApiModelProperty("私聊用户头像")
private String privateUserHeadPicUrl;
@ApiModelProperty("消息内容")
private String content;
@ -87,6 +93,10 @@ public class GroupMessageVO implements Serializable {
public GroupMessageVO() {
}
public GroupMessageVO(String userId) {
this.privateUserId = userId;
}
public GroupMessageVO(GroupMessage message, AdvisorBasicVO advisor) {
this.id = message.getId();
this.msgType = message.getMsgType();
@ -168,6 +178,14 @@ public class GroupMessageVO implements Serializable {
this.userName = userName;
}
public String getUserHeadPicUrl() {
return userHeadPicUrl;
}
public void setUserHeadPicUrl(String userHeadPicUrl) {
this.userHeadPicUrl = userHeadPicUrl;
}
public String getToUserId() {
return toUserId;
}
@ -200,6 +218,14 @@ public class GroupMessageVO implements Serializable {
this.privateUserName = privateUserName;
}
public String getPrivateUserHeadPicUrl() {
return privateUserHeadPicUrl;
}
public void setPrivateUserHeadPicUrl(String privateUserHeadPicUrl) {
this.privateUserHeadPicUrl = privateUserHeadPicUrl;
}
public String getContent() {
return content;
}

View File

@ -0,0 +1,50 @@
package com.syzb.monitor.controller;
import com.syzb.common.result.CommonResult;
import com.syzb.monitor.entity.InterfaceMonitor;
import com.syzb.monitor.query.MonitorQuery;
import com.syzb.monitor.service.MonitorService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiParam;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@Api(tags = "monitor")
@RestController
@CrossOrigin(origins = "*", allowedHeaders = "*", maxAge = 3600)
public class MonitorController {
@Resource
private MonitorService monitorService;
@PostMapping("/admin/monitor/listIP")
public CommonResult<List<String>> listIP(@Validated @RequestBody @ApiParam(required = true) MonitorQuery query) {
List<String> list = monitorService.listIP(query);
return CommonResult.success(list);
}
@PostMapping("/admin/monitor/listInterface")
public CommonResult<List<String>> listInterface(@Validated @RequestBody @ApiParam(required = true) MonitorQuery query) {
List<String> list = monitorService.listInterface(query);
return CommonResult.success(list);
}
@PostMapping("/admin/monitor/listData")
public CommonResult<List<InterfaceMonitor>> listData(@Validated @RequestBody @ApiParam(required = true) MonitorQuery query) {
List<InterfaceMonitor> list = monitorService.listData(query);
return CommonResult.success(list);
}
@PostMapping("/admin/monitor/groupData")
public CommonResult<List<InterfaceMonitor>> groupData(@Validated @RequestBody @ApiParam(required = true) MonitorQuery query) {
List<InterfaceMonitor> list = monitorService.groupData(query);
return CommonResult.success(list);
}
}

View File

@ -0,0 +1,218 @@
package com.syzb.monitor.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 接口监控统计表
* </p>
*
* @author helloSyzb
* @since 2025-04-09
*/
public class InterfaceMonitor implements Serializable {
/**
* 统计时间
*/
private LocalDateTime time;
/**
* 接口名称
*/
@TableId("interface_name")
private String interfaceName;
/**
* 请求来源IP地址
*/
private String ip;
/**
* 接口调用总次数
*/
private Integer total;
/**
* 接口调用平均耗时毫秒
*/
@TableField("average_time")
private Integer averageTime;
/**
* 接口调用最大耗时毫秒
*/
@TableField("max_time")
private Integer maxTime;
/**
* 接口调用最小耗时毫秒
*/
@TableField("min_time")
private Integer minTime;
/**
* 接口调用失败次数
*/
private Integer failure;
/**
* 接口耗时超过10ms的次数
*/
@TableField("over_10ms")
private Integer over10ms;
/**
* 接口耗时超过50ms的次数
*/
@TableField("over_50ms")
private Integer over50ms;
/**
* 接口耗时超过100ms的次数
*/
@TableField("over_100ms")
private Integer over100ms;
/**
* 接口耗时超过500ms的次数
*/
@TableField("over_500ms")
private Integer over500ms;
/**
* 接口耗时超过1000ms的次数
*/
@TableField("over_1000ms")
private Integer over1000ms;
/**
* 接口耗时超过5000ms的次数
*/
@TableField("over_5000ms")
private Integer over5000ms;
public LocalDateTime getTime() {
return time;
}
public void setTime(LocalDateTime time) {
this.time = time;
}
public String getInterfaceName() {
return interfaceName;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Integer getAverageTime() {
return averageTime;
}
public void setAverageTime(Integer averageTime) {
this.averageTime = averageTime;
}
public Integer getMaxTime() {
return maxTime;
}
public void setMaxTime(Integer maxTime) {
this.maxTime = maxTime;
}
public Integer getMinTime() {
return minTime;
}
public void setMinTime(Integer minTime) {
this.minTime = minTime;
}
public Integer getFailure() {
return failure;
}
public void setFailure(Integer failure) {
this.failure = failure;
}
public Integer getOver10ms() {
return over10ms;
}
public void setOver10ms(Integer over10ms) {
this.over10ms = over10ms;
}
public Integer getOver50ms() {
return over50ms;
}
public void setOver50ms(Integer over50ms) {
this.over50ms = over50ms;
}
public Integer getOver100ms() {
return over100ms;
}
public void setOver100ms(Integer over100ms) {
this.over100ms = over100ms;
}
public Integer getOver500ms() {
return over500ms;
}
public void setOver500ms(Integer over500ms) {
this.over500ms = over500ms;
}
public Integer getOver1000ms() {
return over1000ms;
}
public void setOver1000ms(Integer over1000ms) {
this.over1000ms = over1000ms;
}
public Integer getOver5000ms() {
return over5000ms;
}
public void setOver5000ms(Integer over5000ms) {
this.over5000ms = over5000ms;
}
@Override
public String toString() {
return "InterfaceMonitor{" +
"time=" + time +
", interfaceName=" + interfaceName +
", ip=" + ip +
", total=" + total +
", averageTime=" + averageTime +
", maxTime=" + maxTime +
", minTime=" + minTime +
", failure=" + failure +
", over10ms=" + over10ms +
", over50ms=" + over50ms +
", over100ms=" + over100ms +
", over500ms=" + over500ms +
", over1000ms=" + over1000ms +
", over5000ms=" + over5000ms +
"}";
}
}

View File

@ -0,0 +1,16 @@
package com.syzb.monitor.mapper;
import com.syzb.common.mapper.EasyBaseMapper;
import com.syzb.monitor.entity.InterfaceMonitor;
/**
* <p>
* 接口监控统计表 Mapper 接口
* </p>
*
* @author helloSyzb
* @since 2025-04-09
*/
public interface InterfaceMonitorMapper extends EasyBaseMapper<InterfaceMonitor> {
}

View File

@ -0,0 +1,89 @@
package com.syzb.monitor.query;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
public class MonitorQuery {
@NotNull
private LocalDateTime startTime;
@NotNull
private LocalDateTime endTime;
private String ip;
private String name;
private String ticket;
private String groupBy;
private String orderBy;
private String order;
public LocalDateTime getStartTime() {
return startTime;
}
public void setStartTime(LocalDateTime startTime) {
this.startTime = startTime;
}
public LocalDateTime getEndTime() {
return endTime;
}
public void setEndTime(LocalDateTime endTime) {
this.endTime = endTime;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTicket() {
return ticket;
}
public void setTicket(String ticket) {
this.ticket = ticket;
}
public String getGroupBy() {
return groupBy;
}
public void setGroupBy(String groupBy) {
this.groupBy = groupBy;
}
public String getOrderBy() {
return orderBy;
}
public void setOrderBy(String orderBy) {
this.orderBy = orderBy;
}
public String getOrder() {
return order;
}
public void setOrder(String order) {
this.order = order;
}
}

View File

@ -0,0 +1,297 @@
package com.syzb.monitor.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hazelcast.core.HazelcastInstance;
import com.syzb.common.handler.BizException;
import com.syzb.common.result.ResponseStatus;
import com.syzb.monitor.entity.InterfaceMonitor;
import com.syzb.monitor.mapper.InterfaceMonitorMapper;
import com.syzb.monitor.query.MonitorQuery;
import com.syzb.monitor.vo.MonitorResult;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service
public class MonitorService {
@Resource
private HazelcastInstance hazelcastInstance;
@Resource
private InterfaceMonitorMapper interfaceMonitorMapper;
private String host;
// 记录保存天数
private static final int SAVE_DAY = 15;
// 固定排序字段和列关系防止SQL注入
private static Map<String, String> ORDER_BY_MAP = new HashMap<>();
static {
ORDER_BY_MAP.put("total", "total");
ORDER_BY_MAP.put("averageTime", "average_time");
ORDER_BY_MAP.put("maxTime", "max_time");
ORDER_BY_MAP.put("minTime", "min_time");
ORDER_BY_MAP.put("failure", "failure");
ORDER_BY_MAP.put("over10ms", "over_10ms");
ORDER_BY_MAP.put("over50ms", "over_50ms");
ORDER_BY_MAP.put("over100ms", "over_100ms");
ORDER_BY_MAP.put("over500ms", "over_500ms");
ORDER_BY_MAP.put("over1000ms", "over_1000ms");
ORDER_BY_MAP.put("over5000ms", "over_5000ms");
}
@PostConstruct
private void loadHost() {
host = hazelcastInstance.getCluster().getLocalMember().getAddress().getHost();
}
private List<MonitorResult> results = new ArrayList<>();
public void add(String name, Integer time) {
this.add(name, time, null);
}
public void add(String name, Integer time, Integer code) {
this.results.add(new MonitorResult(name, time, code));
}
// 使用自定义线程池+任务实现定时器避免scheduledEnable条件只跑admin服务
@PostConstruct
public void init() {
// 创建一个调度线程池
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 每分钟 保存日志
scheduler.scheduleAtFixedRate(() -> save(), 1, 1, TimeUnit.MINUTES);
// 每天 清理日子
scheduler.scheduleAtFixedRate(() -> clear(), 0, 1, TimeUnit.DAYS);
}
// @Scheduled(cron = "0 * * * * ?")
public void save() {
// 5分钟执行一次
if (LocalDateTime.now().getMinute() % 5 != 0) {
return;
}
List<InterfaceMonitor> list = this.statistic();
if (CollUtil.isNotEmpty(list)) {
interfaceMonitorMapper.insertBatchSomeColumn(list);
}
}
// @Scheduled(cron = "0 1 2 * * ?")
public void clear() {
LocalDateTime time = LocalDate.now().minusDays(SAVE_DAY).atStartOfDay();
LambdaQueryWrapper<InterfaceMonitor> wrapper = Wrappers.<InterfaceMonitor>lambdaQuery()
.lt(InterfaceMonitor::getTime, time);
interfaceMonitorMapper.delete(wrapper);
}
public List<String> listIP(MonitorQuery query) {
checkTicket(query);
LocalDateTime startTime = query.getStartTime();
LocalDateTime endTime = query.getEndTime();
QueryWrapper<InterfaceMonitor> wrapper = Wrappers.<InterfaceMonitor>query()
.select("distinct ip")
.ge("time", startTime)
.lt("time", endTime)
.orderByAsc("ip");
List<InterfaceMonitor> list = interfaceMonitorMapper.selectList(wrapper);
return list.stream().map(InterfaceMonitor::getIp).collect(Collectors.toList());
}
public List<String> listInterface(MonitorQuery query) {
checkTicket(query);
LocalDateTime startTime = query.getStartTime();
LocalDateTime endTime = query.getEndTime();
QueryWrapper<InterfaceMonitor> wrapper = Wrappers.<InterfaceMonitor>query()
.select("distinct interface_name")
.ge("time", startTime)
.lt("time", endTime)
.orderByAsc("interface_name");
List<InterfaceMonitor> list = interfaceMonitorMapper.selectList(wrapper);
return list.stream().map(InterfaceMonitor::getInterfaceName).collect(Collectors.toList());
}
public List<InterfaceMonitor> listData(MonitorQuery query) {
checkTicket(query);
LocalDateTime startTime = query.getStartTime();
LocalDateTime endTime = query.getEndTime();
String ip = query.getIp();
String name = query.getName();
QueryWrapper<InterfaceMonitor> wrapper = Wrappers.<InterfaceMonitor>query()
.select("time", "sum(total) as total", "avg(average_time) as average_time", "max(max_time) as max_time", "min(min_time) as min_time",
"sum(failure) as failure", "sum(over_10ms) as over_10ms", "sum(over_50ms) as over_50ms", "sum(over_100ms) as over_100ms",
"sum(over_500ms) as over_500ms", "sum(over_1000ms) as over_1000ms", "sum(over_5000ms) as over_5000ms")
.ge("time", startTime)
.lt("time", endTime)
.eq(StrUtil.isNotEmpty(ip), "ip", ip)
.likeRight(StrUtil.isNotEmpty(name), "interface_name", name)
.groupBy("time")
.orderByAsc("time");
return interfaceMonitorMapper.selectList(wrapper);
}
public List<InterfaceMonitor> groupData(MonitorQuery query) {
checkTicket(query);
LocalDateTime startTime = query.getStartTime();
LocalDateTime endTime = query.getEndTime();
String groupBy = query.getGroupBy();
String groupColumn;
if ("ip".equals(groupBy)) {
groupColumn = "ip";
} else if ("name".equals(groupBy)) {
groupColumn = "interface_name";
} else {
throw new BizException(ResponseStatus.OUTSYS_ERROR);
}
String orderBy = query.getOrderBy();
if (StrUtil.isNotEmpty(orderBy)) {
orderBy = ORDER_BY_MAP.get(orderBy);
}
String order = query.getOrder();
QueryWrapper<InterfaceMonitor> wrapper = Wrappers.<InterfaceMonitor>query()
.select(groupColumn,
"sum(total) as total", "avg(average_time) as average_time", "max(max_time) as max_time", "min(min_time) as min_time",
"sum(failure) as failure", "sum(over_10ms) as over_10ms", "sum(over_50ms) as over_50ms", "sum(over_100ms) as over_100ms",
"sum(over_500ms) as over_500ms", "sum(over_1000ms) as over_1000ms", "sum(over_5000ms) as over_5000ms")
.ge("time", startTime)
.lt("time", endTime)
.groupBy(groupColumn)
.orderByAsc(StrUtil.isEmpty(orderBy) || (!"ascending".equals(order) && !"descending".equals(order)), groupColumn)
.orderByAsc(StrUtil.isNotEmpty(orderBy) && "ascending".equals(order), orderBy)
.orderByDesc(StrUtil.isNotEmpty(orderBy) && "descending".equals(order), orderBy);
return interfaceMonitorMapper.selectList(wrapper);
}
private List<InterfaceMonitor> statistic() {
// 暂存当前结果列表并清空缓存
List<MonitorResult> list = results;
results = new ArrayList<>();
LocalDateTime now = LocalDateTime.now().withSecond(0).withNano(0);
// 按接口名汇总
Map<String, List<MonitorResult>> map = list.stream().collect(Collectors.groupingBy(result -> result.name));
// 按接口计算总次数平均耗时最大耗时最小耗时失败次数各耗时区间次数
List<InterfaceMonitor> monitors = new ArrayList<>(map.size());
map.forEach((interfaceName, resultList) -> {
InterfaceMonitor monitor = new InterfaceMonitor();
monitor.setTime(now);
monitor.setInterfaceName(interfaceName);
monitor.setIp(host);
int total = resultList.size();
int failureCount = 0;
int totalTime = 0;
int maxTime = 0;
int minTime = 10000;
int over10ms = 0;
int over50ms = 0;
int over100ms = 0;
int over500ms = 0;
int over1000ms = 0;
int over5000ms = 0;
for (MonitorResult result : resultList) {
int time = result.time;
// 计算总时间用于后面计算平均值
totalTime += time;
// 计算最大最小时间
maxTime = Math.max(time, maxTime);
minTime = Math.min(time, minTime);
// 计算各时间区间次数
if (time >= 10) {
over10ms++;
if (time >= 50) {
over50ms++;
if (time >= 100) {
over100ms++;
if (time >= 500) {
over500ms++;
if (time >= 1000) {
over1000ms++;
if (time >= 5000) {
over5000ms++;
}
}
}
}
}
}
// 计算失败次数
if (result.code != null && result.code != 0) {
failureCount++;
}
}
// 设置计算结果
monitor.setTotal(total);
monitor.setFailure(failureCount);
if (total > 0) {
monitor.setAverageTime(totalTime / total);
monitor.setMaxTime(maxTime);
monitor.setMinTime(minTime);
} else {
monitor.setAverageTime(0);
monitor.setMaxTime(0);
monitor.setMinTime(0);
}
monitor.setOver10ms(over10ms);
monitor.setOver50ms(over50ms);
monitor.setOver100ms(over100ms);
monitor.setOver500ms(over500ms);
monitor.setOver1000ms(over1000ms);
monitor.setOver5000ms(over5000ms);
monitors.add(monitor);
});
return monitors;
}
private static void checkTicket(MonitorQuery query) {
if (!query.getTicket().equals(getTicket())) {
throw new BizException(ResponseStatus.OUTSYS_ERROR);
}
}
private static String getTicket() {
String prefix = "hello_syzb_";
String date = LocalDate.now().format(DatePattern.PURE_DATE_FORMATTER);
return SecureUtil.md5(prefix + date);
}
public static void main(String[] args) {
System.out.println(getTicket());
}
}

View File

@ -0,0 +1,15 @@
package com.syzb.monitor.vo;
public class MonitorResult {
// 接口名
public String name;
// 接口耗时
public Integer time;
// 接口返回码(0:成功;非0:失败)
public Integer code;
public MonitorResult(String name, Integer time, Integer code) {
this.name = name;
this.time = time;
this.code = code;
}
}

View File

@ -14,7 +14,6 @@ import com.syzb.common.constant.UserStatus;
import com.syzb.common.constant.UserType;
import com.syzb.common.handler.BizException;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.state.StateMachine;
import com.syzb.common.util.CodecUtil;
import com.syzb.common.util.JwtUtil;
import com.syzb.common.vo.AuthVO;
@ -75,6 +74,9 @@ public class AuthService {
@Resource
private UserService userService;
@Value("${user.admin.roles}")
private Integer adminRoleId;
@Transactional(readOnly = true)
public AuthVO login(LoginQuery query) {
String uuid = query.getUuid();
@ -220,9 +222,8 @@ public class AuthService {
}
public Integer getAuthId(BackendUserVO backendUserVO) {
int admin = StateMachine.ROLE.ADMIN;
List<Integer> roles = backendUserVO.getRoles();
if (roles.contains(admin)) {
if (roles.contains(adminRoleId)) {
return null;
} else {
return backendUserVO.getUserId();
@ -244,7 +245,7 @@ public class AuthService {
String deptId = backendUserVO.getDeptId();
Map<String, Dept> deptMap = deptService.getDeptMap();
Dept dept = deptMap.get(deptId);
boolean contains = backendUserVO.getRoles().contains(StateMachine.ROLE.ADMIN);
boolean contains = backendUserVO.getRoles().contains(adminRoleId);
if (contains) {
//超管
return null;
@ -292,7 +293,7 @@ public class AuthService {
//分部只查看所属部门的数据
//总部查看所有数据
List<Integer> roles = backendUserVO.getRoles();
if (roles.contains(StateMachine.ROLE.ADMIN)) {
if (roles.contains(adminRoleId)) {
return null;
}
if (DeptType.HEAD.value.equals(type)) {

View File

@ -104,6 +104,12 @@ public class UserService {
});
}
public Map<Integer, UserLogin> getUserUserIdLoginMap() {
Map<Integer, UserLogin> userLoginMap = getUserLoginMap();
Map<Integer, UserDept> userDeptMap = getUserMap();
return userDeptMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> userLoginMap.get(entry.getValue().getUserId())));
}
public Map<String, List<UserDept>> getDeptUserMap() {
return cacheService.get(USER, UserKey.USER_DEPT_MAP, () -> {
List<UserDept> userList = userDeptMapper.selectList(Wrappers.emptyWrapper());

View File

@ -0,0 +1,41 @@
package com.syzb.rbac.service;
import com.syzb.common.config.cache.CacheKey;
import com.syzb.common.service.CacheService;
import com.syzb.rbac.entity.WxUser;
import com.syzb.rbac.mapper.WxUserMapper;
import com.syzb.rbac.vo.WxUserVO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class WxUserService {
@Resource
private WxUserMapper wxUserMapper;
@Resource
private CacheService cacheService;
public WxUserVO get(String userId) {
if (userId == null) {
return null;
}
return cacheService.get(CacheKey.WX_USER, CacheKey.WxUserKey.USER + userId, () -> {
WxUser user = wxUserMapper.selectById(userId);
if (user == null) {
return null;
}
return new WxUserVO(user);
});
}
public String getHeadPic(String userId) {
WxUserVO user = get(userId);
if (user == null) {
return null;
}
return user.getImgUrl();
}
}

View File

@ -0,0 +1,47 @@
package com.syzb.rbac.vo;
import com.syzb.rbac.entity.WxUser;
import java.io.Serializable;
public class WxUserVO implements Serializable {
private String id;
private String nickName;
private String imgUrl;
public WxUserVO() {
}
public WxUserVO(WxUser user) {
this.id = user.getId();
this.nickName = user.getNickName();
this.imgUrl = user.getImgUrl();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
}

View File

@ -110,7 +110,7 @@ public class AdminVideoActivityService {
}
VideoActivityStatus dbStatus = VideoActivityStatus.fromValue(dbActivity.getStatus());
VideoActivityStatus event = VideoActivityStatus.fromValue(query.getStatus());
VideoActivityStatus targetStatus = activityStatusSm.send(dbStatus, event, StateMachine.ROLE.ADMIN);
VideoActivityStatus targetStatus = activityStatusSm.send(dbStatus, event, backendUserVO);
VideoLiveActivity activity = query.toPO(targetStatus, VideoActivityStatus.PASS.equals(targetStatus) ? backendUserVO.getUserId() : null);
if (VideoActivityStatus.PASS.equals(targetStatus)) {
check(dbActivity.getActivityRang(), dbActivity.getAdvisorId());

View File

@ -80,7 +80,7 @@ public class AdminVideoCartService {
VideoCart dbCart = getVideoCart(videoId, productId, productType);
VideoCartStatus dbStatus = VideoCartStatus.fromValue(dbCart.getStatus());
VideoCartStatus submitStatus = VideoCartStatus.fromValue(query.getStatus());
VideoCartStatus status = videoCartStatusSm.send(dbStatus, submitStatus, StateMachine.ROLE.ADVISOR);
VideoCartStatus status = videoCartStatusSm.send(dbStatus, submitStatus, backendUserVO);
dbCart.setStatus(status.value);
if (query.getNum() != null) {
dbCart.setSaleLimit(query.getNum());

View File

@ -336,7 +336,7 @@ public class AdminVideoInfoService {
}
}
VideoStatus dbStatus = VideoStatus.fromValue(videoInDb.getStatus());
VideoStatus targetStatus = videoStatusSm.send(dbStatus, VideoStatus.EVENT_UPDATE, StateMachine.ROLE.ADVISOR);
VideoStatus targetStatus = videoStatusSm.send(dbStatus, VideoStatus.EVENT_UPDATE, backendUser);
// 管理员修改/待提交/驳回/下架直接修改
if (VideoStatus.INIT.equals(dbStatus) || VideoStatus.REJECTED.equals(dbStatus) || VideoStatus.SOLD_OUT.equals(dbStatus) || VideoStatus.PASS.equals(dbStatus)) {
// 上架修改需要添加修改待审核
@ -535,7 +535,7 @@ public class AdminVideoInfoService {
if (isSoldOut) {
//下架修改直播状态和推流状态
video.setLiveStatus(VideoLiveStatus.HAS_ENDED.value);
videoCommonService.endVideo(null, new VideoLive(id, VideoLiveStatus.HAS_ENDED.value, now));
videoCommonService.endVideo(null, new VideoLive(id, VideoLiveStatus.HAS_ENDED.value, now), false);
}
if (VideoLiveStatus.LIVING.value.equals(videoInDb.getLiveStatus())) {
video.setRealEndTime(now);

View File

@ -14,14 +14,15 @@ import com.syzb.advisor.entity.AdvisorFollow;
import com.syzb.advisor.mapper.AdvisorFollowMapper;
import com.syzb.common.constant.IsFollow;
import com.syzb.common.constant.IsOrNot;
import com.syzb.common.constant.ProductType;
import com.syzb.common.entity.OnlineUser;
import com.syzb.common.service.OnlineUserService;
import com.syzb.video.constant.VideoLiveStatus;
import com.syzb.video.constant.VideoMessageContentType;
import com.syzb.video.constant.VideoMessageStatus;
import com.syzb.video.constant.VideoUserRecordType;
import com.syzb.video.entity.*;
import com.syzb.video.mapper.*;
import com.syzb.video.service.common.VideoCacheService;
import com.syzb.video.vo.statistic.VideoStatisticStaffDetailVO;
import com.syzb.video.vo.statistic.VideoStatisticUserDetailVO;
import org.springframework.beans.factory.annotation.Value;
@ -73,7 +74,7 @@ public class AdminVideoInteractionService {
private VideoLiveMapper videoLiveMapper;
@Resource
private VideoCacheService videoCacheService;
private OnlineUserService onlineUserService;
@Value("${video.finishReadRatio}")
private Double finishReadRatio;
@ -342,7 +343,7 @@ public class AdminVideoInteractionService {
public Integer queryWatchUserCount(Collection<Integer> videoIds) {
QueryWrapper<VideoUserWatchCollect> wrapper = Wrappers.<VideoUserWatchCollect>query()
// 总观看时长使用字段live_seconds,总人数使用字段vod_seconds
.select("video_id, ifnull(count(0), 0) as vod_seconds")
.select("count(0) as vod_seconds")
.in("video_id", videoIds);
VideoUserWatchCollect totalWatch = videoUserWatchCollectMapper.selectOne(wrapper);
return totalWatch == null ? 0 : totalWatch.getVodSeconds();
@ -563,7 +564,7 @@ public class AdminVideoInteractionService {
*/
@Transactional(rollbackFor = Exception.class)
public void saveVideoUserDataToDB() {
IMap<String, OnlineUser> onlineMap = videoCacheService.getTotalOnlineMap();
IMap<String, OnlineUser> onlineMap = onlineUserService.getTotalOnlineMap(ProductType.VIDEO_SINGLE.value);
Collection<OnlineUser> onlineUsers = onlineMap.values();
if (CollUtil.isEmpty(onlineUsers)) {
return;

View File

@ -19,6 +19,7 @@ import com.syzb.common.handler.BizException;
import com.syzb.common.result.Pager;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.service.CacheService;
import com.syzb.common.service.OnlineUserService;
import com.syzb.common.vo.BackendUserVO;
import com.syzb.rbac.entity.Dept;
import com.syzb.rbac.entity.UserDept;
@ -93,10 +94,12 @@ public class AdminVideoStatisticService {
@Resource
private VideoCloudService videoCloudService;
@Resource
private OnlineUserService onlineUserService;
@Value("${video.finishReadRatio}")
private Double finishReadRatio;
/**
* 单场分析
*
@ -357,7 +360,7 @@ public class AdminVideoStatisticService {
.eq(VideoDataType.ADVISOR.value.equals(type), VideoLive::getAdvisorId, advisorId)
.eq(VideoDataType.VIDEO.value.equals(type), VideoLive::getId, videoId)
.in(CollUtil.isNotEmpty(authSet) && videoId == null, VideoLive::getAdvisorId, authSet)
.eq(VideoLive::getStatus, VideoStatus.PASS.value)
.in(VideoLive::getStatus, VideoStatus.PASS.value, VideoStatus.SOLD_OUT.value)
.eq(VideoLive::getIsCart, IsOrNot.IS.value)
.between(startTime != null && endTime != null, VideoLive::getRealStartTime, startTime, endTime);
List<VideoLive> videoLiveList = videoLiveMapper.selectList(wrapper);
@ -637,7 +640,7 @@ public class AdminVideoStatisticService {
}
Page<VideoStatisticUserDetailVO> page = adminVideoInteractionService.selectVideoUserDetail(query.toPage(), wrapper);
List<VideoStatisticUserDetailVO> records = page.getRecords();
Set<String> onlineUserIdSet = videoCacheService.getOnlineUserIds(videoId);
Set<String> onlineUserIdSet = onlineUserService.getOnlineUserIds(ProductType.VIDEO_SINGLE.value, videoId);
Map<Integer, String> userNameMap = userService.getUserMap().values().stream().collect(Collectors.toMap(UserDept::getUserId, UserDept::getName));
Map<String, Integer> readMinuteMap = adminVideoInteractionService.calcuVideoReadMap(videoId, null, null, null);
Map<String, Integer> messageCountMap = adminVideoInteractionService.calUserMessageCount(videoId, null, VideoMessageContentType.TEXT);
@ -857,7 +860,7 @@ public class AdminVideoStatisticService {
// 填充实时在线人数
// 直播在线人数峰值
vo.setOnlineMostNum(adminVideoInteractionService.getOnlineMax(videoId));
vo.setOnlineNum(!VideoLiveStatus.HAS_ENDED.value.equals(liveStatus) ? videoCacheService.getOnlineCount(videoId) : 0);
vo.setOnlineNum(!VideoLiveStatus.HAS_ENDED.value.equals(liveStatus) ? onlineUserService.getOnlineCountWithoutCache(ProductType.VIDEO_SINGLE.value, videoId) : 0);
// 直播观看
if (realStartTime == null && Objects.equals(video.getPlayType(), VideoPlayType.LIVE.value)) {
vo.setReadNum(0);
@ -1047,7 +1050,7 @@ public class AdminVideoStatisticService {
*/
public UserOnlineVO queryUserOnline(UserOnlineQuery query) {
Integer videoId = query.getVideoId();
Set<String> onlineUserIds = videoCacheService.getOnlineUserIds(videoId);
Set<String> onlineUserIds = onlineUserService.getOnlineUserIds(ProductType.VIDEO_SINGLE.value, videoId);
String userId = query.getUserId();
Integer isOnline = onlineUserIds.contains(userId) ? IsOrNot.IS.value : IsOrNot.NOT.value;
return new UserOnlineVO(userId, videoId, isOnline);

View File

@ -13,6 +13,7 @@ import com.syzb.common.constant.ProductType;
import com.syzb.common.handler.BizException;
import com.syzb.common.result.AppPager;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.service.OnlineUserService;
import com.syzb.common.service.RecommendService;
import com.syzb.common.vo.FrontUserVO;
import com.syzb.rbac.service.UserService;
@ -53,6 +54,9 @@ public class AppVideoColumnService {
@Resource
private UserService userService;
@Resource
private OnlineUserService onlineUserService;
/**
* 获取主栏目列表
*
@ -66,7 +70,7 @@ public class AppVideoColumnService {
ColumnRecommendAppVO columnRecommendAppVO = new ColumnRecommendAppVO(columnId, columnName, null, null);
Integer videoId = videoCacheService.getVideoColumnLivingVideo(columnId);
columnRecommendAppVO.setStatus(videoId == null ? null : VideoLiveStatus.LIVING.value);
columnRecommendAppVO.setOnline(videoId == null ? null : videoCacheService.getOnlineCount(videoId));
columnRecommendAppVO.setOnline(videoId == null ? null : onlineUserService.getOnlineCount(ProductType.VIDEO_SINGLE.value, videoId));
return columnRecommendAppVO;
}).collect(Collectors.toList());
}
@ -138,7 +142,7 @@ public class AppVideoColumnService {
}
VideoColumnAppVO vo = new VideoColumnAppVO(column);
Integer lastVideoId = videoCacheService.getVideoColumnLivingVideo(id);
vo.setLastVideo(lastVideoId != null ? new VideoBasicInfoVO(lastVideoId, videoCacheService.getOnlineCount(lastVideoId)) : null);
vo.setLastVideo(lastVideoId != null ? new VideoBasicInfoVO(lastVideoId, onlineUserService.getOnlineCount(ProductType.VIDEO_SINGLE.value, lastVideoId)) : null);
vo.setReadCount(videoCacheService.getVideoColumnReadCount(vo.getId()));
if (frontUserVO != null) {
vo.setIsSub(videoCacheService.checkFollowColumn(frontUserVO.getUserId(), column.getId()));

View File

@ -27,6 +27,9 @@ import com.syzb.common.vo.AuthResultVO;
import com.syzb.common.vo.FrontUserVO;
import com.syzb.common.vo.MergeProductInfoVO;
import com.syzb.common.vo.TagVO;
import com.syzb.rbac.entity.UserDept;
import com.syzb.rbac.entity.UserLogin;
import com.syzb.rbac.service.UserService;
import com.syzb.video.constant.*;
import com.syzb.video.entity.*;
import com.syzb.video.helper.AbstractVideoSortComparator;
@ -98,6 +101,12 @@ public class AppVideoInfoService {
@Resource
private VideoLiveMixMapper videoLiveMixMapper;
@Resource
private OnlineUserService onlineUserService;
@Resource
private UserService userService;
@Value("${resizeUrl.urlMain}")
private String urlMain;
@ -157,7 +166,15 @@ public class AppVideoInfoService {
appVideoCustomerService.saveCustomerSale(saleUserId, userId, id);
videoCommonService.publishPcMessageWithDebounce(id);
}
if (saleUserId != null) {
UserDept userDept = userService.getUserMap().get(saleUserId);
if (userDept != null && userDept.getLoginId() != null) {
UserLogin userLogin = userService.getUserLoginMap().get(userDept.getLoginId());
if (userLogin != null) {
vo.setSaleUserWorkNo(userLogin.getUpId());
}
}
}
return vo;
}
@ -379,7 +396,7 @@ public class AppVideoInfoService {
Integer liveStatus = vo.getLiveStatus();
vo.setOnline(0);
if (VideoLiveStatus.LIVING.value.equals(liveStatus)) {
vo.setOnline(videoCacheService.getOnlineCount(vo.getId()));
vo.setOnline(onlineUserService.getOnlineCount(ProductType.VIDEO_SINGLE.value, vo.getId()));
}
voList.add(vo);
if (--size == 0) {
@ -478,7 +495,7 @@ public class AppVideoInfoService {
}
vo.setFavorUserCount(videoCacheService.getVideoFavorUserCount(videoId));
vo.setSubscribeUserCount(videoCacheService.getVideoSubscribeUserCount(videoId));
vo.setOnline(videoCacheService.getOnlineCount(videoId));
vo.setOnline(onlineUserService.getOnlineCount(ProductType.VIDEO_SINGLE.value, videoId));
if (video.getProductId() != null && video.getProductType() != null) {
Table<Integer, Integer, MergeProductInfoVO> table = mergeProductService.queryMergeProductInfo(Collections.singletonList(video));
vo.setInfoVO(table.get(video.getProductType(), video.getProductId()));
@ -608,7 +625,7 @@ public class AppVideoInfoService {
List<VideoFollowAdvisorInfoAppVO> mergeVos = getRecommendedVideos(advisorIds, includeVideoIds);
appVOS = appVOS.stream().peek(a -> {
Integer videoId = a.getVideoId();
Integer online = videoCacheService.getOnlineCount(videoId);
Integer online = onlineUserService.getOnlineCount(ProductType.VIDEO_SINGLE.value, videoId);
a.setOnline(online);
}).collect(Collectors.toList());

View File

@ -8,8 +8,6 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hazelcast.collection.ISet;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.sql.SqlResult;
import com.hazelcast.sql.SqlRow;
import com.syzb.advisor.constant.FollowOption;
import com.syzb.advisor.entity.AdvisorBasic;
import com.syzb.advisor.service.AdvisorInfoService;
@ -17,13 +15,11 @@ import com.syzb.common.config.cache.CacheKey;
import com.syzb.common.constant.IsActive;
import com.syzb.common.constant.IsOrNot;
import com.syzb.common.constant.ProductType;
import com.syzb.common.entity.OnlineUser;
import com.syzb.common.result.AppPager;
import com.syzb.common.service.AdvertService;
import com.syzb.common.service.CacheService;
import com.syzb.common.service.RecommendService;
import com.syzb.common.service.TagService;
import com.syzb.common.util.logger.LoggerUtil;
import com.syzb.common.vo.FrontUserVO;
import com.syzb.common.vo.TagVO;
import com.syzb.course.constant.CourseContentType;
@ -56,7 +52,6 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -163,82 +158,6 @@ public class VideoCacheService {
return hazelcastInstance.getMap(mapName);
}
/**
* 获取总在线用户映射
*
* @return 在线用户映射
*/
public IMap<String, OnlineUser> getTotalOnlineMap() {
return hazelcastInstance.getMap(VIDEO_ONLINE_USER);
}
public List<OnlineUser> getTotalOnlineList(Integer videoId) {
String sql = "SELECT * FROM " + VIDEO_ONLINE_USER + " WHERE productId = " + videoId;
SqlResult result = hazelcastInstance.getSql().execute(sql);
List<OnlineUser> list = new ArrayList<>();
for (SqlRow row : result) {
OnlineUser user = new OnlineUser();
user.setProductType(row.getObject("productType"));
user.setProductId(row.getObject("productId"));
user.setUserId(row.getObject("userId"));
user.setUserName(row.getObject("userName"));
user.setImg(row.getObject("img"));
user.setSessionId(row.getObject("sessionId"));
user.setIsOnline(row.getObject("isOnline"));
user.setIsPlay(row.getObject("isPlay"));
user.setCreateTime(row.getObject("createTime"));
user.setExitTime(row.getObject("exitTime"));
list.add(user);
}
return list;
}
public Set<String> getOnlineUserIds(Integer videoId) {
String sql = "SELECT distinct userId FROM " + VIDEO_ONLINE_USER + " WHERE productId = " + videoId;
SqlResult result = hazelcastInstance.getSql().execute(sql);
Set<String> set = new HashSet<>();
for (SqlRow row : result) {
set.add(row.getObject("userId"));
}
return set;
}
/**
* 获取在线人数
*
* @param videoId 视频ID
* @return 在线人数
*/
public int getOnlineCount(Integer videoId) {
IMap<String, Object> map = hazelcastInstance.getMap(VIDEO_LIVE);
String cacheKey = VideoLiveKey.ONLINE_COUNT + videoId;
Integer onlineCount = (Integer) map.get(cacheKey);
if (onlineCount != null) {
return onlineCount;
}
synchronized (this) {
onlineCount = (Integer) map.get(cacheKey);
if (onlineCount != null) {
return onlineCount;
}
long startTime = System.currentTimeMillis();
onlineCount = getOnlineCountWithoutCache(videoId);
LoggerUtil.websocket.info("getOnlineCount-" + videoId + ":" + (System.currentTimeMillis() - startTime) + "ms");
map.put(cacheKey, onlineCount, 2, TimeUnit.SECONDS);
}
return onlineCount;
}
public int getOnlineCountWithoutCache(Integer videoId) {
String sql = "SELECT COUNT(*) FROM " + VIDEO_ONLINE_USER + " WHERE productId = " + videoId + " and isOnline = 1";
SqlResult result = hazelcastInstance.getSql().execute(sql);
Iterator<SqlRow> iter = result.iterator();
if (iter.hasNext()) {
return ((Long)iter.next().getObject(0)).intValue();
}
return 0;
}
public Integer getVideoSubscribeUserCount(Integer videoId) {
return getVideoUserSubscribeIds(videoId).size();
}

View File

@ -471,16 +471,15 @@ public class VideoCloudService {
*
* @see [https://console.cloud.tencent.com/api/explorer?Product=live&Version=2018-08-01&Action=CreateRecordTask]
*/
public String createRecordTask(String streamId) {
public String createRecordTask(String streamId, LocalDateTime endTime) {
try {
// 开启任务流录制
LocalDateTime end = LocalDateTime.now().toLocalDate().plusDays(1).atStartOfDay();
Instant instant = end.atZone(ZoneId.systemDefault()).toInstant();
Instant instant = endTime.atZone(ZoneId.systemDefault()).toInstant();
// 将Instant转换为毫秒级时间戳
long timestamp = instant.toEpochMilli();
long endTime = BigDecimal.valueOf(timestamp).divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP).longValue();
long endTimestamp = BigDecimal.valueOf(timestamp).divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP).longValue();
CreateRecordTaskRequest request = new CreateRecordTaskRequest();
request.setEndTime(endTime);
request.setEndTime(endTimestamp);
request.setStreamName(streamId);
request.setDomainName(config.getPushHost());
request.setAppName(config.getAppName());

View File

@ -1,6 +1,7 @@
package com.syzb.video.service.common;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -110,6 +111,9 @@ public class VideoCommonService {
@Resource
private CacheService cacheService;
@Resource
private OnlineUserService onlineUserService;
// 防抖延时(ms)
private final long pushDelay = 10 * 1000;
@ -212,7 +216,7 @@ public class VideoCommonService {
Integer totalReadCount = adminVideoInteractionService.queryInteractionCount(v.getId(), VideoUserRecordType.READ, null, null, false);
entity.setLiveNum(totalReadCount);
videoLiveMapper.updateById(entity);
endVideo(cacheMap, v);
endVideo(cacheMap, v, true);
adminVideoQuestionService.stopByVideoId(v.getId());
});
}
@ -245,11 +249,21 @@ public class VideoCommonService {
* @param cacheMap 缓存
* @param video 直播信息
*/
public void endVideo(Map<String, Object> cacheMap, VideoLive video) {
public void endVideo(Map<String, Object> cacheMap, VideoLive video, boolean isTask) {
// 清除直播状态缓存
videoCacheService.clearVideoInfoCache(video.getId(), cacheMap);
// 已结束中断投顾推流
videoCloudService.dropLiveStream(video.getId());
// 已结束禁推投顾推流
try {
if (isTask) {
videoCloudService.stopRecordTask(video);
videoCloudService.forbidLiveStream(video.getId().toString());
} else {
videoCloudService.dropLiveStream(video.getId());
}
} catch(Exception e) {
// 忽略异常
LoggerUtil.error("结束直播失败:", ExceptionUtil.stacktraceToString(e));
}
// 直播结束通知
videoMessageService.publishLiveStatusNotification(video.getId(), VideoLiveStatus.HAS_ENDED.value);
// 直播状态推送(pc)
@ -344,7 +358,7 @@ public class VideoCommonService {
try {
Integer videoId = video.getId();
String videoName = video.getTitle();
Set<String> onlineUserIds = videoCacheService.getOnlineUserIds(videoId);
Set<String> onlineUserIds = onlineUserService.getOnlineUserIds(ProductType.VIDEO_SINGLE.value, videoId);
// 查询预约的用户
LambdaQueryWrapper<VideoLiveUser> subWrapper = Wrappers.<VideoLiveUser>lambdaQuery()
@ -571,7 +585,7 @@ public class VideoCommonService {
//观看
Integer readCount = videoCacheService.getVideoReadCount(videoId);
//在线数
int onlineCount = videoCacheService.getOnlineCount(videoId);
int onlineCount = onlineUserService.getOnlineCount(ProductType.VIDEO_SINGLE.value, videoId);
VideoPcAdvisorMessageVO messageVO = new VideoPcAdvisorMessageVO(favorCount, readCount, onlineCount);
videoMessageService.publishPcAdvisorMessage(videoId, messageVO);
}

View File

@ -125,7 +125,8 @@ public class VideoExternalService {
// 记录直播前观看人次
Integer preNum = adminVideoInteractionService.queryInteractionCount(id, VideoUserRecordType.READ, null, null, false);
live.setPreNum(preNum == null ? 0 : preNum);
String taskId = videoCloudService.createRecordTask(streamId);
LocalDateTime endTime = video.getEndTime().toLocalDate().plusDays(1).atStartOfDay();
String taskId = videoCloudService.createRecordTask(streamId, endTime);
live.setTaskId(taskId);
}
live.setLiveStatus(liveStatus);

View File

@ -10,9 +10,11 @@ import com.hazelcast.map.IMap;
import com.hazelcast.topic.ITopic;
import com.syzb.advisor.service.AdvisorInfoService;
import com.syzb.common.constant.IsOrNot;
import com.syzb.common.constant.ProductType;
import com.syzb.common.entity.OnlineUser;
import com.syzb.common.handler.BizException;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.service.OnlineUserService;
import com.syzb.common.util.logger.LoggerUtil;
import com.syzb.common.vo.FrontUserVO;
import com.syzb.rbac.service.UserService;
@ -66,6 +68,9 @@ public class VideoMessageService {
@Resource
private UserService userService;
@Resource
private OnlineUserService onlineUserService;
private ITopic<VideoWsMessageVO<?>> videoNotifyTopic = null;
private ITopic<VideoWsMessageVO<?>> videoMessageTopic = null;
@ -287,7 +292,7 @@ public class VideoMessageService {
}
vo.setAdvisorBasic(advisorInfoService.getAdvisorVoMap().get(message.getAdvisorId()));
if (VideoMessageContentType.USER_ENTER.value.equals(vo.getType())) {
vo.setOnline(videoCacheService.getOnlineCount(vo.getVideoId()));
vo.setOnline(onlineUserService.getOnlineCount(ProductType.VIDEO_SINGLE.value,vo.getVideoId()));
}
if (vo.getCreateUserId() != null) {
vo.setCreateUserVO(userService.get(message.getCreateUserId(), false));
@ -327,14 +332,14 @@ public class VideoMessageService {
*/
public void listMember(Integer videoId) {
//获取进入过直播间的人数
List<OnlineUser> totalOnlineList = videoCacheService.getTotalOnlineList(videoId);
List<OnlineUser> totalOnlineList = onlineUserService.getTotalOnlineList(ProductType.VIDEO_SINGLE.value, videoId);
int totalCount = totalOnlineList.size();
LoggerUtil.info("获取直播间在线人列表入参:" + videoId);
LoggerUtil.info("直播间id=" + videoId + "总人数:" + totalCount);
VideoCustomerVO videoCustomerVO = new VideoCustomerVO(null, 0, 0);
if (totalCount != 0) {
//获取在线人数
int onlineCount = videoCacheService.getOnlineCount(videoId);
int onlineCount = onlineUserService.getOnlineCountWithoutCache(ProductType.VIDEO_SINGLE.value, videoId);
LoggerUtil.info("直播间id=" + videoId + "在线人数:" + onlineCount);
videoCustomerVO = new VideoCustomerVO(totalOnlineList, onlineCount, totalCount);
}
@ -464,7 +469,7 @@ public class VideoMessageService {
LoggerUtil.error("reportPlayStatus上报类型错误, reportType:" + reportType);
throw new BizException(ResponseStatus.PARM_ERROR, "上报类型错误");
}
IMap<String, OnlineUser> totalOnlineMap = videoCacheService.getTotalOnlineMap();
IMap<String, OnlineUser> totalOnlineMap = onlineUserService.getTotalOnlineMap(ProductType.VIDEO_SINGLE.value);
String cacheKey = VideoHelper.buildOnlineUserCacheKey(videoId, sessionKey);
OnlineUser onlineUser = totalOnlineMap.get(cacheKey);
if (onlineUser == null) {

View File

@ -183,6 +183,9 @@ public class VideoDetailAppVO implements IVideoInfoAppVO, Serializable {
@ApiModelProperty("是否显示完整昵称 1 显示 2 屏蔽")
private Integer showNickname;
@ApiModelProperty("营销经理工号")
private String saleUserWorkNo;
public VideoDetailAppVO() {
}
@ -651,4 +654,12 @@ public class VideoDetailAppVO implements IVideoInfoAppVO, Serializable {
public void setShowNickname(Integer showNickname) {
this.showNickname = showNickname;
}
public String getSaleUserWorkNo() {
return saleUserWorkNo;
}
public void setSaleUserWorkNo(String saleUserWorkNo) {
this.saleUserWorkNo = saleUserWorkNo;
}
}

View File

@ -29,7 +29,7 @@ cron:
syncModuleUser: "40 1/5 * * * ?"
user:
admin:
roles: 1,3,4,5 #管理员角色id用逗号隔开
roles: 1 #管理员角色id用逗号隔开
defaultPwd: syzb.hello.dev
dept:
head:

View File

@ -11,24 +11,27 @@ file:
prefix: https://do.tgsys.sztg.com/
resizePrefix: https://do.tgsys.sztg.com/resize/
cron:
collectLivingVideo: "20 0/5 * * * ?" #每分钟统计已开始但未结束的视频直播数据
saveVideoCount: "20 1/5 * * * ?" #从cache刷新视频播放量到DB 每分钟的第10s执行
saveVideoUserDataToDB: "20 2/5 * * * ?"
saveCustomerDataToDB: "20 3/5 * * * ?" #收集用户信息
refreshTranscodeStatus: "20 4/5 * * * ?" #从腾讯云拉取录播上传视频信息更新到DB
updateLiveStatus: "0 1 * * * ?" #更新视频录播状态
stopLivingVideo: "0 1-5 0 * * ?" #结束前一天直播中/暂停中的视频直播
collectLastWeek: "0 30 3 * * ?" #统计一周内的数据
saveWatchSeconds: "0 0/5 * * * ?" #保存短视频观看时长
collectRecentEndVideo: "0 1/5 * * * ?" #每5分钟统计已结束48小时以内的视频直播数据
saveGroupMessageRead: "0 2/5 * * * ?" #每5分钟统计已结束48小时以内的视频直播数据
saveVideoUserDataToDB: "10 * * * * ?"
collectLivingVideo: "20 * * * * ?" #每5分钟统计已开始但未结束的视频直播数据
saveVideoCount: "30 * * * * ?" #从cache刷新视频播放量到DB 每分钟的第10s执行
saveCustomerDataToDB: "40 * * * * ?" #收集用户信息
saveGroupMessageRead: "50 * * * * ?" #每5分钟统计已结束48小时以内的视频直播数据
refreshTranscodeStatus: "0 0/5 * * * ?" #从腾讯云拉取录播上传视频信息更新到DB
saveWatchSeconds: "0 1/5 * * * ?" #保存短视频观看时长
collectRecentEndVideo: "0 2/5 * * * ?" #每5分钟统计已结束48小时以内的视频直播数据
saveGroupUser: "0 3/5 * * * ?"
collectGroupData: "0 4/5 * * * ?"
syncOrder: "40 0/5 * * * ?"
syncModuleUser: "40 1/5 * * * ?"
updateLiveStatus: "25 1 * * * ?" #更新视频录播状态
syncOrder: "25 0/5 * * * ?"
syncModuleUser: "25 1/5 * * * ?"
collectLastWeek: "45 30 3 * * ?" #统计一周内的数据
stopLivingVideo: "45 1-5 0 * * ?" #结束前一天直播中/暂停中的视频直播
user:
admin:
roles: 1,3,4,5 #管理员角色id用逗号隔开
roles: 1 #管理员角色id用逗号隔开
defaultPwd: syzb.hello.dev
dept:
head: