Compare commits
No commits in common. 'main' and 'main' have entirely different histories.
@ -1,45 +0,0 @@
|
||||
### Java template
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
replay_pid*
|
||||
|
||||
### Maven template
|
||||
/.idea
|
||||
**/target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
# Eclipse m2e generated files
|
||||
# Eclipse Core
|
||||
.project
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="0@192.168.59.129" uuid="ad808a5f-004d-4f31-9402-19010c1be1ab">
|
||||
<driver-ref>redis</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<imported>true</imported>
|
||||
<jdbc-driver>jdbc.RedisDriver</jdbc-driver>
|
||||
<jdbc-url>jdbc:redis://192.168.59.129:6379/0</jdbc-url>
|
||||
<jdbc-additional-properties>
|
||||
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
||||
</jdbc-additional-properties>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="luojia_channel@192.168.59.129" uuid="666814a4-5179-4ab7-96f4-c2a810ed102b">
|
||||
<driver-ref>mysql.8</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<imported>true</imported>
|
||||
<remarks>$PROJECT_DIR$/service/src/main/resources/application.yaml</remarks>
|
||||
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mysql://192.168.59.129:3306/luojia_channel?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai</jdbc-url>
|
||||
<jdbc-additional-properties>
|
||||
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
||||
</jdbc-additional-properties>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/common/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/service/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,17 @@
|
||||
|
||||
{
|
||||
"luatools_path": "~/go/bin",
|
||||
"dto_dir": "./ngx_conf/dto",
|
||||
"swagger.docs.path": "./docs",
|
||||
"swagger.excludes": "./client",
|
||||
"swagger.file.type": "json",
|
||||
"swagger.main.lua.path": "./main.lua",
|
||||
"swagger.name": "swagger",
|
||||
"swagger.search_dirs": "./ngx_conf,./config/cn/online",
|
||||
"validator_dir": "./ngx_conf/validator",
|
||||
"yapi.config.file": "docs/swagger-yapi.json",
|
||||
"yapi.config.mode": "mergin",
|
||||
"yapi.config.server": "https://api.yapi.net",
|
||||
"yapi.config.token": "xxxxxxxxx"
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/service/src/main/resources/db/luojia_channel.sql" dialect="MySQL" />
|
||||
<file url="PROJECT" dialect="MySQL" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,4 +1,3 @@
|
||||
{
|
||||
"java.compile.nullAnalysis.mode": "automatic",
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
"java.compile.nullAnalysis.mode": "automatic"
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package com.luojia_channel.common.domain.page;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ScrollPageRequest {
|
||||
private Long lastVal; // 上次查询的最小值(用于游标分页)
|
||||
private Integer offset = 0; // 偏移量(用于分页位置标记)
|
||||
private Long size = 10L; // 每页数量
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package com.luojia_channel.common.domain.page;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// 滚动分页请求
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ScrollPageResponse<T> {
|
||||
private Long lastVal; // 上次查询的最小值(用于游标分页)
|
||||
private Integer offset = 0; // 偏移量(用于分页位置标记)
|
||||
private Long size = 10L; // 每页数量
|
||||
private List<T> records; // 数据列表
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
artifactId=common
|
||||
groupId=com.luojia
|
||||
version=1.0.0
|
@ -0,0 +1,11 @@
|
||||
com\luojia\luojia_channel\advice\GlobalExceptionHandler.class
|
||||
com\luojia\luojia_channel\domain\UserDTO.class
|
||||
com\luojia\luojia_channel\utils\UserContext.class
|
||||
com\luojia\luojia_channel\utils\JWTUtil.class
|
||||
com\luojia\luojia_channel\config\RedisConfig.class
|
||||
com\luojia\luojia_channel\utils\RedisUtil$ZSetItem.class
|
||||
com\luojia\luojia_channel\domain\UserDTO$UserDTOBuilder.class
|
||||
com\luojia\luojia_channel\exception\BaseException.class
|
||||
com\luojia\luojia_channel\utils\RedisUtil.class
|
||||
com\luojia\luojia_channel\domain\Result.class
|
||||
com\luojia\luojia_channel\exception\UserException.class
|
@ -0,0 +1,9 @@
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\advice\GlobalExceptionHandler.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\config\RedisConfig.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\domain\Result.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\domain\UserDTO.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\exception\BaseException.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\exception\UserException.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\utils\JWTUtil.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\utils\RedisUtil.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\utils\UserContext.java
|
@ -1,8 +0,0 @@
|
||||
|
||||
protected-mode no
|
||||
|
||||
requirepass "Redis@9012"
|
||||
|
||||
|
||||
bind 0.0.0.0
|
||||
port 6379
|
@ -1 +0,0 @@
|
||||
LuojiaChannelApplication.java
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
@ -1,81 +0,0 @@
|
||||
package com.luojia_channel.modules.admin.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 管理员评论DTO
|
||||
*/
|
||||
@Data
|
||||
public class AdminCommentDTO {
|
||||
/**
|
||||
* 评论ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
private String userAvatar;
|
||||
|
||||
/**
|
||||
* 帖子ID
|
||||
*/
|
||||
private Long postId;
|
||||
|
||||
/**
|
||||
* 帖子标题
|
||||
*/
|
||||
private String postTitle;
|
||||
|
||||
/**
|
||||
* 父评论ID
|
||||
*/
|
||||
private Long parentCommentId;
|
||||
|
||||
/**
|
||||
* 顶级评论ID
|
||||
*/
|
||||
private Long topId;
|
||||
|
||||
/**
|
||||
* 回复用户名
|
||||
*/
|
||||
private String replyUsername;
|
||||
|
||||
/**
|
||||
* 点赞数
|
||||
*/
|
||||
private Integer likeCount;
|
||||
|
||||
/**
|
||||
* 回复数
|
||||
*/
|
||||
private Integer replyCount;
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package com.luojia_channel.modules.admin.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 管理员用户DTO - 仅包含管理员可见的非隐私信息
|
||||
*/
|
||||
@Data
|
||||
public class AdminUserDTO {
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 用户状态(1正常 2冻结)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 用户角色(1普通用户 2管理员 3超级管理员)
|
||||
*/
|
||||
private Integer role;
|
||||
|
||||
/**
|
||||
* 用户积分
|
||||
*/
|
||||
private Integer integral;
|
||||
|
||||
/**
|
||||
* 发帖数量
|
||||
*/
|
||||
private Integer postCount;
|
||||
|
||||
/**
|
||||
* 评论数量
|
||||
*/
|
||||
private Integer commentCount;
|
||||
|
||||
/**
|
||||
* 粉丝数量
|
||||
*/
|
||||
private Integer followerCount;
|
||||
|
||||
/**
|
||||
* 关注数量
|
||||
*/
|
||||
private Integer followingCount;
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
package com.luojia_channel.modules.admin.service;
|
||||
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.modules.admin.dto.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 管理员服务接口
|
||||
*/
|
||||
public interface AdminService {
|
||||
/**
|
||||
* 获取系统总览数据
|
||||
* @return 总览数据
|
||||
*/
|
||||
AdminOverviewDTO getOverview();
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* @param page 页码
|
||||
* @param size 每页数量
|
||||
* @param keyword 搜索关键词
|
||||
* @param role 角色
|
||||
* @param status 状态
|
||||
* @return 用户列表分页结果
|
||||
*/
|
||||
PageResponse<AdminUserDTO> getUserList(Integer page, Integer size, String keyword, Integer role, Integer status);
|
||||
|
||||
/**
|
||||
* 修改用户状态
|
||||
* @param id 用户ID
|
||||
* @param status 状态
|
||||
*/
|
||||
void changeUserStatus(Long id, Integer status);
|
||||
|
||||
/**
|
||||
* 修改用户角色
|
||||
* @param id 用户ID
|
||||
* @param role 角色
|
||||
*/
|
||||
void changeUserRole(Long id, Integer role);
|
||||
|
||||
/**
|
||||
* 获取帖子列表
|
||||
* @param page 页码
|
||||
* @param size 每页数量
|
||||
* @param keyword 搜索关键词
|
||||
* @param categoryId 分类ID
|
||||
* @param status 状态
|
||||
* @return 帖子列表分页结果
|
||||
*/
|
||||
PageResponse<AdminPostDTO> getPostList(Integer page, Integer size, String keyword, Long categoryId, Integer status);
|
||||
|
||||
/**
|
||||
* 删除帖子
|
||||
* @param id 帖子ID
|
||||
*/
|
||||
void deletePost(Long id);
|
||||
|
||||
/**
|
||||
* 修改帖子状态
|
||||
* @param id 帖子ID
|
||||
* @param action 操作类型
|
||||
*/
|
||||
void changePostStatus(Long id, Integer action);
|
||||
|
||||
/**
|
||||
* 获取评论列表
|
||||
* @param page 页码
|
||||
* @param size 每页数量
|
||||
* @param keyword 搜索关键词
|
||||
* @param postId 帖子ID
|
||||
* @return 评论列表分页结果
|
||||
*/
|
||||
PageResponse<AdminCommentDTO> getCommentList(Integer page, Integer size, String keyword, Long postId);
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
* @param id 评论ID
|
||||
*/
|
||||
void deleteComment(Long id);
|
||||
|
||||
/**
|
||||
* 获取用户统计数据
|
||||
* @param type 统计类型
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getUserStatistics(String type, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 获取帖子统计数据
|
||||
* @param type 统计类型
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getPostStatistics(String type, String startDate, String endDate);
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
package com.luojia_channel.modules.feedback.config;
|
||||
|
||||
import org.springframework.amqp.core.*;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class FeedbackRabbitMQConfig {
|
||||
|
||||
// 反馈工单交换机
|
||||
public static final String FEEDBACK_EXCHANGE = "feedback.exchange";
|
||||
|
||||
// 延迟队列 - 消息先进入此队列,过期后转发到死信队列
|
||||
public static final String FEEDBACK_DELAY_QUEUE = "feedback.delay.queue";
|
||||
|
||||
// 死信交换机 - 延迟队列的死信会转发到此交换机
|
||||
public static final String FEEDBACK_DLX_EXCHANGE = "feedback.dlx.exchange";
|
||||
|
||||
// 死信队列 - 最终消费者监听此队列
|
||||
public static final String FEEDBACK_DLX_QUEUE = "feedback.dlx.queue";
|
||||
|
||||
// 路由键
|
||||
public static final String FEEDBACK_ROUTING_KEY = "feedback.routing.key";
|
||||
|
||||
// 声明反馈交换机
|
||||
@Bean
|
||||
DirectExchange feedbackExchange() {
|
||||
return new DirectExchange(FEEDBACK_EXCHANGE, true, false);
|
||||
}
|
||||
|
||||
// 声明死信交换机
|
||||
@Bean
|
||||
DirectExchange feedbackDlxExchange() {
|
||||
return new DirectExchange(FEEDBACK_DLX_EXCHANGE, true, false);
|
||||
}
|
||||
|
||||
// 声明延迟队列,并绑定到死信交换机
|
||||
@Bean
|
||||
Queue feedbackDelayQueue() {
|
||||
return QueueBuilder.durable(FEEDBACK_DELAY_QUEUE)
|
||||
.withArgument("x-dead-letter-exchange", FEEDBACK_DLX_EXCHANGE)
|
||||
.withArgument("x-dead-letter-routing-key", FEEDBACK_ROUTING_KEY)
|
||||
.build();
|
||||
}
|
||||
|
||||
// 声明死信队列
|
||||
@Bean
|
||||
Queue feedbackDlxQueue() {
|
||||
return new Queue(FEEDBACK_DLX_QUEUE, true);
|
||||
}
|
||||
|
||||
// 绑定延迟队列到反馈交换机
|
||||
@Bean
|
||||
Binding feedbackDelayBinding() {
|
||||
return BindingBuilder.bind(feedbackDelayQueue())
|
||||
.to(feedbackExchange())
|
||||
.with(FEEDBACK_ROUTING_KEY);
|
||||
}
|
||||
|
||||
// 绑定死信队列到死信交换机
|
||||
@Bean
|
||||
Binding feedbackDlxBinding() {
|
||||
return BindingBuilder.bind(feedbackDlxQueue())
|
||||
.to(feedbackDlxExchange())
|
||||
.with(FEEDBACK_ROUTING_KEY);
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package com.luojia_channel.modules.feedback.controller;
|
||||
|
||||
import com.luojia_channel.common.domain.Result;
|
||||
import com.luojia_channel.common.domain.page.PageRequest;
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.common.exception.UserException;
|
||||
import com.luojia_channel.common.utils.UserContext;
|
||||
import com.luojia_channel.modules.feedback.dto.FeedbackTicketBasicInfoDTO;
|
||||
import com.luojia_channel.modules.feedback.dto.FeedbackTicketPageQueryDTO;
|
||||
import com.luojia_channel.modules.feedback.entity.FeedbackTicket;
|
||||
import com.luojia_channel.modules.feedback.service.FeedbackTicketService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/feedback")
|
||||
@Tag(name = "反馈工单模块", description = "反馈工单相关接口")
|
||||
@RequiredArgsConstructor
|
||||
public class FeedbackTicketController {
|
||||
|
||||
private final FeedbackTicketService feedbackTicketService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "创建反馈工单", description = "用户创建反馈工单")
|
||||
public Result<Long> createFeedbackTicket(@RequestBody FeedbackTicket feedbackTicket) {
|
||||
Long id = feedbackTicketService.createFeedbackTicket(feedbackTicket);
|
||||
return Result.success(id);
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "编辑反馈工单", description = "编辑已有的反馈工单")
|
||||
public Result<Void> updateFeedbackTicket(@RequestBody FeedbackTicket feedbackTicket) {
|
||||
feedbackTicketService.updateFeedbackTicket(feedbackTicket);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@GetMapping("/of/me")
|
||||
@Operation(summary = "个人反馈工单查询", description = "查询当前用户的反馈工单")
|
||||
public Result<List<FeedbackTicket>> getPersonalFeedbackTickets() {
|
||||
Long userId = UserContext.getUserId();
|
||||
if(userId == null){
|
||||
throw new UserException("用户未登录");
|
||||
}
|
||||
List<FeedbackTicket> tickets = feedbackTicketService.getPersonalFeedbackTickets(userId);
|
||||
return Result.success(tickets);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "分页查询反馈工单", description = "分页查询反馈工单列表")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "获取成功"),
|
||||
@ApiResponse(responseCode = "500", description = "获取失败,请稍后重试")
|
||||
})
|
||||
public Result<PageResponse<FeedbackTicketBasicInfoDTO>> pageFeedbackTicket(FeedbackTicketPageQueryDTO feedbackTicketPageQueryDTO) {
|
||||
PageResponse<FeedbackTicketBasicInfoDTO> pageResponse = feedbackTicketService.pageFeedbackTicket(feedbackTicketPageQueryDTO);
|
||||
return Result.success(pageResponse);
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package com.luojia_channel.modules.feedback.entity;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("feedback_ticket")
|
||||
public class FeedbackTicket {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private String content;
|
||||
private String feedbackType;
|
||||
private Integer status;
|
||||
private String contactInfo;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime handleTime;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package com.luojia_channel.modules.feedback.mapper;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.luojia_channel.modules.feedback.entity.FeedbackTicket;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface FeedbackTicketMapper extends BaseMapper<FeedbackTicket> {
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package com.luojia_channel.modules.feedback.mq.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DelayHandleTicketMessage {
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private String content;
|
||||
private String feedbackType;
|
||||
private Integer status;
|
||||
private String contactInfo;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package com.luojia_channel.modules.feedback.mq.producer;
|
||||
|
||||
|
||||
import com.luojia_channel.modules.feedback.config.FeedbackRabbitMQConfig;
|
||||
import com.luojia_channel.modules.feedback.mq.domain.DelayHandleTicketMessage;
|
||||
import com.luojia_channel.modules.message.mq.AbstractSendProduceTemplate;
|
||||
import com.luojia_channel.modules.message.mq.BaseSendExtendDTO;
|
||||
import com.luojia_channel.modules.message.mq.MessageWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FeedbackMessageProducer extends AbstractSendProduceTemplate<DelayHandleTicketMessage>{
|
||||
|
||||
public FeedbackMessageProducer(@Autowired RabbitTemplate rabbitTemplate){
|
||||
super(rabbitTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseSendExtendDTO buildBaseSendParam(DelayHandleTicketMessage messageSendEvent) {
|
||||
return BaseSendExtendDTO.builder()
|
||||
.eventName("DelayHandleTicketMessageEvent")
|
||||
.exchange(FeedbackRabbitMQConfig.FEEDBACK_EXCHANGE)
|
||||
.routingKey(FeedbackRabbitMQConfig.FEEDBACK_ROUTING_KEY)
|
||||
.keys(messageSendEvent.getId().toString())
|
||||
.delay(24*60*60*1000L) //延迟消息是1天
|
||||
.build();
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package com.luojia_channel.modules.feedback.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.luojia_channel.modules.feedback.dto.FeedbackTicketBasicInfoDTO;
|
||||
import com.luojia_channel.modules.feedback.dto.FeedbackTicketPageQueryDTO;
|
||||
import com.luojia_channel.modules.feedback.entity.FeedbackTicket;
|
||||
import com.luojia_channel.common.domain.page.PageRequest;
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface FeedbackTicketService extends IService<FeedbackTicket> {
|
||||
/**
|
||||
* 创建反馈工单
|
||||
* @param feedbackTicket 反馈工单实体
|
||||
* @return 工单 ID
|
||||
*/
|
||||
Long createFeedbackTicket(FeedbackTicket feedbackTicket);
|
||||
|
||||
/**
|
||||
* 编辑反馈工单
|
||||
* @param feedbackTicket 反馈工单实体
|
||||
*/
|
||||
void updateFeedbackTicket(FeedbackTicket feedbackTicket);
|
||||
|
||||
/**
|
||||
* 个人反馈工单查询
|
||||
* @param userId 用户 ID
|
||||
* @return 反馈工单列表
|
||||
*/
|
||||
List<FeedbackTicket> getPersonalFeedbackTickets(Long userId);
|
||||
|
||||
/**
|
||||
* 分页查询反馈工单
|
||||
* @param feedbackTicketPageQueryDTO 分页查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResponse<FeedbackTicketBasicInfoDTO> pageFeedbackTicket(FeedbackTicketPageQueryDTO feedbackTicketPageQueryDTO);
|
||||
|
||||
/**
|
||||
* 处理延时反馈工单
|
||||
* @param ticketId 工单ID
|
||||
*/
|
||||
void processDelayedFeedback(Long ticketId);
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
package com.luojia_channel.modules.feedback.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.luojia_channel.common.domain.page.PageRequest;
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.modules.feedback.dto.FeedbackTicketBasicInfoDTO;
|
||||
import com.luojia_channel.modules.feedback.dto.FeedbackTicketPageQueryDTO;
|
||||
import com.luojia_channel.modules.feedback.entity.FeedbackTicket;
|
||||
import com.luojia_channel.modules.feedback.mapper.FeedbackTicketMapper;
|
||||
import com.luojia_channel.modules.feedback.mq.domain.DelayHandleTicketMessage;
|
||||
import com.luojia_channel.modules.feedback.mq.producer.FeedbackMessageProducer;
|
||||
import com.luojia_channel.modules.feedback.service.FeedbackTicketService;
|
||||
import com.luojia_channel.modules.user.entity.User;
|
||||
import com.luojia_channel.modules.user.mapper.UserMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FeedbackTicketServiceImpl extends ServiceImpl<FeedbackTicketMapper, FeedbackTicket> implements FeedbackTicketService {
|
||||
|
||||
private final UserMapper userMapper;
|
||||
private final FeedbackMessageProducer feedbackMessageProducer;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createFeedbackTicket(FeedbackTicket feedbackTicket) {
|
||||
feedbackTicket.setCreateTime(LocalDateTime.now());
|
||||
feedbackTicket.setStatus(1);
|
||||
if(!save(feedbackTicket))
|
||||
throw new RuntimeException("保存工单失败");
|
||||
// 发送延时消息
|
||||
feedbackMessageProducer.sendMessage(BeanUtil.copyProperties(feedbackTicket, DelayHandleTicketMessage.class));
|
||||
return feedbackTicket.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFeedbackTicket(FeedbackTicket feedbackTicket) {
|
||||
updateById(feedbackTicket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FeedbackTicket> getPersonalFeedbackTickets(Long userId) {
|
||||
LambdaQueryWrapper<FeedbackTicket> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FeedbackTicket::getUserId, userId);
|
||||
return list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResponse<FeedbackTicketBasicInfoDTO> pageFeedbackTicket(FeedbackTicketPageQueryDTO feedbackTicketPageQueryDTO) {
|
||||
LambdaQueryWrapper<FeedbackTicket> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (feedbackTicketPageQueryDTO.getFeedbackType() != null) {
|
||||
queryWrapper.eq(FeedbackTicket::getFeedbackType, feedbackTicketPageQueryDTO.getFeedbackType());
|
||||
}
|
||||
if (feedbackTicketPageQueryDTO.getStatus() != null) {
|
||||
queryWrapper.eq(FeedbackTicket::getStatus, feedbackTicketPageQueryDTO.getStatus());
|
||||
}
|
||||
queryWrapper.orderByDesc(FeedbackTicket::getCreateTime);
|
||||
|
||||
Page<FeedbackTicket> page = new Page<>(feedbackTicketPageQueryDTO.getCurrent(), feedbackTicketPageQueryDTO.getSize());
|
||||
IPage<FeedbackTicket> feedbackTicketPage = baseMapper.selectPage(page, queryWrapper);
|
||||
|
||||
List<Long> userIds = new ArrayList<>();
|
||||
feedbackTicketPage.getRecords().forEach(ticket -> userIds.add(ticket.getUserId()));
|
||||
List<User> users = userMapper.selectBatchIds(userIds);
|
||||
Map<Long, User> userMap = users.stream()
|
||||
.collect(Collectors.toMap(User::getId, user -> user));
|
||||
|
||||
List<FeedbackTicketBasicInfoDTO> feedbackTicketBasicInfoDTOS = new ArrayList<>();
|
||||
feedbackTicketPage.getRecords().forEach(ticket -> {
|
||||
FeedbackTicketBasicInfoDTO dto = new FeedbackTicketBasicInfoDTO();
|
||||
BeanUtil.copyProperties(ticket, dto);
|
||||
User user = userMap.getOrDefault(ticket.getUserId(), new User());
|
||||
dto.setUserName(user.getUsername());
|
||||
dto.setUserAvatar(user.getAvatar());
|
||||
feedbackTicketBasicInfoDTOS.add(dto);
|
||||
});
|
||||
|
||||
return PageResponse.<FeedbackTicketBasicInfoDTO>builder()
|
||||
.current(feedbackTicketPage.getCurrent())
|
||||
.size(feedbackTicketPage.getSize())
|
||||
.total(feedbackTicketPage.getTotal())
|
||||
.records(feedbackTicketBasicInfoDTOS)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processDelayedFeedback(Long ticketId) {
|
||||
// 查询工单
|
||||
FeedbackTicket ticket = getById(ticketId);
|
||||
if (ticket == null || ticket.getStatus().equals(3)) {
|
||||
return;
|
||||
}
|
||||
// 处理延时任务,更新工单状态为超时
|
||||
ticket.setStatus(4); //
|
||||
updateById(ticket);
|
||||
// TODO 向管理员发送消息
|
||||
|
||||
}
|
||||
}
|
@ -1,52 +1,34 @@
|
||||
package com.luojia_channel.modules.interact.controller;
|
||||
|
||||
import com.luojia_channel.common.domain.Result;
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.common.domain.page.ScrollPageResponse;
|
||||
import com.luojia_channel.modules.interact.dto.ChatItemDTO;
|
||||
import com.luojia_channel.modules.interact.dto.ChatPageQueryDTO;
|
||||
import com.luojia_channel.modules.interact.service.ChatService;
|
||||
import com.luojia_channel.modules.message.dto.MessageResponse;
|
||||
import com.luojia_channel.modules.message.dto.MessageRequest;
|
||||
import com.luojia_channel.modules.message.server.WebSocketServer;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/message")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "聊天模块", description = "好友聊天模块相关接口")
|
||||
public class ChatController {
|
||||
private final ChatService chatService;
|
||||
|
||||
@Operation(
|
||||
summary = "聊天列表",
|
||||
description = "传入分页参数,查询私信用户列表(带最新消息)",
|
||||
tags = {"聊天模块"}
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "获取成功"),
|
||||
@ApiResponse(responseCode = "500", description = "获取失败,请稍后重试")
|
||||
})
|
||||
@GetMapping("/chat-list")
|
||||
public Result<ScrollPageResponse<ChatItemDTO>> getChatList(ChatPageQueryDTO chatPageQueryDTO) {
|
||||
return Result.success(chatService.getChatList(chatPageQueryDTO));
|
||||
}
|
||||
private WebSocketServer webSocketServer;
|
||||
|
||||
@PostMapping("/sendPrivateMessage")
|
||||
@Operation(
|
||||
summary = "历史记录",
|
||||
description = "传入分页参数,获取与特定用户的完整聊天记录",
|
||||
summary = "发送私信",
|
||||
description = "发送私信给指定用户",
|
||||
tags = {"聊天模块"}
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "获取成功"),
|
||||
@ApiResponse(responseCode = "500", description = "获取失败,请稍后重试")
|
||||
})
|
||||
@GetMapping("/history")
|
||||
public Result<ScrollPageResponse<MessageResponse>> getChatHistory(ChatPageQueryDTO chatPageQueryDTO) {
|
||||
return Result.success(chatService.getChatHistory(chatPageQueryDTO));
|
||||
public String sendPrivateMessage(@RequestParam Long senderId, @RequestBody MessageRequest request) {
|
||||
try {
|
||||
webSocketServer.sendPrivateMessage(senderId, request);
|
||||
return "私信发送成功";
|
||||
} catch (Exception e) {
|
||||
return "私信发送失败: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "聊天列表项DTO")
|
||||
public class ChatItemDTO {
|
||||
@Schema(
|
||||
description = "聊天对象的用户ID",
|
||||
required = true,
|
||||
example = "123456"
|
||||
)
|
||||
private Long chatUserId;
|
||||
|
||||
@Schema(
|
||||
description = "聊天对象的头像URL",
|
||||
example = "https://example.com/avatar.jpg"
|
||||
)
|
||||
private String avatar;
|
||||
|
||||
@Schema(
|
||||
description = "聊天对象的用户名",
|
||||
required = true,
|
||||
example = "张三"
|
||||
)
|
||||
private String username;
|
||||
|
||||
@Schema(
|
||||
description = "最新消息内容",
|
||||
required = true,
|
||||
maxLength = 500,
|
||||
example = "今天下午开会"
|
||||
)
|
||||
private String latestMessage;
|
||||
|
||||
@Schema(
|
||||
description = "最新消息时间",
|
||||
required = true,
|
||||
example = "2023-10-15T14:30:00"
|
||||
)
|
||||
private LocalDateTime latestTime;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.dto;
|
||||
|
||||
import com.luojia_channel.common.domain.page.ScrollPageRequest;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ChatPageQueryDTO extends ScrollPageRequest {
|
||||
private Long chatUserId;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.service;
|
||||
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.common.domain.page.ScrollPageResponse;
|
||||
import com.luojia_channel.modules.interact.dto.ChatItemDTO;
|
||||
import com.luojia_channel.modules.interact.dto.ChatPageQueryDTO;
|
||||
import com.luojia_channel.modules.message.dto.MessageResponse;
|
||||
import com.luojia_channel.modules.message.entity.MessageDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ChatService {
|
||||
|
||||
ScrollPageResponse<ChatItemDTO> getChatList(ChatPageQueryDTO chatPageQueryDTO);
|
||||
|
||||
ScrollPageResponse<MessageResponse> getChatHistory(ChatPageQueryDTO chatPageQueryDTO);
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.luojia_channel.common.domain.page.ScrollPageResponse;
|
||||
import com.luojia_channel.common.utils.PageUtil;
|
||||
import com.luojia_channel.common.utils.RedisUtil;
|
||||
import com.luojia_channel.common.utils.UserContext;
|
||||
import com.luojia_channel.modules.interact.dto.ChatItemDTO;
|
||||
import com.luojia_channel.modules.interact.dto.ChatPageQueryDTO;
|
||||
import com.luojia_channel.modules.interact.service.ChatService;
|
||||
import com.luojia_channel.modules.message.dto.MessageResponse;
|
||||
import com.luojia_channel.modules.message.entity.MessageDO;
|
||||
import com.luojia_channel.modules.message.mapper.MessageMapper;
|
||||
import com.luojia_channel.modules.user.entity.User;
|
||||
import com.luojia_channel.modules.user.mapper.UserMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChatServiceImpl implements ChatService {
|
||||
|
||||
private final MessageMapper messageMapper;
|
||||
private final UserMapper userMapper;
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
@Override
|
||||
public ScrollPageResponse<ChatItemDTO> getChatList(ChatPageQueryDTO chatPageQueryDTO) {
|
||||
/*
|
||||
Long userId = UserContext.getUserId();
|
||||
IPage<ChatItemDTO> chatPage = messageMapper.selectChatList(PageUtil.convert(chatPageQueryDTO), userId);
|
||||
return PageResponse.<ChatItemDTO>builder()
|
||||
.current(chatPage.getCurrent())
|
||||
.size(chatPage.getSize())
|
||||
.total(chatPage.getTotal())
|
||||
.records(chatPage.getRecords())
|
||||
.build();
|
||||
*/
|
||||
Long userId = UserContext.getUserId();
|
||||
String key = "chat:user_list:" + userId;
|
||||
|
||||
return redisUtil.scrollPageQuery(key, ChatItemDTO.class, chatPageQueryDTO,
|
||||
(chatUserIds) -> {
|
||||
List<ChatItemDTO> chatItems = new ArrayList<>();
|
||||
List<Long> latestMessageIds = new ArrayList<>();
|
||||
List<User> users = userMapper.selectByIdsOrderByField(chatUserIds);
|
||||
for(Long chatUserId : chatUserIds){
|
||||
String messageKey = "chat:history:" + Math.min(userId, chatUserId) + ":" +Math.max(userId, chatUserId);
|
||||
// 获取zset中最新的messageId
|
||||
Long latestMessageId = redisUtil.zRevMaxValue(messageKey);
|
||||
latestMessageIds.add(latestMessageId);
|
||||
}
|
||||
List<MessageDO> messageDOS = messageMapper.selectByIdsOrderByField(latestMessageIds);
|
||||
int i=0;
|
||||
for(User user : users){
|
||||
ChatItemDTO chatItemDTO = ChatItemDTO.builder()
|
||||
.chatUserId(user.getId())
|
||||
.avatar(user.getAvatar())
|
||||
.username(user.getUsername())
|
||||
.latestMessage(messageDOS.get(i).getContent())
|
||||
.latestTime(messageDOS.get(i).getCreateTime())
|
||||
.build();
|
||||
chatItems.add(chatItemDTO);
|
||||
i++;
|
||||
}
|
||||
|
||||
return chatItems;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScrollPageResponse<MessageResponse> getChatHistory(ChatPageQueryDTO chatPageQueryDTO) {
|
||||
/*
|
||||
Long userId = UserContext.getUserId();
|
||||
Long chatUserId = chatPageQueryDTO.getChatUserId();
|
||||
LambdaQueryWrapper<MessageDO> queryWrapper = Wrappers.lambdaQuery(MessageDO.class)
|
||||
.eq(MessageDO::getSenderId, userId)
|
||||
.eq(MessageDO::getReceiverId, chatUserId)
|
||||
.or()
|
||||
.eq(MessageDO::getReceiverId, userId)
|
||||
.eq(MessageDO::getSenderId, chatUserId)
|
||||
.orderByDesc(MessageDO::getCreateTime);
|
||||
// 查询的是私信消息
|
||||
queryWrapper.eq(MessageDO::getMessageType, 1);
|
||||
IPage<MessageDO> page = messageMapper.selectPage(PageUtil.convert(chatPageQueryDTO), queryWrapper);
|
||||
User chatUser = userMapper.selectById(chatUserId);
|
||||
return PageUtil.convert(page, (message) -> {
|
||||
MessageResponse messageResponse = BeanUtil.copyProperties(message, MessageResponse.class);
|
||||
if(messageResponse.getSenderId().equals(userId)) {
|
||||
messageResponse.setSenderAvatar(UserContext.getAvatar());
|
||||
messageResponse.setSenderName(UserContext.getUsername());
|
||||
}else{
|
||||
messageResponse.setSenderAvatar(chatUser.getAvatar());
|
||||
messageResponse.setSenderName(chatUser.getUsername());
|
||||
}
|
||||
return messageResponse;
|
||||
});
|
||||
*/
|
||||
|
||||
// 改成滚动分页查询
|
||||
Long userId = UserContext.getUserId();
|
||||
Long chatUserId = chatPageQueryDTO.getChatUserId();
|
||||
String key = "chat:history:" + Math.min(userId, chatUserId) + ":" +Math.max(userId, chatUserId);
|
||||
return redisUtil.scrollPageQuery(key, MessageResponse.class, chatPageQueryDTO,
|
||||
(messageIds) -> {
|
||||
List<MessageDO> messageDOS = messageMapper.selectByIdsOrderByField(messageIds);
|
||||
User chatUser = userMapper.selectById(chatUserId);
|
||||
List<MessageResponse> messageResponses = new ArrayList<>();
|
||||
for(MessageDO message : messageDOS){
|
||||
MessageResponse messageResponse = BeanUtil.copyProperties(message, MessageResponse.class);
|
||||
if(messageResponse.getSenderId().equals(userId)) {
|
||||
messageResponse.setSenderAvatar(UserContext.getAvatar());
|
||||
messageResponse.setSenderName(UserContext.getUsername());
|
||||
}else{
|
||||
messageResponse.setSenderAvatar(chatUser.getAvatar());
|
||||
messageResponse.setSenderName(chatUser.getUsername());
|
||||
}
|
||||
messageResponses.add(messageResponse);
|
||||
}
|
||||
return messageResponses;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package com.luojia_channel.modules.message.util;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WebSocketContext implements ApplicationContextAware {
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
@Autowired
|
||||
public void setApplicationContext(ApplicationContext inApplicationContext) throws BeansException {
|
||||
applicationContext = inApplicationContext;
|
||||
}
|
||||
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public static Object getBean(String name) {
|
||||
return getApplicationContext().getBean(name);
|
||||
}
|
||||
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
return getApplicationContext().getBean(clazz);
|
||||
}
|
||||
|
||||
public static <T> T getBean(String name, Class<T> clazz) {
|
||||
return getApplicationContext().getBean(name, clazz);
|
||||
}
|
||||
|
||||
|
||||
public static String getActiveProfile() {
|
||||
String[] activeProfiles = getApplicationContext().getEnvironment().getActiveProfiles();
|
||||
if (activeProfiles.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return activeProfiles[0];
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
package com.luojia_channel.modules.post.algorithm;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.luojia_channel.common.utils.RedisUtil;
|
||||
import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO;
|
||||
import com.luojia_channel.modules.post.entity.Comment;
|
||||
import com.luojia_channel.modules.post.entity.Post;
|
||||
import com.luojia_channel.modules.post.mapper.CommentMapper;
|
||||
import com.luojia_channel.modules.post.mapper.PostMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class PostSelector {
|
||||
|
||||
private final PostMapper postMapper;
|
||||
private final CommentMapper commentMapper;
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
public void calculatePostScore(Post post){
|
||||
long hours = calculateHoursSince(post.getCreateTime());
|
||||
|
||||
double likes = post.getLikeCount();
|
||||
double comments = post.getCommentCount();
|
||||
double favorites = post.getFavoriteCount();
|
||||
|
||||
double newScore = (1 + likes + comments*0.7 + favorites*0.5)
|
||||
/ Math.pow(1 + hours/24.0, 1.8);
|
||||
|
||||
redisUtil.zAdd("post:hot:"+post.getCategoryId(), post.getId(), newScore);
|
||||
redisUtil.zAdd("post:hot:all", post.getId(), newScore);
|
||||
}
|
||||
|
||||
public void calculateCommentScore(Comment comment){
|
||||
long hours = calculateHoursSince(comment.getCreateTime());
|
||||
|
||||
double likes = comment.getLikeCount();
|
||||
double replies = comment.getReplyCount();
|
||||
double newScore = (1 + likes + replies*0.7)
|
||||
/ Math.pow(1 + hours/24.0, 1.8);
|
||||
|
||||
if(comment.getId().equals(comment.getTopId())) {
|
||||
redisUtil.zAdd("post:comment_by_hot:" + comment.getPostId(), comment.getId(), newScore);
|
||||
} else {
|
||||
redisUtil.zAdd("comment:reply_by_hot:" + comment.getTopId(), comment.getId(), newScore);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新帖子热度分数
|
||||
public void updatePostScore(Long postId) {
|
||||
Post post = postMapper.selectById(postId);
|
||||
if (post == null) return;
|
||||
calculatePostScore(post);
|
||||
}
|
||||
|
||||
// 更新评论热度分数
|
||||
public void updateCommentScore(Long commentId) {
|
||||
Comment comment = commentMapper.selectById(commentId);
|
||||
if(comment == null) return;
|
||||
calculateCommentScore(comment);
|
||||
}
|
||||
|
||||
// 计算从指定时间到现在经过的小时数
|
||||
public long calculateHoursSince(LocalDateTime startTime) {
|
||||
if (startTime == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Instant startInstant = startTime.atZone(ZoneId.systemDefault()).toInstant();
|
||||
Instant nowInstant = Instant.now();
|
||||
|
||||
long millisDiff = Duration.between(startInstant, nowInstant).toMillis();
|
||||
return TimeUnit.HOURS.convert(millisDiff, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
// TODO 自定义首页帖子算法
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue