Compare commits
10 Commits
586a507bb1
...
c79086cf5e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c79086cf5e | ||
|
|
de4cc4d73f | ||
|
|
5a435dd19c | ||
|
|
98f25179d3 | ||
|
|
0cf464e549 | ||
|
|
633b94c375 | ||
|
|
6441018d95 | ||
|
|
e706431df5 | ||
|
|
db1a3566ae | ||
|
|
9225b4ff10 |
@ -54,7 +54,7 @@ spring:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password: lilishop
|
||||
# password: lilishop
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数(使用负值表示没有限制) 默认 8
|
||||
@ -73,9 +73,9 @@ spring:
|
||||
default-datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: lilishop
|
||||
url: jdbc:mysql://82.156.121.2:23306/shop_dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
username: wzj
|
||||
password: A085F27A43B0
|
||||
maxActive: 50
|
||||
initialSize: 10
|
||||
maxWait: 60000
|
||||
|
||||
@ -35,7 +35,7 @@ spring:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password: lilishop
|
||||
# password: lilishop
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数(使用负值表示没有限制) 默认 8
|
||||
|
||||
@ -29,9 +29,9 @@ spring:
|
||||
type: redis
|
||||
# Redis
|
||||
redis:
|
||||
host: 192.168.31.100
|
||||
port: 30379
|
||||
password: lilishop
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
# password: e4ea0caebfd2
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数(使用负值表示没有限制) 默认 8
|
||||
@ -60,9 +60,9 @@ spring:
|
||||
default-datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://192.168.31.100:30306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: lilishop
|
||||
url: jdbc:mysql://82.156.121.2:23306/shop_dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
username: wzj
|
||||
password: A085F27A43B0
|
||||
maxActive: 50
|
||||
initialSize: 20
|
||||
maxWait: 60000
|
||||
@ -256,18 +256,18 @@ lili:
|
||||
data:
|
||||
elasticsearch:
|
||||
cluster-name: elasticsearch
|
||||
cluster-nodes: 192.168.31.100:30920
|
||||
cluster-nodes: 82.156.121.2:19200
|
||||
index:
|
||||
number-of-replicas: 0
|
||||
number-of-shards: 3
|
||||
index-prefix: lili
|
||||
schema: http
|
||||
# account:
|
||||
# username: elastic
|
||||
# password: LiLiShopES
|
||||
account:
|
||||
username: elastic
|
||||
password: B5254c5953d
|
||||
|
||||
logstash:
|
||||
server: 192.168.31.100:30560
|
||||
server: 127.0.0.1:30560
|
||||
rocketmq:
|
||||
promotion-topic: shop_lili_promotion_topic
|
||||
promotion-group: shop_lili_promotion_group
|
||||
@ -288,7 +288,7 @@ lili:
|
||||
after-sale-topic: shop_lili_after_sale_topic
|
||||
after-sale-group: shop_lili_after_sale_group
|
||||
rocketmq:
|
||||
name-server: 192.168.31.100:30876
|
||||
name-server: 127.0.0.1:9876
|
||||
isVIPChannel: false
|
||||
producer:
|
||||
group: lili_group
|
||||
@ -297,7 +297,7 @@ rocketmq:
|
||||
xxl:
|
||||
job:
|
||||
admin:
|
||||
addresses: http://192.168.31.100:30001/xxl-job-admin
|
||||
addresses: http://127.0.1:30001/xxl-job-admin
|
||||
executor:
|
||||
appname: xxl-job-executor-lilishop
|
||||
address:
|
||||
|
||||
@ -38,7 +38,7 @@ spring:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password: lilishop
|
||||
# password: lilishop
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数(使用负值表示没有限制) 默认 8
|
||||
@ -67,9 +67,9 @@ spring:
|
||||
default-datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
url: jdbc:mysql://127.0.0.1:3306/wuzhongjie?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: lilishop
|
||||
password: 123
|
||||
maxActive: 20
|
||||
initialSize: 5
|
||||
maxWait: 60000
|
||||
@ -274,12 +274,12 @@ rocketmq:
|
||||
name-server: 127.0.0.1:9876
|
||||
producer:
|
||||
group: lili_group
|
||||
send-message-timeout: 30000
|
||||
send-message-timeout: 100000
|
||||
|
||||
xxl:
|
||||
job:
|
||||
admin:
|
||||
addresses: http://127.0.0.1:9001/xxl-job-admin
|
||||
addresses: http://127.0.0.1:30001/xxl-job-admin
|
||||
executor:
|
||||
appname: xxl-job-executor-lilishop
|
||||
address:
|
||||
|
||||
@ -15,6 +15,11 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.belerweb</groupId>
|
||||
<artifactId>pinyin4j</artifactId>
|
||||
<version>2.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
@ -111,10 +116,10 @@
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<!-- <!– Websocket –>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-websocket</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<!-- MybatisPlus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
@ -468,6 +473,11 @@
|
||||
<scope>runtime</scope>
|
||||
<classifier>osx-aarch_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||
<artifactId>wechatpay-java</artifactId>
|
||||
<version>0.2.15</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@ -131,4 +131,13 @@ public class ResultUtil<T> {
|
||||
return this.resultMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回失败
|
||||
*
|
||||
* @return 消息
|
||||
*/
|
||||
public static <T> ResultMessage<T> error() {
|
||||
return new ResultUtil<T>().setErrorMsg(ResultCode.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package cn.lili.common.exception;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.enums.ResultUtil;
|
||||
import cn.lili.common.vo.ResultMessage;
|
||||
@ -42,26 +43,21 @@ public class GlobalControllerExceptionHandler {
|
||||
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
|
||||
public ResultMessage<Object> handleServiceException(HttpServletRequest request, final Exception e, HttpServletResponse response) {
|
||||
|
||||
|
||||
//如果是自定义异常,则获取异常,返回自定义错误消息
|
||||
if (e instanceof ServiceException) {
|
||||
|
||||
ServiceException serviceException = ((ServiceException) e);
|
||||
ResultCode resultCode = serviceException.getResultCode();
|
||||
|
||||
Integer code = null;
|
||||
String message = null;
|
||||
Integer code = resultCode.code();
|
||||
String message = resultCode.message();
|
||||
|
||||
if (resultCode != null) {
|
||||
code = resultCode.code();
|
||||
message = resultCode.message();
|
||||
}
|
||||
//如果有扩展消息,则输出异常中,跟随补充异常
|
||||
if (!serviceException.getMsg().equals(ServiceException.DEFAULT_MESSAGE)) {
|
||||
message += ":" + serviceException.getMsg();
|
||||
if (message != null) {
|
||||
message = appendErrorMessage(message, serviceException.getMsg());
|
||||
}
|
||||
|
||||
// 对一些特殊异常处理,不再打印error级别的日志
|
||||
assert serviceException.getResultCode() != null;
|
||||
if (serviceException.getResultCode().equals(ResultCode.DEMO_SITE_EXCEPTION)) {
|
||||
log.debug("[DEMO_SITE_EXCEPTION]:{}", serviceException.getResultCode().message(), e);
|
||||
return ResultUtil.error(code, message);
|
||||
@ -103,7 +99,30 @@ public class GlobalControllerExceptionHandler {
|
||||
|
||||
log.error("全局异常[RuntimeException]:", e);
|
||||
|
||||
return ResultUtil.error(ResultCode.ERROR);
|
||||
// 检查异常链是否包含 ServiceException
|
||||
ServiceException serviceException = findServiceException(e);
|
||||
|
||||
if (serviceException != null) {
|
||||
ResultCode resultCode = serviceException.getResultCode();
|
||||
Integer code = resultCode.code();
|
||||
String message = resultCode.message();
|
||||
if (message != null) {
|
||||
message = appendErrorMessage(message, serviceException.getMsg());
|
||||
}
|
||||
return ResultUtil.error(code, message);
|
||||
}
|
||||
return ResultUtil.error();
|
||||
}
|
||||
|
||||
// 遍历异常链,查找 ServiceException
|
||||
private ServiceException findServiceException(Throwable ex) {
|
||||
while (ex != null) {
|
||||
if (ex instanceof ServiceException) {
|
||||
return (ServiceException) ex;
|
||||
}
|
||||
ex = ex.getCause();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// /**
|
||||
@ -168,4 +187,41 @@ public class GlobalControllerExceptionHandler {
|
||||
ConstraintViolationException exception = (ConstraintViolationException) e;
|
||||
return ResultUtil.error(ResultCode.PARAMS_ERROR.code(), exception.getMessage());
|
||||
}
|
||||
/**
|
||||
* 拼接错误消息
|
||||
*
|
||||
* @param message 原始消息
|
||||
* @param appendMessage 需要拼接的消息
|
||||
* @return 拼接后的消息
|
||||
*/
|
||||
private String appendErrorMessage(String message, String appendMessage) {
|
||||
|
||||
//这里的代码看起来有点乱,简单解释一下
|
||||
//场景1:服务A,服务B=》
|
||||
// 服务A调用服务B=》
|
||||
// 服务B抛出异常{扩展消息},拼接后成为{默认消息}:{扩展消息}
|
||||
// 异常被服务A捕获=》
|
||||
// 最终消息拼接过程中,当前方法体参数message是{默认消息},参数appendMessage是服务A给的{默认消息}+{扩展消息},最终会形成{默认消息}+{默认消息}+{扩展消息}
|
||||
//场景2:只有服务A=》
|
||||
// 服务A抛出异常{扩展消息}=》
|
||||
// 当前方法体拼接{默认消息}:{扩展消息} 并输出返回。
|
||||
//
|
||||
//总的来说,由于消息拼接是一个流式传递,由服务间传递,所以这里的消息可能存在A包含B,也可能出现B包含A,
|
||||
// 所以这里需要双重判定,A包含B=》返回A,B包含A=》返回B,否则返回拼接后的消息
|
||||
|
||||
if (message.contains(appendMessage)) {
|
||||
return message;
|
||||
}
|
||||
if (appendMessage.contains(message)) {
|
||||
return appendMessage;
|
||||
}
|
||||
//忽略默认错误信息,如果有其他错误消息体就不再返回默认的错误消息
|
||||
if (message.equals(ResultCode.ERROR.message())) {
|
||||
return appendMessage;
|
||||
}
|
||||
if (appendMessage.equals(ResultCode.ERROR.message())) {
|
||||
return message;
|
||||
}
|
||||
return CharSequenceUtil.format("{}-{}", message, appendMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,12 +15,11 @@ public class ServiceException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 3447728300174142127L;
|
||||
|
||||
public static final String DEFAULT_MESSAGE = "网络错误,请稍后重试!";
|
||||
|
||||
/**
|
||||
* 异常消息
|
||||
*/
|
||||
private String msg = DEFAULT_MESSAGE;
|
||||
private String msg = ResultCode.ERROR.message();
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
|
||||
@ -125,13 +125,21 @@ public final class CurrencyUtil {
|
||||
return (int) price;
|
||||
}
|
||||
|
||||
public static Long getFenLong(Double money) {
|
||||
BigDecimal bigDecimalValue = BigDecimal.valueOf(money);
|
||||
// 乘以 100 并四舍五入到最接近的整数
|
||||
BigDecimal fenValue = bigDecimalValue.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP);
|
||||
|
||||
return fenValue.longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额转分
|
||||
*
|
||||
* @param money 金额
|
||||
* @return double类型分
|
||||
*/
|
||||
public static double reversalFen(Double money) {
|
||||
public static double reversalFen(Integer money) {
|
||||
return div(money, 100);
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,20 @@
|
||||
package cn.lili.common.vo;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.lili.common.utils.StringUtils;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
|
||||
* 分页视图对象
|
||||
|
||||
* @author Chopper
|
||||
|
||||
*/
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.lili.common.utils.StringUtils;
|
||||
/**
|
||||
* 查询参数
|
||||
*
|
||||
@ -42,5 +50,5 @@ public class PageVO implements Serializable {
|
||||
}
|
||||
return sort;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package cn.lili.common.vo;
|
||||
|
||||
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -39,4 +39,81 @@ public class ResultMessage<T> implements Serializable {
|
||||
* 结果对象
|
||||
*/
|
||||
private T result;
|
||||
|
||||
/**
|
||||
* 构造空参对象
|
||||
*/
|
||||
public ResultMessage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建消息对象
|
||||
* @param success 是否成功
|
||||
* @param code 状态码
|
||||
* @param message 消息
|
||||
* @param result 结果
|
||||
*/
|
||||
public ResultMessage(boolean success, Integer code, String message, T result) {
|
||||
this.success = success;
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建成功消息
|
||||
* @param result 结果
|
||||
* @return 消息对象
|
||||
*/
|
||||
public static <T> ResultMessage<T> success(T result) {
|
||||
return new ResultMessage<>(true, ResultCode.SUCCESS.code(), ResultCode.SUCCESS.message(), result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建成功消息
|
||||
* @param message 消息
|
||||
* @param result 结果
|
||||
* @return 消息对象
|
||||
*/
|
||||
public static <T> ResultMessage<T> success(String message, T result) {
|
||||
return new ResultMessage<>(true, ResultCode.SUCCESS.code(), message, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建错误消息
|
||||
* @param code 错误码
|
||||
* @return 消息对象
|
||||
*/
|
||||
public static <T> ResultMessage<T> error(Integer code) {
|
||||
return new ResultMessage<>(false, code, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建错误消息
|
||||
* @param code 错误码
|
||||
* @param message 错误消息
|
||||
* @return 消息对象
|
||||
*/
|
||||
public static <T> ResultMessage<T> error(Integer code, String message) {
|
||||
return new ResultMessage<>(false, code, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建错误消息
|
||||
* @param resultCode 结果枚举
|
||||
* @return 消息对象
|
||||
*/
|
||||
public static <T> ResultMessage<T> error(ResultCode resultCode) {
|
||||
return new ResultMessage<>(false, resultCode.code(), resultCode.message(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建错误消息
|
||||
* @param resultCode 结果枚举
|
||||
* @param message 错误消息
|
||||
* @return 消息对象
|
||||
*/
|
||||
public static <T> ResultMessage<T> error(ResultCode resultCode, String message) {
|
||||
return new ResultMessage<>(false, resultCode.code(), message, null);
|
||||
}
|
||||
}
|
||||
|
||||
46
framework/src/main/java/cn/lili/common/vo/ResultPageVO.java
Normal file
46
framework/src/main/java/cn/lili/common/vo/ResultPageVO.java
Normal file
@ -0,0 +1,46 @@
|
||||
package cn.lili.common.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页结果视图对象
|
||||
* @author Chopper
|
||||
*/
|
||||
@Data
|
||||
public class ResultPageVO<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "总条数")
|
||||
private Long total;
|
||||
|
||||
@ApiModelProperty(value = "当前页数")
|
||||
private Long currentPage;
|
||||
|
||||
@ApiModelProperty(value = "总页数")
|
||||
private Long pages;
|
||||
|
||||
@ApiModelProperty(value = "每页显示的条数")
|
||||
private Long size;
|
||||
|
||||
@ApiModelProperty(value = "数据列表")
|
||||
private List<T> records;
|
||||
|
||||
public ResultPageVO() {
|
||||
}
|
||||
|
||||
public ResultPageVO(Long total, Long pages, Long currentPage, Long size, List<T> records) {
|
||||
this.total = total;
|
||||
this.pages = pages;
|
||||
this.currentPage = currentPage;
|
||||
this.size = size;
|
||||
this.records = records;
|
||||
}
|
||||
|
||||
public static <T> ResultPageVO<T> empty() {
|
||||
return new ResultPageVO<>(0L, 0L, 1L, 10L, null);
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
package cn.lili.modules.im.config;
|
||||
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
@ -15,6 +15,7 @@ import javax.websocket.server.ServerEndpointConfig;
|
||||
* @version v1.0
|
||||
* 2021-12-31 11:53
|
||||
*/
|
||||
@Component
|
||||
public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
@ -22,13 +23,13 @@ public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator
|
||||
*/
|
||||
private static volatile BeanFactory context;
|
||||
|
||||
@Override
|
||||
public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
|
||||
return context.getBean(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
CustomSpringConfigurator.context = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
|
||||
return context.getBean(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package cn.lili.modules.im.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
package cn.lili.modules.im.config;
|
||||
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* WebSocketConfigurator
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2021-12-31 11:53
|
||||
*/
|
||||
@ConditionalOnWebApplication
|
||||
@Configuration
|
||||
public class WebSocketConfigurator {
|
||||
|
||||
@Bean
|
||||
public CustomSpringConfigurator customSpringConfigurator() {
|
||||
// This is just to get context
|
||||
return new CustomSpringConfigurator();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package cn.lili.modules.im.entity.dos;
|
||||
|
||||
import cn.lili.common.vo.SerializableStream;
|
||||
import cn.lili.mybatis.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
|
||||
|
||||
@Data
|
||||
|
||||
@TableName("li_friend")
|
||||
|
||||
@ApiModel(value = "好友关系")
|
||||
|
||||
public class Friend implements Serializable {
|
||||
|
||||
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
|
||||
@ApiModelProperty(value = "ID")
|
||||
|
||||
private String id;
|
||||
|
||||
|
||||
|
||||
@ApiModelProperty(value = "用户ID")
|
||||
|
||||
private String userId;
|
||||
|
||||
|
||||
|
||||
@ApiModelProperty(value = "好友ID")
|
||||
|
||||
private String friendId;
|
||||
|
||||
|
||||
|
||||
@ApiModelProperty(value = "好友备注")
|
||||
|
||||
private String remark;
|
||||
|
||||
|
||||
|
||||
@ApiModelProperty(value = "状态 1:已关注")
|
||||
|
||||
private Integer status;
|
||||
|
||||
|
||||
|
||||
@ApiModelProperty(value = "是否是店铺 0:否 1:是")
|
||||
|
||||
private Integer storeFlag;
|
||||
|
||||
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
|
||||
private Date createTime;
|
||||
|
||||
|
||||
|
||||
@ApiModelProperty(value = "更新时间")
|
||||
|
||||
private Date updateTime;
|
||||
@ApiModelProperty(value = "是否关注")
|
||||
private Integer isMutual;
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package cn.lili.modules.im.entity.dos;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("li_im_group")
|
||||
public class ImGroup {
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
// 群名称
|
||||
private String name;
|
||||
|
||||
// 群头像
|
||||
private String avatar;
|
||||
|
||||
// 群公告
|
||||
private String notice;
|
||||
|
||||
// 群主ID
|
||||
private String ownerId;
|
||||
|
||||
// 创建时间
|
||||
private Date createTime;
|
||||
|
||||
// 更新时间
|
||||
private Date updateTime;
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package cn.lili.modules.im.entity.dos;
|
||||
|
||||
import cn.lili.mybatis.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("li_im_group_member")
|
||||
@ApiModel(value = "群成员")
|
||||
public class ImGroupMember extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ApiModelProperty(value = "成员ID")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty(value = "群ID")
|
||||
private String groupId;
|
||||
|
||||
@ApiModelProperty(value = "成员ID")
|
||||
private String memberId;
|
||||
|
||||
@ApiModelProperty(value = "成员昵称")
|
||||
private String nickname;
|
||||
|
||||
@ApiModelProperty(value = "角色(0:普通成员 1:管理员 2:群主)")
|
||||
private Integer role;
|
||||
|
||||
@ApiModelProperty(value = "是否被禁言")
|
||||
private int isMuted;
|
||||
|
||||
@ApiModelProperty(value = "禁言结束时间")
|
||||
private Date muteEndTime;
|
||||
|
||||
@ApiModelProperty(value = "加入时间")
|
||||
private Date joinTime;
|
||||
|
||||
@ApiModelProperty(value = "创建者")
|
||||
private String createBy;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
@ApiModelProperty(value = "更新时间")
|
||||
private Date updateTime;
|
||||
|
||||
@ApiModelProperty(value = "删除标志")
|
||||
private Boolean deleteFlag;
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package cn.lili.modules.im.entity.dos;
|
||||
|
||||
import cn.lili.mybatis.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@TableName("li_im_member")
|
||||
@ApiModel(value = "IM用户")
|
||||
public class ImMember extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "用户名")
|
||||
private String username;
|
||||
|
||||
@ApiModelProperty(value = "昵称")
|
||||
private String nickname;
|
||||
|
||||
@ApiModelProperty(value = "头像")
|
||||
private String avatar;
|
||||
|
||||
// 其他需要的字段...
|
||||
}
|
||||
@ -4,8 +4,10 @@ import cn.lili.common.utils.SnowFlake;
|
||||
import cn.lili.modules.im.entity.enums.MessageTypeEnum;
|
||||
import cn.lili.modules.im.entity.vo.MessageOperation;
|
||||
import cn.lili.mybatis.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@ -17,48 +19,83 @@ import java.util.Date;
|
||||
*/
|
||||
@Data
|
||||
@TableName("li_im_message")
|
||||
@ApiModel(value = "Im消息")
|
||||
@ApiModel(value = "即时通讯消息")
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ImMessage extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 发送者
|
||||
*/
|
||||
@TableField("talk_id")
|
||||
@ApiModelProperty(value = "会话ID")
|
||||
private String talkId;
|
||||
@TableField("from_user")
|
||||
@ApiModelProperty(value = "发送者ID")
|
||||
private String fromUser;
|
||||
|
||||
/**
|
||||
* 接收者
|
||||
*/
|
||||
@TableField("to_user")
|
||||
@ApiModelProperty(value = "接收者ID")
|
||||
private String toUser;
|
||||
|
||||
/**
|
||||
* 已阅
|
||||
*/
|
||||
private Boolean isRead;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private MessageTypeEnum messageType;
|
||||
|
||||
/**
|
||||
* 聊天id
|
||||
*/
|
||||
private String talkId;
|
||||
|
||||
/**
|
||||
* 消息实体
|
||||
*/
|
||||
@ApiModelProperty(value = "消息内容")
|
||||
private String text;
|
||||
|
||||
@ApiModelProperty(value = "消息类型")
|
||||
private String type;
|
||||
|
||||
@TableField("message_type")
|
||||
@ApiModelProperty(value = "聊天类型")
|
||||
private MessageTypeEnum messageType;
|
||||
|
||||
@ApiModelProperty(value = "消息状态")
|
||||
private Integer status;
|
||||
@TableField("is_read")
|
||||
@ApiModelProperty(value = "是否已读")
|
||||
private Boolean isRead;
|
||||
// @TableField("read_time")
|
||||
// @ApiModelProperty(value = "阅读时间")
|
||||
// private Date readTime; // 确保类型为 Date
|
||||
|
||||
// 文件相关字段
|
||||
@TableField("file_name")
|
||||
@ApiModelProperty(value = "文件名")
|
||||
private String fileName;
|
||||
@TableField("file_size")
|
||||
@ApiModelProperty(value = "文件大小")
|
||||
private Long fileSize;
|
||||
@TableField("file_url")
|
||||
@ApiModelProperty(value = "文件URL")
|
||||
private String fileUrl;
|
||||
|
||||
// 图片相关字段
|
||||
@TableField("image_url")
|
||||
@ApiModelProperty(value = "图片URL")
|
||||
private String imageUrl;
|
||||
@TableField("image_width")
|
||||
@ApiModelProperty(value = "图片宽度")
|
||||
private Integer imageWidth;
|
||||
@TableField("image_height")
|
||||
@ApiModelProperty(value = "图片高度")
|
||||
private Integer imageHeight;
|
||||
@TableField("thumbnail_url")
|
||||
@ApiModelProperty(value = "缩略图URL")
|
||||
private String thumbnailUrl;
|
||||
|
||||
// 语音相关字段
|
||||
@TableField("voice_url")
|
||||
@ApiModelProperty(value = "语音URL")
|
||||
private String voiceUrl;
|
||||
|
||||
@ApiModelProperty(value = "语音时长")
|
||||
private Integer duration;
|
||||
|
||||
@ApiModelProperty(value = "额外信息")
|
||||
private String extra;
|
||||
private String groupId;
|
||||
|
||||
public ImMessage(MessageOperation messageOperation){
|
||||
this.setFromUser(messageOperation.getFrom());
|
||||
this.setMessageType(messageOperation.getMessageType());
|
||||
this.setIsRead(false);
|
||||
// this.setReadTime(new Date());
|
||||
this.setText(messageOperation.getContext());
|
||||
this.setTalkId(messageOperation.getTalkId());
|
||||
this.setCreateTime(new Date());
|
||||
@ -66,4 +103,11 @@ public class ImMessage extends BaseEntity {
|
||||
this.setId(SnowFlake.getIdStr());
|
||||
}
|
||||
|
||||
public void setMessageType(MessageTypeEnum messageType) {
|
||||
this.messageType = messageType;
|
||||
// if (messageType != null) {
|
||||
// this.messageType = MessageTypeEnum.valueOf(messageType.getType()); // 确保type字段也使用大写
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@ -80,6 +80,8 @@ public class ImTalk extends BaseTenantEntity {
|
||||
|
||||
@ApiModelProperty(value = "坐席名称")
|
||||
private String tenantName;
|
||||
@ApiModelProperty(value = "是否关注")
|
||||
private Integer isMutual;
|
||||
|
||||
|
||||
public ImTalk(String userId1, String userId2,
|
||||
@ -135,6 +137,14 @@ public class ImTalk extends BaseTenantEntity {
|
||||
this.name2 = member2.getNickName();
|
||||
}
|
||||
}
|
||||
// public ImTalk2(String userId1, String userId2, String face1, String face2, String name1, String name2) {
|
||||
// this.userId1 = userId1;
|
||||
// this.userId2 = userId2;
|
||||
// this.face1 = face1;
|
||||
// this.face2 = face2;
|
||||
// this.name1 = name1;
|
||||
// this.name2 = name2;
|
||||
// }
|
||||
public ImTalk(Member member, Store store){
|
||||
if(Long.parseLong(member.getId()) > Long.parseLong(store.getId())){
|
||||
this.userId1 = store.getId();
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
package cn.lili.modules.im.entity.dos;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("li_member")
|
||||
public class Member {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
// 创建者
|
||||
private String createBy;
|
||||
|
||||
// 创建时间
|
||||
private Date createTime;
|
||||
|
||||
// 删除标志 true/false
|
||||
private Boolean deleteFlag;
|
||||
|
||||
// 更新者
|
||||
private String updateBy;
|
||||
|
||||
// 更新时间
|
||||
private Date updateTime;
|
||||
|
||||
// 会员生日
|
||||
private Date birthday;
|
||||
|
||||
// 会员状态
|
||||
private Boolean disabled;
|
||||
|
||||
// 会员头像
|
||||
private String face;
|
||||
|
||||
// 是否开通店铺
|
||||
private Boolean haveStore;
|
||||
|
||||
// 手机号码
|
||||
private String mobile;
|
||||
|
||||
// 会员昵称
|
||||
private String nickName;
|
||||
|
||||
// 会员密码
|
||||
private String password;
|
||||
|
||||
// 积分数量
|
||||
private Long point;
|
||||
|
||||
// 会员性别
|
||||
private Integer sex;
|
||||
|
||||
// 店铺ID
|
||||
private String storeId;
|
||||
|
||||
// 会员用户名
|
||||
private String username;
|
||||
|
||||
// 会员地址
|
||||
private String region;
|
||||
|
||||
// 会员地址ID
|
||||
private String regionId;
|
||||
|
||||
// 客户端
|
||||
private String clientEnum;
|
||||
|
||||
// 最后一次登录时间
|
||||
private Date lastLoginDate;
|
||||
|
||||
// 等级ID
|
||||
private String gradeId;
|
||||
|
||||
// 经验值
|
||||
private Long experience;
|
||||
|
||||
// 总积分
|
||||
private Long totalPoint;
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package cn.lili.modules.im.entity.dos;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("li_store") // 表名
|
||||
public class Store {
|
||||
|
||||
@TableId
|
||||
private Long id; // 主键 ID
|
||||
|
||||
private String createBy; // 创建者
|
||||
private Date createTime; // 创建时间
|
||||
private Boolean deleteFlag; // 删除标志
|
||||
private String updateBy; // 更新者
|
||||
private Date updateTime; // 更新时间
|
||||
private String memberId; // 会员 ID
|
||||
private String memberName; // 会员名称
|
||||
private Boolean selfOperated; // 是否自营
|
||||
private String storeDisable; // 店铺状态
|
||||
private Date storeEndTime; // 店铺结束时间
|
||||
private String storeLogo; // 店铺 logo
|
||||
private String storeName; // 店铺名称
|
||||
private String storeAddressDetail; // 详细地址
|
||||
private String storeAddressIdPath; // 地址 ID 路径
|
||||
private String storeAddressPath; // 地址路径
|
||||
private String storeCenter; // 经纬度
|
||||
private String storeDesc; // 店铺介绍
|
||||
private BigDecimal deliveryScore; // 送货评分
|
||||
private BigDecimal descriptionScore; // 描述评分
|
||||
private BigDecimal serviceScore; // 服务评分
|
||||
private Integer goodsNum; // 商品数量
|
||||
private Integer collectionNum; // 收藏数量
|
||||
private String yzfMpSign; // yzf mp sign
|
||||
private String yzfSign; // yzf sign
|
||||
|
||||
// 其他字段可以根据需要添加
|
||||
}
|
||||
@ -6,9 +6,13 @@ import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.vo.PageVO;
|
||||
import cn.lili.modules.im.entity.dos.ImMessage;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* MessageQueryParams
|
||||
*
|
||||
@ -18,6 +22,7 @@ import lombok.EqualsAndHashCode;
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@ApiModel(value = "消息查询参数")
|
||||
public class MessageQueryParams extends PageVO {
|
||||
|
||||
private static final long serialVersionUID = 3504156704697214077L;
|
||||
@ -25,15 +30,39 @@ public class MessageQueryParams extends PageVO {
|
||||
/**
|
||||
* 聊天窗口
|
||||
*/
|
||||
@ApiModelProperty(value = "会话ID")
|
||||
private String talkId;
|
||||
/**
|
||||
* 最后一个消息
|
||||
* 最早消息ID(用于向上加载历史消息)
|
||||
*/
|
||||
private String lastMessageId;
|
||||
@ApiModelProperty(value = "最早消息ID(用于向上加载历史消息)")
|
||||
private String earliestMsgId;
|
||||
/**
|
||||
* 最新消息ID(用于获取新消息)
|
||||
*/
|
||||
@ApiModelProperty(value = "最新消息ID(用于获取新消息)")
|
||||
private String latestMsgId;
|
||||
|
||||
/**
|
||||
* 获取消息数量
|
||||
*/
|
||||
@ApiModelProperty(value = "返回消息数量", example = "20")
|
||||
private Integer num;
|
||||
/**
|
||||
* 消息类型(text:文本,image:图片等)
|
||||
*/
|
||||
@ApiModelProperty(value = "消息类型(text:文本,image:图片等)")
|
||||
private String type;
|
||||
/**
|
||||
* 发送者ID
|
||||
*/
|
||||
@ApiModelProperty(value = "发送者ID")
|
||||
private String fromUser;
|
||||
/**
|
||||
* 接收者ID
|
||||
*/
|
||||
@ApiModelProperty(value = "接收者ID")
|
||||
private String toUser;
|
||||
|
||||
public LambdaQueryWrapper<ImMessage> initQueryWrapper() {
|
||||
if (CharSequenceUtil.isEmpty(talkId)) {
|
||||
@ -45,8 +74,11 @@ public class MessageQueryParams extends PageVO {
|
||||
|
||||
LambdaQueryWrapper<ImMessage> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(ImMessage::getTalkId, talkId);
|
||||
if (CharSequenceUtil.isNotEmpty(lastMessageId)) {
|
||||
lambdaQueryWrapper.lt(ImMessage::getId, lastMessageId);
|
||||
if (CharSequenceUtil.isNotEmpty(earliestMsgId)) {
|
||||
lambdaQueryWrapper.ge(ImMessage::getId, earliestMsgId);
|
||||
}
|
||||
if (CharSequenceUtil.isNotEmpty(latestMsgId)) {
|
||||
lambdaQueryWrapper.le(ImMessage::getId, latestMsgId);
|
||||
}
|
||||
lambdaQueryWrapper.orderByDesc(ImMessage::getCreateTime);
|
||||
// lambdaQueryWrapper.last("limit " + num);
|
||||
|
||||
@ -1,31 +1,53 @@
|
||||
package cn.lili.modules.im.entity.enums;
|
||||
|
||||
/**
|
||||
* 消息的类型
|
||||
*
|
||||
* @author liushuai(liushuai711 @ gmail.com)
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2022/2/10 16:36
|
||||
* 消息状态枚举
|
||||
*/
|
||||
public enum MessageStatusEnum {
|
||||
//socket刚打开时发送的消息,这个一般是是刚打开socket链接,进行登录,传入token用
|
||||
CONNECT,
|
||||
//心跳类型的消息,此种类型的消息只有 type 、 text 两种属性
|
||||
HEARTBEAT,
|
||||
//用户打开一个对话框,准备跟某人聊天时
|
||||
OPEN,
|
||||
//客服进行自动回复。客户端发起这种类型请求,则是在拉取对方是否有自动回复,如果有,服务端就会给客户端发送过自动回复的信息
|
||||
AUTO_REPLY,
|
||||
//正常收发消息沟通,文字、表情等沟通
|
||||
MSG,
|
||||
//扩展。比如发送商品、发送订单
|
||||
EXTEND,
|
||||
//系统提示,如提示 对方已离线
|
||||
SYSTEM,
|
||||
//服务端发送到客户端,用于设置客户端的用户信息。会吧 com.xnx3.yunkefu.core.vo.bean.User 传过去
|
||||
SET_USER,
|
||||
//结束服务
|
||||
CLOSE_SERVICE;
|
||||
|
||||
NORMAL(0, "正常"),
|
||||
DELETED(1, "已删除"),
|
||||
RECALLED(2, "已撤回"),
|
||||
|
||||
// WebSocket相关状态
|
||||
CONNECT(100, "连接"),
|
||||
HEARTBEAT(101, "心跳"),
|
||||
OPEN(102, "打开对话"),
|
||||
AUTO_REPLY(103, "自动回复"),
|
||||
MSG(104, "普通消息"),
|
||||
EXTEND(105, "扩展消息"),
|
||||
SYSTEM(106, "系统消息"),
|
||||
SET_USER(107, "设置用户"),
|
||||
CLOSE_SERVICE(108, "结束服务");
|
||||
|
||||
private final Integer status;
|
||||
private final String description;
|
||||
|
||||
MessageStatusEnum(Integer status, String description) {
|
||||
this.status = status;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Integer getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据状态码获取枚举
|
||||
*/
|
||||
public static MessageStatusEnum getByStatus(Integer status) {
|
||||
if (status == null) {
|
||||
return null;
|
||||
}
|
||||
for (MessageStatusEnum statusEnum : values()) {
|
||||
if (statusEnum.getStatus().equals(status)) {
|
||||
return statusEnum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,45 @@
|
||||
package cn.lili.modules.im.entity.enums;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*
|
||||
* @author liushuai
|
||||
* 消息类型枚举
|
||||
*/
|
||||
public enum MessageTypeEnum {
|
||||
/**
|
||||
* 消息类型枚举
|
||||
* <p>
|
||||
* 普通消息
|
||||
* 图片
|
||||
* 语音
|
||||
* 视频
|
||||
*/
|
||||
MESSAGE,
|
||||
PICTURE,
|
||||
VOICE,
|
||||
GOODS,
|
||||
ORDER,
|
||||
VIDEO
|
||||
MESSAGE("message", "普通消息"),
|
||||
TEXT("TEXT", "文本消息"),
|
||||
IMAGE("IMAGE", "图片消息"),
|
||||
FILE("FILE", "文件消息"),
|
||||
VOICE("VOICE", "语音消息"),
|
||||
VIDEO("VIDEO", "视频消息"),
|
||||
SYSTEM("SYSTEM", "系统消息"),
|
||||
GROUP("GROUP", "群聊消息"),
|
||||
LOCATION("LOCATION", "位置消息");
|
||||
|
||||
private final String type;
|
||||
private final String description;
|
||||
|
||||
MessageTypeEnum(String type, String description) {
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public static MessageTypeEnum fromType(String type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (MessageTypeEnum typeEnum : MessageTypeEnum.values()) {
|
||||
if (typeEnum.getType().equalsIgnoreCase(type)) {
|
||||
return typeEnum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,5 @@ public enum OperationType {
|
||||
MESSAGE,
|
||||
HISTORY,
|
||||
READ,
|
||||
UNREAD,
|
||||
|
||||
UNREAD
|
||||
}
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
package cn.lili.modules.im.entity.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@ApiModel(value = "好友申请VO")
|
||||
public class FriendRequestVO {
|
||||
|
||||
@ApiModelProperty(value = "申请ID")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty(value = "申请者ID")
|
||||
private String userId;
|
||||
|
||||
@ApiModelProperty(value = "申请者昵称")
|
||||
private String nickname;
|
||||
|
||||
@ApiModelProperty(value = "申请者头像")
|
||||
private String avatar;
|
||||
|
||||
@ApiModelProperty(value = "申请者手机号")
|
||||
private String mobile;
|
||||
|
||||
@ApiModelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty(value = "状态(0:待处理 1:已接受 2:已拒绝)")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "申请时间")
|
||||
private Date createTime;
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package cn.lili.modules.im.entity.vo;
|
||||
|
||||
import cn.lili.modules.im.entity.dos.Friend;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
import cn.lili.modules.im.entity.dos.Friend;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
|
||||
|
||||
@Data
|
||||
|
||||
@ApiModel(value = "好友VO")
|
||||
|
||||
public class FriendVO implements Serializable {
|
||||
|
||||
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
|
||||
@ApiModelProperty(value = "用户ID")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty(value = "关系ID")
|
||||
private String friendId;
|
||||
private String talkId;
|
||||
|
||||
@ApiModelProperty(value = "昵称")
|
||||
private String nickname;
|
||||
|
||||
@ApiModelProperty(value = "头像")
|
||||
private String avatar;
|
||||
|
||||
@ApiModelProperty(value = "地区")
|
||||
private String region;
|
||||
|
||||
@ApiModelProperty(value = "手机号")
|
||||
private String mobile;
|
||||
|
||||
@ApiModelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty(value = "是否是店铺 0:否 1:是")
|
||||
private Integer storeFlag;
|
||||
|
||||
@ApiModelProperty(value = "是否互相关注")
|
||||
private Integer isMutual;
|
||||
|
||||
@ApiModelProperty(value = "关注时间")
|
||||
private Date createTime;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.lili.modules.im.entity.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@ApiModel(value = "群成员信息")
|
||||
public class ImGroupMemberVO {
|
||||
@ApiModelProperty(value = "成员ID")
|
||||
private String memberId;
|
||||
|
||||
@ApiModelProperty(value = "成员昵称")
|
||||
private String nickname;
|
||||
|
||||
@ApiModelProperty(value = "成员头像")
|
||||
private String avatar;
|
||||
|
||||
@ApiModelProperty(value = "角色(0:普通成员 1:管理员 2:群主)")
|
||||
private Integer role;
|
||||
|
||||
@ApiModelProperty(value = "是否被禁言")
|
||||
private Integer isMuted;
|
||||
|
||||
@ApiModelProperty(value = "禁言结束时间")
|
||||
private Date muteEndTime;
|
||||
|
||||
@ApiModelProperty(value = "加入时间")
|
||||
private Date joinTime;
|
||||
private String displayName;
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package cn.lili.modules.im.entity.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@ApiModel(value = "群聊信息")
|
||||
public class ImGroupVO {
|
||||
@ApiModelProperty(value = "群ID")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty(value = "群名称")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "群头像")
|
||||
private String avatar;
|
||||
|
||||
@ApiModelProperty(value = "群公告")
|
||||
private String notice;
|
||||
|
||||
@ApiModelProperty(value = "群主ID")
|
||||
private String ownerId;
|
||||
|
||||
@ApiModelProperty(value = "成员数量")
|
||||
private Integer memberCount;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
private Integer role;
|
||||
private String displayName;
|
||||
@ApiModelProperty(value = "最后一条消息")
|
||||
private ImMessageVO lastMessage;
|
||||
|
||||
@ApiModelProperty(value = "未读消息数")
|
||||
private Integer unreadCount;
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package cn.lili.modules.im.entity.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@ApiModel(value = "即时通讯消息VO")
|
||||
public class ImMessageVO {
|
||||
|
||||
@ApiModelProperty(value = "消息ID")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty(value = "会话ID")
|
||||
private String talkId;
|
||||
|
||||
@ApiModelProperty(value = "发送者ID")
|
||||
private String fromUser;
|
||||
|
||||
@ApiModelProperty(value = "发送者昵称")
|
||||
private String fromName;
|
||||
|
||||
@ApiModelProperty(value = "发送者头像")
|
||||
private String fromAvatar;
|
||||
|
||||
@ApiModelProperty(value = "接收者ID")
|
||||
private String toUser;
|
||||
|
||||
@ApiModelProperty(value = "消息内容")
|
||||
private String text;
|
||||
|
||||
@ApiModelProperty(value = "消息类型")
|
||||
private String type;
|
||||
|
||||
@ApiModelProperty(value = "聊天类型(单聊/群聊)")
|
||||
private String messageType;
|
||||
|
||||
@ApiModelProperty(value = "消息状态")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "是否已读")
|
||||
private Boolean isRead;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
// 扩展字段
|
||||
@ApiModelProperty(value = "文件名")
|
||||
private String fileName;
|
||||
|
||||
@ApiModelProperty(value = "文件大小")
|
||||
private Long fileSize;
|
||||
|
||||
@ApiModelProperty(value = "文件URL")
|
||||
private String fileUrl;
|
||||
|
||||
@ApiModelProperty(value = "图片URL")
|
||||
private String imageUrl;
|
||||
|
||||
@ApiModelProperty(value = "图片宽度")
|
||||
private Integer imageWidth;
|
||||
|
||||
@ApiModelProperty(value = "图片高度")
|
||||
private Integer imageHeight;
|
||||
|
||||
@ApiModelProperty(value = "缩略图URL")
|
||||
private String thumbnailUrl;
|
||||
|
||||
@ApiModelProperty(value = "语音URL")
|
||||
private String voiceUrl;
|
||||
|
||||
@ApiModelProperty(value = "语音时长")
|
||||
private Integer duration;
|
||||
|
||||
@ApiModelProperty(value = "额外信息")
|
||||
private String extra;
|
||||
}
|
||||
@ -53,6 +53,8 @@ public class ImTalkVO extends BaseTenantEntity {
|
||||
|
||||
@ApiModelProperty(value = "未读数量")
|
||||
private Long unread;
|
||||
@ApiModelProperty(value = "是否关注")
|
||||
private Integer isMutual;
|
||||
|
||||
public ImTalkVO() {
|
||||
|
||||
@ -66,6 +68,7 @@ public class ImTalkVO extends BaseTenantEntity {
|
||||
name = imTalk.getName1();
|
||||
face = imTalk.getFace1();
|
||||
storeFlag = imTalk.getStoreFlag1();
|
||||
isMutual=imTalk.getIsMutual();
|
||||
} else {
|
||||
userId = imTalk.getUserId2();
|
||||
top = imTalk.getTop2();
|
||||
@ -73,6 +76,7 @@ public class ImTalkVO extends BaseTenantEntity {
|
||||
name = imTalk.getName2();
|
||||
face = imTalk.getFace2();
|
||||
storeFlag = imTalk.getStoreFlag2();
|
||||
isMutual=imTalk.getIsMutual();
|
||||
}
|
||||
lastTalkMessage = imTalk.getLastTalkMessage();
|
||||
lastTalkTime = imTalk.getLastTalkTime();
|
||||
|
||||
@ -41,13 +41,13 @@ public class MessageOperation {
|
||||
|
||||
public void setOperationType(String operationType) {
|
||||
if (!StringUtils.isEmpty(operationType)) {
|
||||
this.operationType = OperationType.valueOf(operationType);
|
||||
this.operationType = OperationType.valueOf(operationType.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
public void setMessageType(String messageType) {
|
||||
if (!StringUtils.isEmpty(messageType)) {
|
||||
this.messageType = MessageTypeEnum.valueOf(messageType);
|
||||
this.messageType = MessageTypeEnum.valueOf(messageType.toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
package cn.lili.modules.im.mapper;
|
||||
|
||||
|
||||
import cn.lili.modules.im.entity.dos.Friend;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
|
||||
|
||||
@Mapper
|
||||
|
||||
public interface FriendMapper extends BaseMapper<Friend> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package cn.lili.modules.im.mapper;
|
||||
|
||||
import cn.lili.modules.im.entity.dos.ImGroup;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
public interface ImGroupMapper extends BaseMapper<ImGroup> {
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package cn.lili.modules.im.mapper;
|
||||
|
||||
import cn.lili.modules.im.entity.dos.ImGroupMember;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
|
||||
@Mapper
|
||||
|
||||
public interface ImGroupMemberMapper extends BaseMapper<ImGroupMember> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
package cn.lili.modules.im.service;
|
||||
|
||||
import cn.lili.modules.im.entity.dos.Friend;
|
||||
import cn.lili.modules.im.entity.vo.FriendVO;
|
||||
import cn.lili.modules.im.entity.vo.FriendRequestVO;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 好友关系业务层
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
public interface FriendService extends IService<Friend> {
|
||||
|
||||
/**
|
||||
* 获取关注列表
|
||||
* @param userId 用户ID
|
||||
* @return 关注的用户列表
|
||||
*/
|
||||
List<FriendVO> getMutualFriends(String userId);
|
||||
|
||||
/**
|
||||
* 获取用户详细信息
|
||||
* @param friendId 用户ID
|
||||
* @return 用户详细信息
|
||||
*/
|
||||
FriendVO getFriendDetails(String friendId);
|
||||
|
||||
/**
|
||||
* 取消关注
|
||||
* @param userId 当前用户ID
|
||||
* @param friendId 要取消关注的用户ID
|
||||
*/
|
||||
void removeFriend(String userId, String friendId);
|
||||
|
||||
/**
|
||||
* 关注用户
|
||||
* @param userId 当前用户ID
|
||||
* @param friendId 要关注的用户ID
|
||||
*/
|
||||
void addFriend(String userId, String friendId);
|
||||
|
||||
/**
|
||||
* 通过手机号关注用户
|
||||
* @param mobile 手机号
|
||||
* @param remark 备注
|
||||
*/
|
||||
void addFriendByMobile(String mobile, String remark);
|
||||
|
||||
/**
|
||||
* 更新备注
|
||||
* @param userId 当前用户ID
|
||||
* @param friendId 关注的用户ID
|
||||
* @param remark 备注
|
||||
*/
|
||||
void updateRemark(String userId, String friendId, String remark);
|
||||
//
|
||||
// /**
|
||||
// * 获取好友申请列表
|
||||
// * @param userId 用户ID
|
||||
// * @return 申请列表
|
||||
// */
|
||||
// List<FriendRequestVO> getFriendRequests(String userId);
|
||||
//
|
||||
// /**
|
||||
// * 接受好友申请
|
||||
// * @param requestId 申请ID
|
||||
// * @param userId 当前用户ID
|
||||
// */
|
||||
// void acceptFriendRequest(String requestId, String userId);
|
||||
//
|
||||
// /**
|
||||
// * 拒绝好友申请
|
||||
// * @param requestId 申请ID
|
||||
// * @param userId 当前用户ID
|
||||
// */
|
||||
// void rejectFriendRequest(String requestId, String userId);
|
||||
|
||||
/**
|
||||
* 搜索用户
|
||||
* @param keyword 搜索关键词(用户名/手机号)
|
||||
* @param onlyStore 是否只搜索店铺
|
||||
* @param currentUserId 当前用户ID
|
||||
* @return 用户列表
|
||||
*/
|
||||
List<FriendVO> searchUsers(String keyword, Boolean onlyStore, String currentUserId);
|
||||
}
|
||||
@ -0,0 +1,154 @@
|
||||
package cn.lili.modules.im.service;
|
||||
|
||||
import cn.lili.modules.im.entity.dos.ImGroup;
|
||||
|
||||
import cn.lili.modules.im.entity.vo.FriendVO;
|
||||
import cn.lili.modules.im.entity.vo.ImGroupMemberVO;
|
||||
import cn.lili.modules.im.entity.vo.ImGroupVO;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
public interface ImGroupService extends IService<ImGroup> {
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户加入的群聊列表
|
||||
* @param userId 用户ID
|
||||
* @return 群聊列表
|
||||
*/
|
||||
List<ImGroupVO> getUserGroups(String userId);
|
||||
|
||||
/**
|
||||
* 获取群聊详情
|
||||
* @param groupId 群ID
|
||||
* @return 群聊详情
|
||||
*/
|
||||
ImGroupVO getGroupDetail(String groupId);
|
||||
|
||||
/**
|
||||
* 获取群成员列表
|
||||
* @param groupId 群ID
|
||||
* @return 群成员列表
|
||||
*/
|
||||
List<ImGroupMemberVO> getGroupMembers(String groupId);
|
||||
|
||||
/**
|
||||
* 退出群聊
|
||||
* @param groupId 群ID
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
void quitGroup(String groupId, String userId);
|
||||
|
||||
/**
|
||||
* 修改群信息
|
||||
* @param groupId 群ID
|
||||
* @param groupName 群名称
|
||||
* @param notice 群公告
|
||||
* @param avatar 群头像
|
||||
*/
|
||||
void updateGroupInfo(String groupId, String groupName, String notice, String avatar);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
* 创建群聊
|
||||
|
||||
* @param groupName 群名称
|
||||
|
||||
* @param memberIds 邀请的成员ID列表
|
||||
|
||||
* @return 群聊信息
|
||||
|
||||
*/
|
||||
|
||||
ImGroup createGroup(String groupName, List<String> memberIds);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
* 解散群聊
|
||||
|
||||
* @param groupId 群ID
|
||||
|
||||
*/
|
||||
|
||||
void dismissGroup(String groupId);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
* 邀请成员
|
||||
|
||||
* @param groupId 群ID
|
||||
|
||||
* @param memberIds 成员ID列表
|
||||
|
||||
*/
|
||||
|
||||
void inviteMembers(String groupId, List<String> memberIds);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
* 设置管理员
|
||||
|
||||
* @param groupId 群ID
|
||||
|
||||
* @param memberId 成员ID
|
||||
|
||||
*/
|
||||
|
||||
void setAdmin(String groupId, String memberId);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
* 取消管理员
|
||||
|
||||
* @param groupId 群ID
|
||||
|
||||
* @param memberId 成员ID
|
||||
|
||||
*/
|
||||
|
||||
void removeAdmin(String groupId, String memberId);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
* 禁言成员
|
||||
|
||||
* @param groupId 群ID
|
||||
|
||||
* @param memberId 成员ID
|
||||
|
||||
* @param duration 禁言时长(分钟)
|
||||
|
||||
*/
|
||||
|
||||
void muteMember(String groupId, String memberId, Integer duration);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
* 解除成员禁言
|
||||
|
||||
* @param groupId 群ID
|
||||
|
||||
* @param memberId 成员ID
|
||||
|
||||
*/
|
||||
|
||||
void unmuteMember(String groupId, String memberId);
|
||||
|
||||
}
|
||||
@ -1,7 +1,10 @@
|
||||
package cn.lili.modules.im.service;
|
||||
|
||||
import cn.lili.common.vo.PageVO;
|
||||
import cn.lili.common.vo.ResultPageVO;
|
||||
import cn.lili.modules.im.entity.dos.ImMessage;
|
||||
import cn.lili.modules.im.entity.dto.MessageQueryParams;
|
||||
import cn.lili.modules.im.entity.vo.ImMessageVO;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
@ -13,6 +16,33 @@ import java.util.List;
|
||||
*/
|
||||
public interface ImMessageService extends IService<ImMessage> {
|
||||
|
||||
/**
|
||||
* 获取历史消息
|
||||
* @param talkId 对话ID
|
||||
* @param earliestMsgId 最早消息ID
|
||||
* @param pageSize 每页大小
|
||||
* @return 消息列表
|
||||
*/
|
||||
List<ImMessageVO> getHistoryMessages(String talkId, String earliestMsgId, Integer pageSize);
|
||||
|
||||
/**
|
||||
* 获取最近消息
|
||||
* @param talkId 对话ID
|
||||
* @param latestMsgId 最新消息ID
|
||||
* @param pageSize 每页大小
|
||||
* @return 消息列表
|
||||
*/
|
||||
List<ImMessageVO> getRecentMessages(String talkId, String latestMsgId, Integer pageSize);
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @param toId 接收者ID
|
||||
* @param content 消息内容
|
||||
* @param type 消息类型
|
||||
* @return 消息对象
|
||||
*/
|
||||
ImMessageVO sendMessage(String toId, String content, String type);
|
||||
|
||||
/**
|
||||
* 阅读消息
|
||||
*
|
||||
@ -63,4 +93,60 @@ public interface ImMessageService extends IService<ImMessage> {
|
||||
* 清空所有未读消息
|
||||
*/
|
||||
void cleanUnreadMessage();
|
||||
|
||||
/**
|
||||
* 获取群聊消息历史
|
||||
* @param groupId 群ID
|
||||
* @param pageNumber 页码
|
||||
* @param pageSize 每页大小
|
||||
* @return 消息列表
|
||||
*/
|
||||
ResultPageVO<ImMessageVO> getGroupMessages(String groupId, Integer pageNumber, Integer pageSize);
|
||||
|
||||
/**
|
||||
* 发送群聊消息
|
||||
* @param groupId 群ID
|
||||
* @param content 消息内容
|
||||
* @param type 消息类型
|
||||
* @return 消息对象
|
||||
*/
|
||||
ImMessageVO sendGroupMessage(String groupId, String content, String type);
|
||||
|
||||
/**
|
||||
* 撤回消息
|
||||
* @param messageId 消息ID
|
||||
*/
|
||||
void recallMessage(String messageId);
|
||||
|
||||
/**
|
||||
* 加载历史消息
|
||||
* @param talkId 会话ID
|
||||
* @param earliestMsgId 最早消息ID
|
||||
* @param size 每页大小
|
||||
* @return 消息列表
|
||||
*/
|
||||
List<ImMessageVO> loadHistoryMessages(String talkId, String earliestMsgId, Integer size);
|
||||
|
||||
/**
|
||||
* 加载最新消息
|
||||
* @param talkId 会话ID
|
||||
* @param size 每页大小
|
||||
* @return 消息列表
|
||||
*/
|
||||
List<ImMessageVO> loadLatestMessages(String talkId, Integer size);
|
||||
|
||||
/**
|
||||
* 获取新消息
|
||||
* @param talkId 会话ID
|
||||
* @param latestMsgId 最新消息ID
|
||||
* @return 新消息列表
|
||||
*/
|
||||
List<ImMessageVO> getNewerMessages(String talkId, String latestMsgId);
|
||||
|
||||
/**
|
||||
* 标记消息已读
|
||||
* @param messages 消息列表
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
void readMessages(List<ImMessage> messages, String userId);
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package cn.lili.modules.im.service;
|
||||
|
||||
import cn.lili.modules.im.entity.vo.MessageVO;
|
||||
|
||||
public interface MessageSender {
|
||||
void sendMessage(String sessionId, MessageVO message);
|
||||
}
|
||||
@ -0,0 +1,404 @@
|
||||
package cn.lili.modules.im.serviceimpl;
|
||||
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.modules.im.entity.dos.Friend;
|
||||
import cn.lili.modules.im.entity.dos.ImTalk;
|
||||
import cn.lili.modules.im.entity.vo.FriendVO;
|
||||
import cn.lili.modules.im.entity.vo.FriendRequestVO;
|
||||
import cn.lili.modules.im.mapper.FriendMapper;
|
||||
import cn.lili.modules.im.service.FriendService;
|
||||
import cn.lili.modules.im.service.ImTalkService;
|
||||
import cn.lili.modules.member.entity.dos.Member;
|
||||
import cn.lili.modules.member.service.MemberService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 好友业务层实现
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> implements FriendService {
|
||||
private final ImTalkService imTalkService;
|
||||
private final MemberService memberService;
|
||||
private final FriendMapper friendMapper;
|
||||
|
||||
@Override
|
||||
public List<FriendVO> getMutualFriends(String userId) {
|
||||
// 验证用户登录状态
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
|
||||
// 获取当前用户的ID
|
||||
String currentUserId = currentUser.getId();
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Friend::getUserId, currentUserId) // 查询当前用户的关注列表
|
||||
.eq(Friend::getStatus, 1); // 只查询状态为1的记录
|
||||
|
||||
// 查询关注列表
|
||||
List<Friend> friends = this.list(queryWrapper);
|
||||
if (friends.isEmpty()) {
|
||||
return new ArrayList<>(); // 如果没有找到,返回空列表
|
||||
}
|
||||
|
||||
// 收集所有关注的用户ID
|
||||
List<String> friendIds = friends.stream()
|
||||
.map(Friend::getFriendId) // 获取 friendId
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 检查 friendIds 是否为空
|
||||
if (friendIds.isEmpty()) {
|
||||
log.warn("No friend IDs found for user: {}", currentUserId);
|
||||
return new ArrayList<>(); // 或者抛出异常
|
||||
}
|
||||
|
||||
// 批量查询用户信息
|
||||
List<Member> members = memberService.listByIds(friendIds);
|
||||
Map<String, Member> memberMap = members.stream()
|
||||
.collect(Collectors.toMap(Member::getId, member -> member));
|
||||
|
||||
// 组装VO对象
|
||||
return friends.stream().map(friend -> {
|
||||
Member member = memberMap.get(friend.getFriendId());
|
||||
if (member != null) {
|
||||
FriendVO vo = new FriendVO();
|
||||
vo.setId(member.getId().toString()); // 设置为用户的 ID
|
||||
vo.setFriendId(friend.getFriendId()); // 设置为 Friend 的 ID
|
||||
vo.setNickname(member.getNickName());
|
||||
vo.setAvatar(member.getFace());
|
||||
// 其他字段设置...
|
||||
return vo;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// 假设这个方法用于从 ImTalk 表中获取 talkId
|
||||
private String getTalkId(String userId1, String userId2) {
|
||||
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper
|
||||
.or().eq(ImTalk::getUserId1, userId1).eq(ImTalk::getUserId2, userId2)
|
||||
.or().eq(ImTalk::getUserId1, userId2).eq(ImTalk::getUserId2, userId1);
|
||||
|
||||
ImTalk imTalk = imTalkService.getOne(queryWrapper);
|
||||
return imTalk != null ? imTalk.getId() : null; // 返回 talkId
|
||||
}
|
||||
@Override
|
||||
public void addFriend(String userId, String friendId) {
|
||||
// 验证用户登录状态
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
|
||||
// 检查是否已经关注
|
||||
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Friend::getUserId, userId)
|
||||
.eq(Friend::getFriendId, friendId);
|
||||
|
||||
// 如果已经关注,抛出异常
|
||||
if (this.count(queryWrapper) > 0) {
|
||||
throw new ServiceException("您已经关注过该用户");
|
||||
}
|
||||
|
||||
// 创建新的关注关系
|
||||
Friend friend = new Friend();
|
||||
friend.setUserId(userId);
|
||||
friend.setFriendId(friendId);
|
||||
friend.setStatus(1); // 设置为已关注状态
|
||||
friend.setIsMutual(1);
|
||||
friend.setCreateTime(new Date());
|
||||
// friend.setUpdateTime(new Date());
|
||||
|
||||
// 检查对方是否也关注了当前用户
|
||||
Integer mutualStatus = isFollowing(friendId, userId); // 检查 friendId 是否关注了 userId
|
||||
|
||||
// 设置互相关注状态
|
||||
if (mutualStatus == 1) {
|
||||
friend.setIsMutual(2); // 设置为互相关注
|
||||
} else {
|
||||
friend.setIsMutual(1); // 设置为单向关注
|
||||
}
|
||||
|
||||
// 保存关注关系
|
||||
this.save(friend);
|
||||
}
|
||||
@Override
|
||||
public FriendVO getFriendDetails(String friendId) {
|
||||
// 验证用户登录状态
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
|
||||
// 获取好友信息
|
||||
Member member = memberService.getById(friendId);
|
||||
if (member == null) {
|
||||
throw new ServiceException("用户不存在");
|
||||
}
|
||||
|
||||
// 转换为VO
|
||||
FriendVO vo = new FriendVO();
|
||||
vo.setId(member.getId().toString());
|
||||
vo.setNickname(member.getNickName());
|
||||
vo.setAvatar(member.getFace());
|
||||
vo.setRegion(member.getRegion());
|
||||
vo.setMobile(member.getMobile());
|
||||
|
||||
// 设置默认值
|
||||
vo.setRemark(""); // 或者设置为 null
|
||||
vo.setStoreFlag(member.getHaveStore() ? 1 : 0); // 假设你有这个字段
|
||||
vo.setCreateTime(new Date()); // 或者设置为 null
|
||||
|
||||
// 获取当前用户ID
|
||||
String currentUserId = currentUser.getId();
|
||||
|
||||
// 查询Friend表以获取isMutual值
|
||||
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Friend::getUserId, currentUserId)
|
||||
.eq(Friend::getFriendId, friendId);
|
||||
|
||||
Friend friendRelation = this.getOne(queryWrapper);
|
||||
if (friendRelation != null) {
|
||||
vo.setIsMutual(friendRelation.getIsMutual()); // 设置isMutual值
|
||||
vo.setFriendId(friendRelation.getFriendId());
|
||||
} else {
|
||||
vo.setIsMutual(0); // 如果没有找到关系,设置为未关注
|
||||
}
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFriend(String userId, String friendId) {
|
||||
// 验证用户登录状态
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
|
||||
// 验证操作权限
|
||||
if (!currentUser.getId().equals(userId)) {
|
||||
throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
|
||||
}
|
||||
|
||||
// 取消关注
|
||||
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Friend::getUserId, userId)
|
||||
.eq(Friend::getFriendId, friendId);
|
||||
|
||||
if (!this.remove(queryWrapper)) {
|
||||
throw new ServiceException("取消关注失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRemark(String userId, String friendId, String remark) {
|
||||
// 验证用户登录状态
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
|
||||
// 验证操作权限
|
||||
if (!currentUser.getId().equals(userId)) {
|
||||
throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
|
||||
}
|
||||
|
||||
// 更新备注
|
||||
LambdaQueryWrapper<Friend> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Friend::getUserId, userId)
|
||||
.eq(Friend::getFriendId, friendId)
|
||||
.eq(Friend::getStatus, 1);
|
||||
|
||||
Friend friend = this.getOne(queryWrapper);
|
||||
if (friend == null) {
|
||||
throw new ServiceException("未关注该用户");
|
||||
}
|
||||
|
||||
friend.setRemark(remark);
|
||||
friend.setUpdateTime(new Date());
|
||||
|
||||
if (!this.updateById(friend)) {
|
||||
throw new ServiceException("更新备注失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFriendByMobile(String mobile, String remark) {
|
||||
// 查找目标用户
|
||||
Member targetMember = memberService.findByMobile(mobile);
|
||||
if (targetMember == null) {
|
||||
throw new ServiceException("该手机号未注册");
|
||||
}
|
||||
|
||||
String currentUserId = UserContext.getCurrentUser().getId();
|
||||
String targetUserId = targetMember.getId().toString();
|
||||
|
||||
// 不能关注自己
|
||||
if (currentUserId.equals(targetUserId)) {
|
||||
throw new ServiceException("不能关注自己");
|
||||
}
|
||||
|
||||
// 检查是否已经关注
|
||||
if (isFollowing(currentUserId, targetUserId) > 0) {
|
||||
throw new ServiceException("已经关注该用户");
|
||||
}
|
||||
|
||||
// 创建关注关系
|
||||
Friend friend = new Friend();
|
||||
friend.setUserId(currentUserId);
|
||||
friend.setFriendId(targetUserId);
|
||||
friend.setRemark(remark);
|
||||
friend.setStatus(1);
|
||||
friend.setIsMutual(1); // 直接设置为已关注状态
|
||||
friend.setStoreFlag(targetMember.getHaveStore() ? 1 : 0); // 设置是否是店铺
|
||||
friend.setCreateTime(new Date());
|
||||
// friend.setUpdateTime(new Date());
|
||||
|
||||
// 检查对方是否也关注了当前用户
|
||||
Integer mutualStatus = isFollowing(targetUserId, currentUserId);
|
||||
|
||||
// 设置互相关注状态
|
||||
if (mutualStatus == 1) {
|
||||
friend.setIsMutual(2); // 设置为互相关注
|
||||
} else {
|
||||
friend.setIsMutual(1); // 设置为单向关注
|
||||
}
|
||||
|
||||
// 保存关注关系
|
||||
this.save(friend);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否是好友关系(互相关注)
|
||||
*/
|
||||
private Integer isFriend(String userId, String friendId) {
|
||||
// 检查是否互相关注
|
||||
Integer isUserFollowingFriend = isFollowing(userId, friendId);
|
||||
Integer isFriendFollowingUser = isFollowing(friendId, userId);
|
||||
|
||||
// 如果双方都关注,返回 2;如果只有一个方向关注,返回 1;否则返回 0
|
||||
if (isUserFollowingFriend == 1 && isFriendFollowingUser == 1) {
|
||||
return 2; // 互相关注
|
||||
} else if (isUserFollowingFriend == 1 || isFriendFollowingUser == 1) {
|
||||
return 1; // 单向关注
|
||||
}
|
||||
return 0; // 未关注
|
||||
}
|
||||
/**
|
||||
* 检查是否已关注
|
||||
*/
|
||||
public Integer isFollowing(String currentUserId, String targetUserId) {
|
||||
// 查询当前用户是否关注目标用户
|
||||
Long count = friendMapper.selectCount(new LambdaQueryWrapper<Friend>()
|
||||
.eq(Friend::getUserId, currentUserId)
|
||||
.eq(Friend::getFriendId, targetUserId)
|
||||
.eq(Friend::getIsMutual, 1)); // 确保状态为已关注
|
||||
|
||||
if (count > 0) {
|
||||
// 查询目标用户是否也关注当前用户
|
||||
Long mutualCount = friendMapper.selectCount(new LambdaQueryWrapper<Friend>()
|
||||
.eq(Friend::getUserId, targetUserId)
|
||||
.eq(Friend::getFriendId, currentUserId)
|
||||
.eq(Friend::getIsMutual, 1)); // 确保状态为已关注
|
||||
|
||||
return mutualCount > 0 ? 2 : 1; // 2 表示互相关注,1 表示单向关注
|
||||
}
|
||||
|
||||
return 0; // 未关注
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FriendVO> searchUsers(String keyword, Boolean onlyStore, String currentUserId) {
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<Member> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.and(wrapper -> wrapper
|
||||
.like(Member::getNickName, keyword)
|
||||
.or()
|
||||
.like(Member::getMobile, keyword)
|
||||
)
|
||||
.ne(Member::getId, currentUserId); // 排除自己
|
||||
|
||||
if (Boolean.TRUE.equals(onlyStore)) {
|
||||
queryWrapper.eq(Member::getHaveStore, true);
|
||||
}
|
||||
|
||||
// 限制返回数量
|
||||
queryWrapper.last("LIMIT 20");
|
||||
|
||||
// 查询用户
|
||||
List<Member> members = memberService.list(queryWrapper);
|
||||
if (members.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 获取已关注的用户ID
|
||||
List<String> memberIds = members.stream()
|
||||
.map(member -> member.getId().toString())
|
||||
.distinct() // 确保唯一
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 查询当前用户与这些用户的关注关系
|
||||
LambdaQueryWrapper<Friend> friendWrapper = new LambdaQueryWrapper<>();
|
||||
friendWrapper.eq(Friend::getUserId, currentUserId)
|
||||
.in(Friend::getFriendId, memberIds)
|
||||
.eq(Friend::getStatus, 1);
|
||||
List<Friend> friends = this.list(friendWrapper);
|
||||
|
||||
// 构建已关注用户的Map
|
||||
Map<String, Friend> friendMap = friends.stream()
|
||||
.collect(Collectors.toMap(Friend::getFriendId, friend -> friend, (existing, replacement) -> existing)); // 处理重复键
|
||||
|
||||
// 转换为VO对象
|
||||
return members.stream().map(member -> {
|
||||
FriendVO vo = new FriendVO();
|
||||
vo.setId(member.getId().toString());
|
||||
vo.setNickname(member.getNickName());
|
||||
vo.setAvatar(member.getFace());
|
||||
vo.setRegion(member.getRegion());
|
||||
vo.setMobile(member.getMobile());
|
||||
vo.setStoreFlag(member.getHaveStore() ? 1 : 0);
|
||||
|
||||
// 设置是否互相关注
|
||||
Friend friend = friendMap.get(member.getId().toString());
|
||||
if (friend != null) {
|
||||
vo.setFriendId(friend.getId());
|
||||
vo.setRemark(friend.getRemark());
|
||||
vo.setCreateTime(friend.getCreateTime());
|
||||
// 检查是否互相关注
|
||||
vo.setIsMutual(friend.getIsMutual() != null ? friend.getIsMutual() : 0);
|
||||
} else {
|
||||
vo.setIsMutual(0); // 如果没有找到对应的 Friend 记录,设置为 0
|
||||
}
|
||||
|
||||
return vo;
|
||||
}).distinct() // 确保返回的 VO 对象唯一
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,405 @@
|
||||
package cn.lili.modules.im.serviceimpl;
|
||||
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.modules.im.entity.dos.ImGroup;
|
||||
import cn.lili.modules.im.entity.dos.ImGroupMember;
|
||||
import cn.lili.modules.im.entity.vo.ImGroupMemberVO;
|
||||
import cn.lili.modules.im.entity.vo.ImGroupVO;
|
||||
import cn.lili.modules.im.mapper.ImGroupMapper;
|
||||
import cn.lili.modules.im.mapper.ImGroupMemberMapper;
|
||||
import cn.lili.modules.im.service.ImGroupService;
|
||||
import cn.lili.modules.im.service.FriendService;
|
||||
import cn.lili.modules.im.entity.vo.FriendVO;
|
||||
import cn.lili.modules.member.entity.dos.Member;
|
||||
import cn.lili.modules.member.mapper.MemberMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup> implements ImGroupService {
|
||||
|
||||
private final ImGroupMemberMapper groupMemberMapper;
|
||||
private final MemberMapper memberMapper;
|
||||
private final FriendService friendService;
|
||||
|
||||
@Override
|
||||
public List<ImGroupVO> getUserGroups(String userId) {
|
||||
// 查询用户加入的群组
|
||||
LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
|
||||
memberQuery.eq(ImGroupMember::getMemberId, userId);
|
||||
List<ImGroupMember> groupMembers = groupMemberMapper.selectList(memberQuery);
|
||||
|
||||
if (groupMembers.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 获取群组ID列表
|
||||
List<String> groupIds = groupMembers.stream()
|
||||
.map(ImGroupMember::getGroupId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 批量查询群组信息
|
||||
List<ImGroup> groups = this.listByIds(groupIds);
|
||||
|
||||
// 转换为VO对象
|
||||
return groups.stream().map(group -> {
|
||||
ImGroupVO vo = new ImGroupVO();
|
||||
BeanUtils.copyProperties(group, vo);
|
||||
|
||||
// 设置成员数量
|
||||
vo.setMemberCount(getGroupMemberCount(group.getId()));
|
||||
|
||||
// TODO: 设置最后一条消息和未读数
|
||||
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImGroupVO getGroupDetail(String groupId) {
|
||||
// 获取群组基本信息
|
||||
ImGroup group = this.getById(groupId);
|
||||
if (group == null) {
|
||||
throw new ServiceException("群组不存在");
|
||||
}
|
||||
|
||||
// 转换为VO
|
||||
ImGroupVO vo = new ImGroupVO();
|
||||
BeanUtils.copyProperties(group, vo);
|
||||
|
||||
// 设置成员数量
|
||||
vo.setMemberCount(getGroupMemberCount(groupId));
|
||||
|
||||
// TODO: 设置最后一条消息和未读数
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImGroupMemberVO> getGroupMembers(String groupId) {
|
||||
// 查询群成员
|
||||
LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
|
||||
memberQuery.eq(ImGroupMember::getGroupId, groupId)
|
||||
.orderByDesc(ImGroupMember::getRole)
|
||||
.orderByAsc(ImGroupMember::getJoinTime);
|
||||
|
||||
List<ImGroupMember> members = groupMemberMapper.selectList(memberQuery);
|
||||
|
||||
if (members.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 获取成员用户信息
|
||||
List<String> memberIds = members.stream()
|
||||
.map(ImGroupMember::getMemberId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Member> users = memberMapper.selectBatchIds(memberIds);
|
||||
Map<String, Member> userMap = users.stream()
|
||||
.collect(Collectors.toMap(member -> member.getId().toString(), member -> member));
|
||||
|
||||
// 转换为VO
|
||||
return members.stream().map(member -> {
|
||||
ImGroupMemberVO vo = new ImGroupMemberVO();
|
||||
BeanUtils.copyProperties(member, vo);
|
||||
|
||||
Member user = userMap.get(member.getMemberId());
|
||||
if (user != null) {
|
||||
vo.setNickname(user.getNickName());
|
||||
vo.setAvatar(user.getFace());
|
||||
}
|
||||
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void quitGroup(String groupId, String userId) {
|
||||
// 验证群组是否存在
|
||||
ImGroup group = this.getById(groupId);
|
||||
if (group == null) {
|
||||
throw new ServiceException("群组不存在");
|
||||
}
|
||||
|
||||
// 群主不能退群
|
||||
if (group.getOwnerId().equals(userId)) {
|
||||
throw new ServiceException("群主不能退群");
|
||||
}
|
||||
|
||||
// 删除群成员记录
|
||||
LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
|
||||
memberQuery.eq(ImGroupMember::getGroupId, groupId)
|
||||
.eq(ImGroupMember::getMemberId, userId);
|
||||
|
||||
groupMemberMapper.delete(memberQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGroupInfo(String groupId, String groupName, String notice, String avatar) {
|
||||
// 验证群组是否存在
|
||||
ImGroup group = this.getById(groupId);
|
||||
if (group == null) {
|
||||
throw new ServiceException("群组不存在");
|
||||
}
|
||||
|
||||
// 验证操作权限
|
||||
String currentUserId = UserContext.getCurrentUser().getId();
|
||||
LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
|
||||
memberQuery.eq(ImGroupMember::getGroupId, groupId)
|
||||
.eq(ImGroupMember::getMemberId, currentUserId)
|
||||
.ge(ImGroupMember::getRole, 1); // 管理员及以上权限
|
||||
|
||||
if (groupMemberMapper.selectCount(memberQuery) == 0) {
|
||||
throw new ServiceException("没有权限修改群信息");
|
||||
}
|
||||
|
||||
// 更新群信息
|
||||
ImGroup updateGroup = new ImGroup();
|
||||
updateGroup.setId(groupId);
|
||||
if (groupName != null) {
|
||||
updateGroup.setName(groupName);
|
||||
}
|
||||
if (notice != null) {
|
||||
updateGroup.setNotice(notice);
|
||||
}
|
||||
if (avatar != null) {
|
||||
updateGroup.setAvatar(avatar);
|
||||
}
|
||||
updateGroup.setUpdateTime(new Date());
|
||||
|
||||
this.updateById(updateGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImGroup createGroup(String groupName, List<String> memberIds) {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
|
||||
// 创建群组
|
||||
ImGroup group = new ImGroup();
|
||||
group.setName(groupName);
|
||||
group.setOwnerId(currentUser.getId());
|
||||
group.setCreateTime(new Date());
|
||||
group.setUpdateTime(new Date());
|
||||
|
||||
if (!this.save(group)) {
|
||||
throw new ServiceException("创建群组失败");
|
||||
}
|
||||
|
||||
// 添加群主为群成员
|
||||
addGroupMember(group.getId(), currentUser.getId(), 2); // 2表示群主
|
||||
|
||||
// 添加初始成员
|
||||
if (memberIds != null && !memberIds.isEmpty()) {
|
||||
for (String memberId : memberIds) {
|
||||
addGroupMember(group.getId(), memberId, 0); // 0表示普通成员
|
||||
}
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismissGroup(String groupId) {
|
||||
// 验证群组是否存在
|
||||
ImGroup group = this.getById(groupId);
|
||||
if (group == null) {
|
||||
throw new ServiceException("群组不存在");
|
||||
}
|
||||
|
||||
// 验证是否是群主
|
||||
String currentUserId = UserContext.getCurrentUser().getId();
|
||||
if (!group.getOwnerId().equals(currentUserId)) {
|
||||
throw new ServiceException("只有群主能解散群组");
|
||||
}
|
||||
|
||||
// 删除群成员
|
||||
LambdaQueryWrapper<ImGroupMember> memberQuery = new LambdaQueryWrapper<>();
|
||||
memberQuery.eq(ImGroupMember::getGroupId, groupId);
|
||||
groupMemberMapper.delete(memberQuery);
|
||||
|
||||
// 删除群组
|
||||
this.removeById(groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void inviteMembers(String groupId, List<String> memberIds) {
|
||||
String currentUserId = UserContext.getCurrentUser().getId();
|
||||
|
||||
// 检查权限
|
||||
ImGroupMember operator = getGroupMember(groupId, currentUserId);
|
||||
if (operator == null || operator.getRole() < 1) {
|
||||
throw new ServiceException("没有邀请权限");
|
||||
}
|
||||
|
||||
// 添加成员
|
||||
for (String memberId : memberIds) {
|
||||
if (getGroupMember(groupId, memberId) == null) {
|
||||
addGroupMember(groupId, memberId, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void setAdmin(String groupId, String memberId) {
|
||||
String currentUserId = UserContext.getCurrentUser().getId();
|
||||
|
||||
// 检查是否是群主
|
||||
ImGroupMember owner = getGroupMember(groupId, currentUserId);
|
||||
if (owner == null || owner.getRole() != 2) {
|
||||
throw new ServiceException("只有群主能设置管理员");
|
||||
}
|
||||
|
||||
// 设置管理员
|
||||
ImGroupMember member = getGroupMember(groupId, memberId);
|
||||
if (member == null) {
|
||||
throw new ServiceException("该成员不在群中");
|
||||
}
|
||||
|
||||
member.setRole(1);
|
||||
member.setUpdateTime(new Date());
|
||||
groupMemberMapper.updateById(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void removeAdmin(String groupId, String memberId) {
|
||||
String currentUserId = UserContext.getCurrentUser().getId();
|
||||
|
||||
// 检查是否是群主
|
||||
ImGroupMember owner = getGroupMember(groupId, currentUserId);
|
||||
if (owner == null || owner.getRole() != 2) {
|
||||
throw new ServiceException("只有群主能取消管理员");
|
||||
}
|
||||
|
||||
// 取消管理员
|
||||
ImGroupMember member = getGroupMember(groupId, memberId);
|
||||
if (member == null) {
|
||||
throw new ServiceException("该成员不在群中");
|
||||
}
|
||||
|
||||
member.setRole(0);
|
||||
member.setUpdateTime(new Date());
|
||||
groupMemberMapper.updateById(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void muteMember(String groupId, String memberId, Integer duration) {
|
||||
String currentUserId = UserContext.getCurrentUser().getId();
|
||||
|
||||
// 检查操作权限
|
||||
ImGroupMember operator = getGroupMember(groupId, currentUserId);
|
||||
if (operator == null || operator.getRole() < 1) {
|
||||
throw new ServiceException("没有禁言权限");
|
||||
}
|
||||
|
||||
// 检查被禁言成员
|
||||
ImGroupMember member = getGroupMember(groupId, memberId);
|
||||
if (member == null) {
|
||||
throw new ServiceException("该成员不在群中");
|
||||
}
|
||||
|
||||
// 管理员不能禁言群主和其他管理员
|
||||
if (operator.getRole() == 1 && member.getRole() > 0) {
|
||||
throw new ServiceException("没有权限禁言该成员");
|
||||
}
|
||||
|
||||
// 设置禁言
|
||||
member.setIsMuted(1);
|
||||
member.setMuteEndTime(new Date(System.currentTimeMillis() + duration * 60 * 1000));
|
||||
member.setUpdateTime(new Date());
|
||||
groupMemberMapper.updateById(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void unmuteMember(String groupId, String memberId) {
|
||||
String currentUserId = UserContext.getCurrentUser().getId();
|
||||
|
||||
// 检查操作权限
|
||||
ImGroupMember operator = getGroupMember(groupId, currentUserId);
|
||||
if (operator == null || operator.getRole() < 1) {
|
||||
throw new ServiceException("没有解除禁言权限");
|
||||
}
|
||||
|
||||
// 检查被解除禁言成员
|
||||
ImGroupMember member = getGroupMember(groupId, memberId);
|
||||
if (member == null) {
|
||||
throw new ServiceException("该成员不在群中");
|
||||
}
|
||||
|
||||
// 管理员不能解除群主和其他管理员的禁言
|
||||
if (operator.getRole() == 1 && member.getRole() > 0) {
|
||||
throw new ServiceException("没有权限解除该成员的禁言");
|
||||
}
|
||||
|
||||
// 解除禁言
|
||||
member.setIsMuted(0);
|
||||
member.setMuteEndTime(null);
|
||||
member.setUpdateTime(new Date());
|
||||
groupMemberMapper.updateById(member);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群成员数量
|
||||
*/
|
||||
private Integer getGroupMemberCount(String groupId) {
|
||||
LambdaQueryWrapper<ImGroupMember> countQuery = new LambdaQueryWrapper<>();
|
||||
countQuery.eq(ImGroupMember::getGroupId, groupId);
|
||||
return Math.toIntExact(groupMemberMapper.selectCount(countQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加群成员
|
||||
*/
|
||||
private void addGroupMember(String groupId, String memberId, Integer role) {
|
||||
ImGroupMember member = new ImGroupMember();
|
||||
member.setGroupId(groupId);
|
||||
member.setMemberId(memberId);
|
||||
member.setRole(role);
|
||||
member.setJoinTime(new Date());
|
||||
member.setIsMuted(0);
|
||||
|
||||
groupMemberMapper.insert(member);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群成员信息
|
||||
*/
|
||||
private ImGroupMember getGroupMember(String groupId, String memberId) {
|
||||
LambdaQueryWrapper<ImGroupMember> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(ImGroupMember::getGroupId, groupId)
|
||||
.eq(ImGroupMember::getMemberId, memberId);
|
||||
return groupMemberMapper.selectOne(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否是好友关系
|
||||
*/
|
||||
private boolean isFriend(String userId, String friendId) {
|
||||
List<FriendVO> friends = friendService.getMutualFriends(userId);
|
||||
return friends.stream()
|
||||
.anyMatch(friend -> friend.getFriendId().equals(friendId));
|
||||
}
|
||||
}
|
||||
@ -6,10 +6,12 @@ import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.modules.im.entity.dos.ImMessage;
|
||||
import cn.lili.modules.im.entity.dos.ImTalk;
|
||||
import cn.lili.modules.im.entity.dto.MessageQueryParams;
|
||||
import cn.lili.modules.im.mapper.ImMessageMapper;
|
||||
import cn.lili.modules.im.service.ImMessageService;
|
||||
import cn.lili.modules.im.service.ImTalkService;
|
||||
import cn.lili.modules.im.websocket.WebSocketServer;
|
||||
import cn.lili.mybatis.util.PageUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
@ -19,9 +21,14 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import cn.lili.modules.im.entity.vo.ImMessageVO;
|
||||
import cn.lili.modules.im.entity.dos.ImGroup;
|
||||
import cn.lili.modules.im.mapper.ImGroupMapper;
|
||||
import cn.lili.modules.im.entity.vo.MessageOperation;
|
||||
import cn.lili.common.vo.ResultPageVO;
|
||||
|
||||
/**
|
||||
* Im消息 业务实现
|
||||
@ -33,8 +40,26 @@ import java.util.Objects;
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage> implements ImMessageService {
|
||||
|
||||
// @Autowired
|
||||
// private final WebSocketServer imTalkService;
|
||||
|
||||
@Autowired
|
||||
private ImTalkService imTalkService;
|
||||
private ImGroupMapper imGroupMapper;
|
||||
|
||||
@Override
|
||||
public List<ImMessageVO> getHistoryMessages(String talkId, String earliestMsgId, Integer pageSize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImMessageVO> getRecentMessages(String talkId, String latestMsgId, Integer pageSize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImMessageVO sendMessage(String toId, String content, String type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(String talkId, String accessToken) {
|
||||
@ -43,6 +68,7 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
|
||||
updateWrapper.eq(ImMessage::getTalkId, talkId);
|
||||
updateWrapper.eq(ImMessage::getToUser, userId);
|
||||
updateWrapper.set(ImMessage::getIsRead, true);
|
||||
// updateWrapper.set(ImMessage::getReadTime,new Date());
|
||||
this.update(updateWrapper);
|
||||
}
|
||||
|
||||
@ -51,7 +77,7 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
|
||||
String userId = UserContext.getAuthUser(accessToken).getId();
|
||||
LambdaQueryWrapper<ImMessage> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(ImMessage::getToUser, userId);
|
||||
queryWrapper.eq(ImMessage::getIsRead, false);
|
||||
// queryWrapper.eq(ImMessage::getIsRead, false);
|
||||
return this.list(queryWrapper);
|
||||
}
|
||||
|
||||
@ -77,12 +103,23 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
|
||||
|
||||
@Override
|
||||
public List<ImMessage> getList(MessageQueryParams messageQueryParams) {
|
||||
List<ImMessage> messageList = this.page(PageUtil.initPage(messageQueryParams), messageQueryParams.initQueryWrapper()).getRecords();
|
||||
// 使用 LambdaQueryWrapper 创建查询条件
|
||||
LambdaQueryWrapper<ImMessage> queryWrapper = messageQueryParams.initQueryWrapper();
|
||||
|
||||
// 关联查询,假设 ImTalkMapper 是你的 Mapper 类
|
||||
queryWrapper.eq(ImMessage::getTalkId, messageQueryParams.getTalkId());
|
||||
|
||||
// 执行分页查询
|
||||
List<ImMessage> messageList = this.page(PageUtil.initPage(messageQueryParams), queryWrapper).getRecords();
|
||||
|
||||
// 对消息列表进行排序和已读处理
|
||||
ListSort(messageList);
|
||||
readMessage(messageList);
|
||||
|
||||
return messageList;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Long unreadMessageCount() {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
@ -101,6 +138,63 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
|
||||
this.update(new LambdaUpdateWrapper<ImMessage>().eq(ImMessage::getToUser,currentUser.getId()).set(ImMessage::getIsRead,true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultPageVO<ImMessageVO> getGroupMessages(String groupId, Integer pageNumber, Integer pageSize) {
|
||||
List<ImMessage> messages = this.getMessagesByGroupId(groupId, pageNumber, pageSize);
|
||||
|
||||
List<ImMessageVO> messageVOs = messages.stream()
|
||||
.map(message -> new ImMessageVO())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Long total = this.count(new LambdaQueryWrapper<ImMessage>().eq(ImMessage::getGroupId, groupId));
|
||||
|
||||
return new ResultPageVO<>(total, (long) Math.ceil((double) total / pageSize), (long) pageNumber, (long) pageSize, messageVOs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImMessageVO sendGroupMessage(String groupId, String content, String type) {
|
||||
ImMessage imMessage = new ImMessage();
|
||||
imMessage.setText(content);
|
||||
imMessage.setType(type);
|
||||
imMessage.setGroupId(groupId);
|
||||
imMessage.setCreateTime(new Date());
|
||||
|
||||
this.save(imMessage);
|
||||
|
||||
return new ImMessageVO();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recallMessage(String messageId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImMessageVO> loadHistoryMessages(String talkId, String earliestMsgId, Integer size) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImMessageVO> loadLatestMessages(String talkId, Integer size) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImMessageVO> getNewerMessages(String talkId, String latestMsgId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMessages(List<ImMessage> messages, String userId) {
|
||||
|
||||
}
|
||||
|
||||
private List<ImMessage> getMessagesByGroupId(String groupId, Integer pageNumber, Integer pageSize) {
|
||||
return this.list(new LambdaQueryWrapper<ImMessage>().eq(ImMessage::getGroupId, groupId)
|
||||
.orderByDesc(ImMessage::getCreateTime)
|
||||
.last("LIMIT " + (pageNumber - 1) * pageSize + ", " + pageSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间倒叙
|
||||
*
|
||||
@ -144,6 +238,7 @@ public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage
|
||||
for (ImMessage imMessage : messageList) {
|
||||
if(Boolean.FALSE.equals(imMessage.getIsRead()) && imMessage.getToUser().equals(toUserId)){
|
||||
imMessage.setIsRead(true);
|
||||
// imMessage.setReadTime(new Date());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,10 +6,12 @@ import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.modules.im.entity.dos.Friend;
|
||||
import cn.lili.modules.im.entity.dos.ImMessage;
|
||||
import cn.lili.modules.im.entity.dos.ImTalk;
|
||||
import cn.lili.modules.im.entity.dto.IMTalkQueryParams;
|
||||
import cn.lili.modules.im.entity.vo.ImTalkVO;
|
||||
import cn.lili.modules.im.mapper.FriendMapper;
|
||||
import cn.lili.modules.im.mapper.ImTalkMapper;
|
||||
import cn.lili.modules.im.service.ImMessageService;
|
||||
import cn.lili.modules.im.service.ImTalkService;
|
||||
@ -20,33 +22,43 @@ import cn.lili.modules.store.service.StoreService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 聊天 业务实现
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
//@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class ImTalkServiceImpl extends ServiceImpl<ImTalkMapper, ImTalk> implements ImTalkService {
|
||||
|
||||
private final ImMessageService imMessageService;
|
||||
@Autowired
|
||||
public ImTalkServiceImpl(@Lazy ImMessageService imMessageService) {
|
||||
this.imMessageService = imMessageService;
|
||||
}
|
||||
@Autowired
|
||||
FriendMapper friendMapper;
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
|
||||
@Autowired
|
||||
private StoreService storeService;
|
||||
|
||||
@Autowired
|
||||
private ImMessageService imMessageService;
|
||||
|
||||
public ImTalk getTalkByUser(String userId) {
|
||||
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
|
||||
AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser());
|
||||
@ -180,14 +192,52 @@ public class ImTalkServiceImpl extends ServiceImpl<ImTalkMapper, ImTalk> impleme
|
||||
if (authUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.and(wq -> wq.eq(ImTalk::getUserId1, authUser.getId()).or().eq(ImTalk::getUserId2, authUser.getId()));
|
||||
|
||||
if (CharSequenceUtil.isNotEmpty(imTalkQueryParams.getUserName())) {
|
||||
queryWrapper.and(wq -> wq.ne(ImTalk::getUserId1, authUser.getId()).like(ImTalk::getName1, imTalkQueryParams.getUserName()).or().ne(ImTalk::getUserId2, authUser.getId()).like(ImTalk::getName2, imTalkQueryParams.getUserName()));
|
||||
queryWrapper.and(wq -> wq.ne(ImTalk::getUserId1, authUser.getId())
|
||||
.like(ImTalk::getName1, imTalkQueryParams.getUserName())
|
||||
.or()
|
||||
.ne(ImTalk::getUserId2, authUser.getId())
|
||||
.like(ImTalk::getName2, imTalkQueryParams.getUserName()));
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc(ImTalk::getLastTalkTime);
|
||||
List<ImTalk> imTalks = this.list(queryWrapper);
|
||||
List<ImTalkVO> imTalkVOList = imTalks.stream().map(imTalk -> new ImTalkVO(imTalk, authUser.getId())).collect(Collectors.toList());
|
||||
|
||||
// 获取好友关系
|
||||
List<String> friendIds = imTalks.stream()
|
||||
.flatMap(imTalk -> Stream.of(imTalk.getUserId1(), imTalk.getUserId2()))
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 查询好友关系
|
||||
List<Friend> friends = friendMapper.selectList(new LambdaQueryWrapper<Friend>()
|
||||
.in(Friend::getFriendId, friendIds)
|
||||
.eq(Friend::getUserId, authUser.getId())
|
||||
.eq(Friend::getStatus, 1)); // 只查询状态为1的记录
|
||||
|
||||
// 创建一个 Set 来存储已关注的用户 ID
|
||||
Set<String> mutualFriends = friends.stream()
|
||||
.map(Friend::getFriendId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 更新 isMutual 字段
|
||||
for (ImTalk imTalk : imTalks) {
|
||||
if (mutualFriends.contains(imTalk.getUserId1()) || mutualFriends.contains(imTalk.getUserId2())) {
|
||||
imTalk.setIsMutual(1); // 设置为单向关注
|
||||
} else {
|
||||
imTalk.setIsMutual(0); // 设置为未关注
|
||||
}
|
||||
}
|
||||
|
||||
// 将更新后的 imTalk 转换为 VO 对象
|
||||
List<ImTalkVO> imTalkVOList = imTalks.stream()
|
||||
.map(imTalk -> new ImTalkVO(imTalk, authUser.getId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
getUnread(imTalkVOList);
|
||||
return imTalkVOList;
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
package cn.lili.controller.im;
|
||||
package cn.lili.modules.im.websocket;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.lili.cache.Cache;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
@ -13,8 +14,11 @@ import cn.lili.modules.im.entity.vo.MessageOperation;
|
||||
import cn.lili.modules.im.entity.vo.MessageVO;
|
||||
import cn.lili.modules.im.service.ImMessageService;
|
||||
import cn.lili.modules.im.service.ImTalkService;
|
||||
import cn.lili.modules.member.entity.dos.Member;
|
||||
import cn.lili.modules.member.service.MemberService;
|
||||
import com.alibaba.druid.util.StringUtils;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -40,6 +44,7 @@ import org.springframework.stereotype.Component;
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class WebSocketServer {
|
||||
private final MemberService memberService;
|
||||
/**
|
||||
* 在线人数 PS 注意,只能单节点,如果多节点部署需要自行寻找方案
|
||||
*/
|
||||
@ -107,21 +112,36 @@ public class WebSocketServer {
|
||||
* @param messageOperation
|
||||
*/
|
||||
private void operation(String accessToken, MessageOperation messageOperation) {
|
||||
|
||||
AuthUser authUser = UserContext.getAuthUser(accessToken);
|
||||
|
||||
switch (messageOperation.getOperationType()) {
|
||||
case PING:
|
||||
break;
|
||||
case MESSAGE:
|
||||
//保存消息
|
||||
// 获取或创建聊天记录
|
||||
ImTalk imTalk = getOrCreateTalk(authUser.getId(), messageOperation.getTo());
|
||||
|
||||
// 如果 imTalk 为 null,说明创建失败,记录日志并返回
|
||||
if (imTalk == null) {
|
||||
log.warn("Failed to create ImTalk for users: {} and {}", authUser.getId(), messageOperation.getTo());
|
||||
return; // 或者抛出异常
|
||||
}
|
||||
|
||||
// 保存消息
|
||||
ImMessage imMessage = new ImMessage(messageOperation);
|
||||
imMessage.setCreateBy(authUser.getNickName());
|
||||
imMessage.setTalkId(imTalk.getId()); // 设置 talk_id 为 imTalk 的 id
|
||||
imMessageService.save(imMessage);
|
||||
//修改最后消息信息
|
||||
imTalkService.update(new LambdaUpdateWrapper<ImTalk>().eq(ImTalk::getId, messageOperation.getTalkId())
|
||||
log.info("Message saved: {}", imMessage);
|
||||
|
||||
// 更新最后消息信息
|
||||
imTalkService.update(new LambdaUpdateWrapper<ImTalk>()
|
||||
.eq(ImTalk::getId, imTalk.getId())
|
||||
.set(ImTalk::getLastTalkMessage, messageOperation.getContext())
|
||||
.set(ImTalk::getLastTalkTime, imMessage.getCreateTime())
|
||||
.set(ImTalk::getLastMessageType, imMessage.getMessageType()));
|
||||
//发送消息
|
||||
|
||||
// 发送消息
|
||||
sendMessage(messageOperation.getTo(), new MessageVO(MessageResultType.MESSAGE, imMessage));
|
||||
break;
|
||||
case READ:
|
||||
@ -141,7 +161,45 @@ public class WebSocketServer {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 获取或创建聊天记录
|
||||
public ImTalk getOrCreateTalk(String userId1, String userId2) {
|
||||
// 查询现有的聊天记录
|
||||
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper
|
||||
.or().eq(ImTalk::getUserId1, userId1).eq(ImTalk::getUserId2, userId2)
|
||||
.or().eq(ImTalk::getUserId1, userId2).eq(ImTalk::getUserId2, userId1);
|
||||
|
||||
ImTalk imTalk = imTalkService.getOne(queryWrapper);
|
||||
if (imTalk != null) {
|
||||
log.info("Found existing talk: {}", imTalk);
|
||||
return imTalk;
|
||||
}
|
||||
|
||||
// 如果没有找到,创建新的聊天记录
|
||||
log.info("Creating new talk between {} and {}", userId1, userId2);
|
||||
|
||||
// 获取用户信息
|
||||
Member member1 = memberService.getById(userId1);
|
||||
Member member2 = memberService.getById(userId2);
|
||||
|
||||
// 检查用户是否存在
|
||||
if (member1 == null || member2 == null) {
|
||||
log.warn("One of the members does not exist: member1={}, member2={}", member1, member2);
|
||||
throw new ServiceException("One of the users does not exist."); // 或者根据需要处理
|
||||
}
|
||||
|
||||
String face1 = member1.getFace() != null ? member1.getFace() : ""; // 获取头像
|
||||
String face2 = member2.getFace() != null ? member2.getFace() : ""; // 获取头像
|
||||
String name1 = member1.getNickName() != null ? member1.getNickName() : ""; // 获取昵称
|
||||
String name2 = member2.getNickName() != null ? member2.getNickName() : ""; // 获取昵称
|
||||
|
||||
// 创建新的聊天记录
|
||||
imTalk = new ImTalk(userId1, userId2, face1, face2, name1, name2);
|
||||
imTalkService.save(imTalk);
|
||||
log.info("New talk created: {}", imTalk);
|
||||
|
||||
return imTalk;
|
||||
}
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
@ -6,6 +6,8 @@ import cn.lili.common.security.sensitive.enums.SensitiveStrategy;
|
||||
import cn.lili.common.utils.CommonUtil;
|
||||
import cn.lili.mybatis.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
@ -31,6 +33,24 @@ public class Member extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private String id;
|
||||
|
||||
// 创建者
|
||||
private String createBy;
|
||||
|
||||
// 创建时间
|
||||
private Date createTime;
|
||||
|
||||
// 删除标志 true/false
|
||||
private Boolean deleteFlag;
|
||||
|
||||
// 更新者
|
||||
private String updateBy;
|
||||
|
||||
// 更新时间
|
||||
private Date updateTime;
|
||||
|
||||
@ApiModelProperty(value = "会员用户名")
|
||||
private String username;
|
||||
|
||||
@ -97,13 +117,13 @@ public class Member extends BaseEntity {
|
||||
@ApiModelProperty(value = "经验值数量")
|
||||
private Long experience;
|
||||
|
||||
|
||||
public Member(String username, String password, String mobile) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.mobile = mobile;
|
||||
this.nickName = CommonUtil.getSpecialStr("用户");
|
||||
this.disabled = true;
|
||||
this.deleteFlag=false;
|
||||
this.haveStore = false;
|
||||
this.sex = 0;
|
||||
this.point = 0L;
|
||||
@ -115,6 +135,7 @@ public class Member extends BaseEntity {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.mobile = mobile;
|
||||
this.deleteFlag=false;
|
||||
this.nickName = nickName;
|
||||
this.disabled = true;
|
||||
this.haveStore = false;
|
||||
|
||||
@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -17,7 +18,9 @@ import java.util.List;
|
||||
*
|
||||
* @author Bulbasaur
|
||||
* @since 2020-02-25 14:10:16
|
||||
* @
|
||||
*/
|
||||
@Mapper
|
||||
public interface MemberMapper extends BaseMapper<Member> {
|
||||
|
||||
/**
|
||||
|
||||
@ -118,7 +118,11 @@ public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> impleme
|
||||
public Member getUserInfo() {
|
||||
AuthUser tokenUser = UserContext.getCurrentUser();
|
||||
if (tokenUser != null) {
|
||||
return this.findByUsername(tokenUser.getUsername());
|
||||
Member member = this.findByUsername(tokenUser.getUsername());
|
||||
if(member != null && !member.getDisabled()){
|
||||
throw new ServiceException(ResultCode.USER_STATUS_ERROR);
|
||||
}
|
||||
return member;
|
||||
}
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
|
||||
@ -313,16 +313,34 @@ public class PayKit {
|
||||
* v3 接口创建签名
|
||||
*
|
||||
* @param signMessage 待签名的参数
|
||||
* @param keyPath key.pem 证书路径
|
||||
* @param key key.pem 证书
|
||||
* @return 生成 v3 签名
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String createSign(String signMessage, String keyPath) throws Exception {
|
||||
public static String createSign(String signMessage, String key) throws Exception {
|
||||
if (StrUtil.isEmpty(signMessage)) {
|
||||
return null;
|
||||
}
|
||||
//获取商户私钥
|
||||
PrivateKey privateKey = PayKit.getPrivateKey(keyPath);
|
||||
PrivateKey privateKey = PayKit.getPrivateKey(key);
|
||||
//生成签名
|
||||
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* v3 接口创建签名
|
||||
*
|
||||
* @param signMessage 待签名的参数
|
||||
* @param key key.pem 证书
|
||||
* @return 生成 v3 签名
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String createPublicSign(String signMessage, String key) throws Exception {
|
||||
if (StrUtil.isEmpty(signMessage)) {
|
||||
return null;
|
||||
}
|
||||
//获取商户私钥
|
||||
PrivateKey privateKey = PayKit.getPublicKey(key);
|
||||
//生成签名
|
||||
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
|
||||
}
|
||||
@ -378,13 +396,13 @@ public class PayKit {
|
||||
/**
|
||||
* 获取商户私钥
|
||||
*
|
||||
* @param keyPath 商户私钥证书路径
|
||||
* @param key 商户私钥证书
|
||||
* @return {@link PrivateKey} 商户私钥
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static PrivateKey getPrivateKey(String keyPath) throws Exception {
|
||||
String originalKey = FileUtil.readUtf8String(keyPath);
|
||||
String privateKey = originalKey
|
||||
public static PrivateKey getPrivateKey(String key) throws Exception {
|
||||
// String originalKey = FileUtil.readUtf8String(keyPath);
|
||||
String privateKey = key
|
||||
.replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
.replaceAll("\\s+", "");
|
||||
@ -392,6 +410,23 @@ public class PayKit {
|
||||
return RsaKit.loadPrivateKey(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商户公钥
|
||||
*
|
||||
* @param key 商户私钥证书
|
||||
* @return {@link PrivateKey} 商户私钥
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static PrivateKey getPublicKey(String key) throws Exception {
|
||||
// String originalKey = FileUtil.readUtf8String(keyPath);
|
||||
String privateKey = key
|
||||
.replace("-----BEGIN PUBLIC KEY-----", "")
|
||||
.replace("-----END PUBLIC KEY-----", "")
|
||||
.replaceAll("\\s+", "");
|
||||
|
||||
return RsaKit.loadPrivateKey(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取证书
|
||||
*
|
||||
|
||||
@ -312,13 +312,18 @@ public class RsaKit {
|
||||
*/
|
||||
public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
|
||||
try {
|
||||
|
||||
|
||||
byte[] buffer = Base64.decode(privateKeyStr);
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
return keyFactory.generatePrivate(keySpec);
|
||||
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new Exception("无此算法");
|
||||
} catch (InvalidKeySpecException e) {
|
||||
e.printStackTrace();
|
||||
throw new Exception("私钥非法");
|
||||
} catch (NullPointerException e) {
|
||||
throw new Exception("私钥数据为空");
|
||||
|
||||
@ -449,7 +449,8 @@ public class WxPayKit {
|
||||
* @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param keyPath key.pem 证书路径
|
||||
* @param key key.pem 证书
|
||||
* @param publicKey 公钥证书
|
||||
* @param body 接口请求参数
|
||||
* @param nonceStr 随机字符库
|
||||
* @param timestamp 时间戳
|
||||
@ -458,11 +459,12 @@ public class WxPayKit {
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
|
||||
String serialNo, String keyPath, String body, String nonceStr,
|
||||
String serialNo, String key, String publicKey ,String body, String nonceStr,
|
||||
long timestamp, String authType) throws Exception {
|
||||
//构建签名参数
|
||||
String buildSignMessage = PayKit.buildSignMessage(method, urlSuffix, timestamp, nonceStr, body);
|
||||
String signature = PayKit.createSign(buildSignMessage, keyPath);
|
||||
String publicKeySignature = PayKit.createPublicSign(buildSignMessage, publicKey);
|
||||
String signature = PayKit.createSign(publicKeySignature, key);
|
||||
//根据平台规则生成请求头 authorization
|
||||
return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType);
|
||||
}
|
||||
@ -492,27 +494,27 @@ public class WxPayKit {
|
||||
return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 v3 接口所需的 Authorization
|
||||
*
|
||||
* @param method {@link RequestMethodEnums} 请求方法
|
||||
* @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接
|
||||
* @param mchId 商户Id
|
||||
* @param serialNo 商户 API 证书序列号
|
||||
* @param keyPath key.pem 证书路径
|
||||
* @param body 接口请求参数
|
||||
* @return {@link String} 返回 v3 所需的 Authorization
|
||||
* @throws Exception 异常信息
|
||||
*/
|
||||
public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
|
||||
String serialNo, String keyPath, String body) throws Exception {
|
||||
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
String nonceStr = IdUtil.fastSimpleUUID();
|
||||
|
||||
return buildAuthorization(method, urlSuffix, mchId, serialNo, keyPath, body, nonceStr, timestamp, authType);
|
||||
}
|
||||
// /**
|
||||
// * 构建 v3 接口所需的 Authorization
|
||||
// *
|
||||
// * @param method {@link RequestMethodEnums} 请求方法
|
||||
// * @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接
|
||||
// * @param mchId 商户Id
|
||||
// * @param serialNo 商户 API 证书序列号
|
||||
// * @param keyPath key.pem 证书路径
|
||||
// * @param body 接口请求参数
|
||||
// * @return {@link String} 返回 v3 所需的 Authorization
|
||||
// * @throws Exception 异常信息
|
||||
// */
|
||||
// public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
|
||||
// String serialNo, String keyPath, String body) throws Exception {
|
||||
//
|
||||
// long timestamp = System.currentTimeMillis() / 1000;
|
||||
// String authType = "WECHATPAY2-SHA256-RSA2048";
|
||||
// String nonceStr = IdUtil.fastSimpleUUID();
|
||||
//
|
||||
// return buildAuthorization(method, urlSuffix, mchId, serialNo, keyPath, body, nonceStr, timestamp, authType);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 构建 v3 接口所需的 Authorization
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,15 +2,11 @@ package cn.lili.modules.payment.kit.plugin.wechat;
|
||||
|
||||
import cn.hutool.core.net.URLDecoder;
|
||||
import cn.hutool.core.net.URLEncoder;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.lili.cache.Cache;
|
||||
import cn.lili.cache.CachePrefix;
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.enums.ResultUtil;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.properties.ApiProperties;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.utils.CurrencyUtil;
|
||||
import cn.lili.common.utils.SnowFlake;
|
||||
@ -26,17 +22,16 @@ import cn.lili.modules.payment.entity.RefundLog;
|
||||
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
|
||||
import cn.lili.modules.payment.kit.CashierSupport;
|
||||
import cn.lili.modules.payment.kit.Payment;
|
||||
import cn.lili.modules.payment.kit.core.PaymentHttpResponse;
|
||||
import cn.lili.modules.payment.kit.core.enums.RequestMethodEnums;
|
||||
import cn.lili.modules.payment.kit.core.enums.SignType;
|
||||
import cn.lili.modules.payment.kit.core.kit.*;
|
||||
import cn.lili.modules.payment.kit.core.kit.HttpKit;
|
||||
import cn.lili.modules.payment.kit.core.kit.IpKit;
|
||||
import cn.lili.modules.payment.kit.core.kit.WxPayKit;
|
||||
import cn.lili.modules.payment.kit.core.utils.DateTimeZoneUtil;
|
||||
import cn.lili.modules.payment.kit.dto.PayParam;
|
||||
import cn.lili.modules.payment.kit.dto.PaymentSuccessParams;
|
||||
import cn.lili.modules.payment.kit.params.dto.CashierParam;
|
||||
import cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApiEnum;
|
||||
import cn.lili.modules.payment.kit.plugin.wechat.enums.WechatDomain;
|
||||
import cn.lili.modules.payment.kit.plugin.wechat.model.*;
|
||||
import cn.lili.modules.payment.kit.plugin.wechat.model.H5Info;
|
||||
import cn.lili.modules.payment.kit.plugin.wechat.model.SceneInfo;
|
||||
import cn.lili.modules.payment.service.PaymentService;
|
||||
import cn.lili.modules.payment.service.RefundLogService;
|
||||
import cn.lili.modules.system.entity.dos.Setting;
|
||||
@ -51,6 +46,29 @@ import cn.lili.modules.wallet.entity.dto.TransferResultDTO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.google.gson.Gson;
|
||||
import com.wechat.pay.java.core.Config;
|
||||
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||
import com.wechat.pay.java.core.RSAPublicKeyConfig;
|
||||
import com.wechat.pay.java.core.exception.ValidationException;
|
||||
import com.wechat.pay.java.core.notification.NotificationConfig;
|
||||
import com.wechat.pay.java.core.notification.NotificationParser;
|
||||
import com.wechat.pay.java.core.notification.RequestParam;
|
||||
import com.wechat.pay.java.service.payments.app.AppService;
|
||||
import com.wechat.pay.java.service.payments.h5.H5Service;
|
||||
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
|
||||
import com.wechat.pay.java.service.payments.model.Transaction;
|
||||
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
|
||||
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
|
||||
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
|
||||
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
|
||||
import com.wechat.pay.java.service.refund.RefundService;
|
||||
import com.wechat.pay.java.service.refund.model.AmountReq;
|
||||
import com.wechat.pay.java.service.refund.model.CreateRequest;
|
||||
import com.wechat.pay.java.service.refund.model.Refund;
|
||||
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
|
||||
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
|
||||
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
|
||||
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -58,12 +76,9 @@ import org.springframework.stereotype.Component;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 微信支付
|
||||
@ -141,31 +156,34 @@ public class WechatPlugin implements Payment {
|
||||
if (appid == null) {
|
||||
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
}
|
||||
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
.setAppid(appid)
|
||||
.setMchid(setting.getMchId())
|
||||
.setDescription(cashierParam.getDetail())
|
||||
.setOut_trade_no(outOrderNo)
|
||||
.setTime_expire(timeExpire)
|
||||
.setAttach(attach)
|
||||
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(),PaymentMethodEnum.WECHAT))
|
||||
.setAmount(new Amount().setTotal(fen)).setScene_info(sceneInfo);
|
||||
|
||||
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
PaymentHttpResponse response = WechatApi.v3(
|
||||
RequestMethodEnums.POST,
|
||||
WechatDomain.CHINA.toString(),
|
||||
WechatApiEnum.H5_PAY.toString(),
|
||||
setting.getMchId(),
|
||||
setting.getSerialNumber(),
|
||||
null,
|
||||
setting.getApiclient_key(),
|
||||
JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
);
|
||||
Config config =null;
|
||||
if(setting.getPublicType().equals("CERT")){
|
||||
config=this.getCertificateConfig(setting);
|
||||
}else {
|
||||
config=this.getPublicKeyConfig(setting);
|
||||
}
|
||||
|
||||
// 构建service
|
||||
H5Service service = new H5Service.Builder().config(config).build();
|
||||
|
||||
com.wechat.pay.java.service.payments.h5.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.h5.model.PrepayRequest();
|
||||
com.wechat.pay.java.service.payments.h5.model.Amount amount = new com.wechat.pay.java.service.payments.h5.model.Amount();
|
||||
amount.setTotal(fen);
|
||||
prepayRequest.setAmount(amount);
|
||||
prepayRequest.setAppid(appid);
|
||||
prepayRequest.setMchid(setting.getMchId());
|
||||
prepayRequest.setDescription(cashierParam.getDetail());
|
||||
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
|
||||
prepayRequest.setAttach(attach);
|
||||
prepayRequest.setTimeExpire(timeExpire);
|
||||
prepayRequest.setOutTradeNo(outOrderNo);
|
||||
|
||||
// 调用下单方法,得到应答
|
||||
com.wechat.pay.java.service.payments.h5.model.PrepayResponse response = service.prepay(prepayRequest);
|
||||
updateOrderPayNo(payParam,outOrderNo);
|
||||
|
||||
return ResultUtil.data(JSONUtil.toJsonStr(response.getBody()));
|
||||
return ResultUtil.data(response.getH5Url());
|
||||
} catch (Exception e) {
|
||||
log.error("微信H5支付错误", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
@ -183,8 +201,7 @@ public class WechatPlugin implements Payment {
|
||||
return null;
|
||||
}
|
||||
|
||||
Payer payer = new Payer();
|
||||
payer.setOpenid(connect.getUnionId());
|
||||
|
||||
|
||||
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
|
||||
@ -202,46 +219,38 @@ public class WechatPlugin implements Payment {
|
||||
if (appid == null) {
|
||||
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
}
|
||||
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
.setAppid(appid)
|
||||
.setMchid(setting.getMchId())
|
||||
.setDescription(cashierParam.getDetail())
|
||||
.setOut_trade_no(outOrderNo)
|
||||
.setTime_expire(timeExpire)
|
||||
.setAttach(attach)
|
||||
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
|
||||
.setAmount(new Amount().setTotal(fen))
|
||||
.setPayer(payer);
|
||||
|
||||
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
PaymentHttpResponse response = WechatApi.v3(
|
||||
RequestMethodEnums.POST,
|
||||
WechatDomain.CHINA.toString(),
|
||||
WechatApiEnum.JS_API_PAY.toString(),
|
||||
setting.getMchId(),
|
||||
setting.getSerialNumber(),
|
||||
null,
|
||||
setting.getApiclient_key(),
|
||||
JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
);
|
||||
//根据证书序列号查询对应的证书来验证签名结果
|
||||
boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
log.info("verifySignature: {}", verifySignature);
|
||||
log.info("统一下单响应 {}", response);
|
||||
Config config =null;
|
||||
if(setting.getPublicType().equals("CERT")){
|
||||
config=this.getCertificateConfig(setting);
|
||||
}else {
|
||||
config=this.getPublicKeyConfig(setting);
|
||||
}
|
||||
// 构建service
|
||||
JsapiService service = new JsapiService.Builder().config(config).build();
|
||||
|
||||
if (verifySignature) {
|
||||
String body = response.getBody();
|
||||
JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
String prepayId = jsonObject.getStr("prepay_id");
|
||||
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
|
||||
log.info("唤起支付参数:{}", map);
|
||||
com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
|
||||
com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
|
||||
|
||||
com.wechat.pay.java.service.payments.jsapi.model.Payer payer = new com.wechat.pay.java.service.payments.jsapi.model.Payer();
|
||||
payer.setOpenid(connect.getUnionId());
|
||||
amount.setTotal(fen);
|
||||
prepayRequest.setAmount(amount);
|
||||
prepayRequest.setAppid(appid);
|
||||
prepayRequest.setMchid(setting.getMchId());
|
||||
prepayRequest.setDescription(cashierParam.getDetail());
|
||||
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
|
||||
prepayRequest.setAttach(attach);
|
||||
prepayRequest.setTimeExpire(timeExpire);
|
||||
prepayRequest.setOutTradeNo(outOrderNo);
|
||||
prepayRequest.setPayer(payer);
|
||||
// 调用下单方法,得到应答
|
||||
com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse response = service.prepay(prepayRequest);
|
||||
updateOrderPayNo(payParam,outOrderNo);
|
||||
|
||||
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, response.getPrepayId(), setting.getApiclientKey());
|
||||
log.info("唤起支付参数:{}", map);
|
||||
return ResultUtil.data(map);
|
||||
}
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
@ -269,48 +278,39 @@ public class WechatPlugin implements Payment {
|
||||
if (appid == null) {
|
||||
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
}
|
||||
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
.setAppid(appid)
|
||||
.setMchid(setting.getMchId())
|
||||
.setDescription(cashierParam.getDetail())
|
||||
.setOut_trade_no(outOrderNo)
|
||||
.setTime_expire(timeExpire)
|
||||
.setAttach(attach)
|
||||
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
|
||||
.setAmount(new Amount().setTotal(fen));
|
||||
|
||||
Config config =null;
|
||||
if(setting.getPublicType().equals("CERT")){
|
||||
config=this.getCertificateConfig(setting);
|
||||
}else {
|
||||
config=this.getPublicKeyConfig(setting);
|
||||
}
|
||||
// 构建service
|
||||
AppService service = new AppService.Builder().config(config).build();
|
||||
|
||||
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
PaymentHttpResponse response = WechatApi.v3(
|
||||
RequestMethodEnums.POST,
|
||||
WechatDomain.CHINA.toString(),
|
||||
WechatApiEnum.APP_PAY.toString(),
|
||||
setting.getMchId(),
|
||||
setting.getSerialNumber(),
|
||||
null,
|
||||
setting.getApiclient_key(),
|
||||
JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
);
|
||||
//根据证书序列号查询对应的证书来验证签名结果
|
||||
boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
log.info("verifySignature: {}", verifySignature);
|
||||
log.info("统一下单响应 {}", response);
|
||||
com.wechat.pay.java.service.payments.app.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
|
||||
com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
|
||||
amount.setTotal(fen);
|
||||
prepayRequest.setAmount(amount);
|
||||
prepayRequest.setAppid(appid);
|
||||
prepayRequest.setMchid(setting.getMchId());
|
||||
prepayRequest.setDescription(cashierParam.getDetail());
|
||||
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
|
||||
prepayRequest.setAttach(attach);
|
||||
prepayRequest.setTimeExpire(timeExpire);
|
||||
prepayRequest.setOutTradeNo(outOrderNo);
|
||||
|
||||
if (verifySignature) {
|
||||
JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
|
||||
String prepayId = jsonObject.getStr("prepay_id");
|
||||
// 调用下单方法,得到应答
|
||||
com.wechat.pay.java.service.payments.app.model.PrepayResponse response = service.prepay(prepayRequest);
|
||||
updateOrderPayNo(payParam,outOrderNo);
|
||||
Map<String, String> map = WxPayKit.appPrepayIdCreateSign(appid,
|
||||
setting.getMchId(),
|
||||
prepayId,
|
||||
setting.getApiclient_key(), SignType.MD5);
|
||||
response.getPrepayId(),
|
||||
setting.getApiclientKey(), SignType.MD5);
|
||||
log.info("唤起支付参数:{}", map);
|
||||
|
||||
//修改付款单号
|
||||
updateOrderPayNo(payParam,outOrderNo);
|
||||
|
||||
return ResultUtil.data(map);
|
||||
}
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
@ -339,41 +339,33 @@ public class WechatPlugin implements Payment {
|
||||
if (appid == null) {
|
||||
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
|
||||
}
|
||||
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
.setAppid(appid)
|
||||
.setMchid(setting.getMchId())
|
||||
.setDescription(cashierParam.getDetail())
|
||||
.setOut_trade_no(outOrderNo)
|
||||
.setTime_expire(timeExpire)
|
||||
//回传参数
|
||||
.setAttach(attach)
|
||||
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
|
||||
.setAmount(new Amount().setTotal(fen));
|
||||
|
||||
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
PaymentHttpResponse response = WechatApi.v3(
|
||||
RequestMethodEnums.POST,
|
||||
WechatDomain.CHINA.toString(),
|
||||
WechatApiEnum.NATIVE_PAY.toString(),
|
||||
setting.getMchId(),
|
||||
setting.getSerialNumber(),
|
||||
null,
|
||||
setting.getApiclient_key(),
|
||||
JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
);
|
||||
log.info("统一下单响应 {}", response);
|
||||
//根据证书序列号查询对应的证书来验证签名结果
|
||||
boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
log.info("verifySignature: {}", verifySignature);
|
||||
|
||||
if (verifySignature) {
|
||||
updateOrderPayNo(payParam,outOrderNo);
|
||||
|
||||
return ResultUtil.data(new JSONObject(response.getBody()).getStr("code_url"));
|
||||
} else {
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
Config config =null;
|
||||
if(setting.getPublicType().equals("CERT")){
|
||||
config=this.getCertificateConfig(setting);
|
||||
}else {
|
||||
config=this.getPublicKeyConfig(setting);
|
||||
}
|
||||
// 构建service
|
||||
NativePayService service = new NativePayService.Builder().config(config).build();
|
||||
|
||||
PrepayRequest prepayRequest = new PrepayRequest();
|
||||
Amount amount = new Amount();
|
||||
amount.setTotal(fen);
|
||||
prepayRequest.setAmount(amount);
|
||||
prepayRequest.setAppid(appid);
|
||||
prepayRequest.setMchid(setting.getMchId());
|
||||
prepayRequest.setDescription(cashierParam.getDetail());
|
||||
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
|
||||
prepayRequest.setAttach(attach);
|
||||
prepayRequest.setTimeExpire(timeExpire);
|
||||
prepayRequest.setOutTradeNo(outOrderNo);
|
||||
|
||||
// 调用下单方法,得到应答
|
||||
PrepayResponse response = service.prepay(prepayRequest);
|
||||
updateOrderPayNo(payParam,outOrderNo);
|
||||
return ResultUtil.data(response.getCodeUrl());
|
||||
|
||||
} catch (ServiceException e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
@ -381,6 +373,7 @@ public class WechatPlugin implements Payment {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -394,7 +387,7 @@ public class WechatPlugin implements Payment {
|
||||
return null;
|
||||
}
|
||||
|
||||
Payer payer = new Payer();
|
||||
com.wechat.pay.java.service.payments.jsapi.model.Payer payer = new com.wechat.pay.java.service.payments.jsapi.model.Payer();
|
||||
payer.setOpenid(connect.getUnionId());
|
||||
|
||||
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
|
||||
@ -415,44 +408,37 @@ public class WechatPlugin implements Payment {
|
||||
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
|
||||
|
||||
WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
|
||||
.setAppid(appid)
|
||||
.setMchid(setting.getMchId())
|
||||
.setDescription(cashierParam.getDetail())
|
||||
.setOut_trade_no(outOrderNo)
|
||||
.setTime_expire(timeExpire)
|
||||
.setAttach(attach)
|
||||
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
|
||||
.setAmount(new Amount().setTotal(fen))
|
||||
.setPayer(payer);
|
||||
|
||||
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
|
||||
PaymentHttpResponse response = WechatApi.v3(
|
||||
RequestMethodEnums.POST,
|
||||
WechatDomain.CHINA.toString(),
|
||||
WechatApiEnum.JS_API_PAY.toString(),
|
||||
setting.getMchId(),
|
||||
setting.getSerialNumber(),
|
||||
null,
|
||||
setting.getApiclient_key(),
|
||||
JSONUtil.toJsonStr(unifiedOrderModel)
|
||||
);
|
||||
//根据证书序列号查询对应的证书来验证签名结果
|
||||
boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
|
||||
log.info("verifySignature: {}", verifySignature);
|
||||
log.info("统一下单响应 {}", response);
|
||||
|
||||
if (verifySignature) {
|
||||
String body = response.getBody();
|
||||
JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
String prepayId = jsonObject.getStr("prepay_id");
|
||||
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
|
||||
log.info("唤起支付参数:{}", map);
|
||||
updateOrderPayNo(payParam,outOrderNo);
|
||||
return ResultUtil.data(map);
|
||||
Config config =null;
|
||||
if(setting.getPublicType().equals("CERT")){
|
||||
config=this.getCertificateConfig(setting);
|
||||
}else {
|
||||
config=this.getPublicKeyConfig(setting);
|
||||
}
|
||||
log.error("微信支付参数验证错误,请及时处理");
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
// 构建service
|
||||
JsapiService service = new JsapiService.Builder().config(config).build();
|
||||
|
||||
com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
|
||||
com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
|
||||
amount.setTotal(fen);
|
||||
prepayRequest.setAmount(amount);
|
||||
prepayRequest.setAppid(appid);
|
||||
prepayRequest.setMchid(setting.getMchId());
|
||||
prepayRequest.setDescription(cashierParam.getDetail());
|
||||
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
|
||||
prepayRequest.setAttach(attach);
|
||||
prepayRequest.setTimeExpire(timeExpire);
|
||||
prepayRequest.setOutTradeNo(outOrderNo);
|
||||
prepayRequest.setPayer(payer);
|
||||
// 调用下单方法,得到应答
|
||||
com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse response = service.prepay(prepayRequest);
|
||||
updateOrderPayNo(payParam,outOrderNo);
|
||||
|
||||
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, response.getPrepayId(), setting.getApiclientKey());
|
||||
log.info("唤起支付参数:{}", map);
|
||||
|
||||
return ResultUtil.data(map);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常", e);
|
||||
throw new ServiceException(ResultCode.PAY_ERROR);
|
||||
@ -514,48 +500,51 @@ public class WechatPlugin implements Payment {
|
||||
}
|
||||
}
|
||||
|
||||
//获取微信设置
|
||||
WechatPaymentSetting wechatPaymentSetting = wechatPaymentSetting();
|
||||
|
||||
|
||||
//获取用户openId
|
||||
Connect connect = connectService.queryConnect(
|
||||
ConnectQueryDTO.builder().userId(memberWithdrawApply.getMemberId())
|
||||
.unionType(source).build()
|
||||
);
|
||||
//构建提现,发起申请
|
||||
TransferModel transferModel = new TransferModel()
|
||||
.setAppid(withdrawalSetting.getWechatAppId())
|
||||
.setOut_batch_no(SnowFlake.createStr("T"))
|
||||
.setBatch_name("用户提现")
|
||||
.setBatch_remark("用户提现")
|
||||
.setTotal_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney()))
|
||||
.setTotal_num(1)
|
||||
.setTransfer_scene_id("1000");
|
||||
//获取微信设置
|
||||
WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
|
||||
Config config =null;
|
||||
if(setting.getPublicType().equals("CERT")){
|
||||
config=this.getCertificateConfig(setting);
|
||||
}else {
|
||||
config=this.getPublicKeyConfig(setting);
|
||||
}
|
||||
// 构建service
|
||||
TransferBatchService service = new TransferBatchService.Builder().config(config).build();
|
||||
|
||||
InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
|
||||
request.setAppid(withdrawalSetting.getWechatAppId());
|
||||
request.setOutBatchNo(SnowFlake.createStr("T"));
|
||||
request.setBatchName("用户提现");
|
||||
request.setBatchRemark("用户提现");
|
||||
request.setTotalAmount(CurrencyUtil.getFenLong(memberWithdrawApply.getApplyMoney()));
|
||||
request.setTotalNum(1);
|
||||
request.setTransferSceneId("1000");
|
||||
|
||||
List<TransferDetailInput> transferDetailListList = new ArrayList<>();
|
||||
{
|
||||
TransferDetailInput transferDetailInput = new TransferDetailInput();
|
||||
transferDetailInput.setOut_detail_no(SnowFlake.createStr("TD"));
|
||||
transferDetailInput.setTransfer_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney()));
|
||||
transferDetailInput.setTransfer_remark("用户提现");
|
||||
transferDetailInput.setOutDetailNo(SnowFlake.createStr("TD"));
|
||||
transferDetailInput.setTransferAmount(CurrencyUtil.getFenLong(memberWithdrawApply.getApplyMoney()));
|
||||
transferDetailInput.setTransferRemark("用户提现");
|
||||
transferDetailInput.setOpenid(connect.getUnionId());
|
||||
transferDetailListList.add(transferDetailInput);
|
||||
}
|
||||
transferModel.setTransfer_detail_list(transferDetailListList);
|
||||
request.setTransferDetailList(transferDetailListList);
|
||||
|
||||
PaymentHttpResponse response = WechatApi.v3(
|
||||
RequestMethodEnums.POST,
|
||||
WechatDomain.CHINA.toString(),
|
||||
WechatApiEnum.TRANSFER_BATCHES.toString(),
|
||||
wechatPaymentSetting.getMchId(),
|
||||
wechatPaymentSetting.getSerialNumber(),
|
||||
null,
|
||||
wechatPaymentSetting.getApiclient_key(),
|
||||
JSONUtil.toJsonStr(transferModel)
|
||||
);
|
||||
// 调用下单方法,得到应答
|
||||
InitiateBatchTransferResponse response = service.initiateBatchTransfer(request);
|
||||
log.info("微信提现响应 {}", response);
|
||||
String body = response.getBody();
|
||||
JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
|
||||
return TransferResultDTO.builder().result(jsonObject.getStr("batch_id") != null).response(body).build();
|
||||
|
||||
return TransferResultDTO.builder().result(response.getBatchId()!= null).build();
|
||||
//根据自身业务进行接下来的任务处理
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@ -572,41 +561,61 @@ public class WechatPlugin implements Payment {
|
||||
*/
|
||||
private void verifyNotify(HttpServletRequest request) throws Exception {
|
||||
|
||||
String timestamp = request.getHeader("Wechatpay-Timestamp");
|
||||
String nonce = request.getHeader("Wechatpay-Nonce");
|
||||
String serialNo = request.getHeader("Wechatpay-Serial");
|
||||
String signature = request.getHeader("Wechatpay-Signature");
|
||||
|
||||
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
|
||||
String result = HttpKit.readData(request);
|
||||
log.info("微信支付通知密文 {}", result);
|
||||
// 构造 RequestParam
|
||||
RequestParam requestParam = new RequestParam.Builder()
|
||||
.serialNumber(request.getHeader("Wechatpay-Serial"))
|
||||
.nonce(request.getHeader("Wechatpay-Nonce"))
|
||||
.signature(request.getHeader("Wechatpay-Signature"))
|
||||
.timestamp(request.getHeader("Wechatpay-Timestamp"))
|
||||
.body(HttpKit.readData(request))
|
||||
.build();
|
||||
|
||||
WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
//校验服务器端响应¬
|
||||
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
|
||||
setting.getApiKey3(), Objects.requireNonNull(getPlatformCert()));
|
||||
NotificationConfig config=null;
|
||||
if(setting.getPublicType().equals("CERT")) {
|
||||
config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(setting.getMchId())
|
||||
.privateKey(setting.getApiclientKey())
|
||||
.merchantSerialNumber(setting.getSerialNumber())
|
||||
.apiV3Key(setting.getApiKey3())
|
||||
.build();
|
||||
}else{
|
||||
config = new RSAPublicKeyConfig.Builder()
|
||||
.merchantId(setting.getMchId())
|
||||
.apiV3Key(setting.getApiKey3())
|
||||
.privateKey(setting.getApiclientKey())
|
||||
.merchantSerialNumber(setting.getSerialNumber())
|
||||
.publicKeyId(setting.getPublicId())
|
||||
.publicKey(setting.getPublicKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
log.info("微信支付通知明文 {}", plainText);
|
||||
// 初始化 NotificationParser
|
||||
NotificationParser parser = new NotificationParser(config);
|
||||
|
||||
JSONObject jsonObject = JSONUtil.parseObj(plainText);
|
||||
try {
|
||||
// 以支付通知回调为例,验签、解密并转换成 Transaction
|
||||
Transaction transaction = parser.parse(requestParam, Transaction.class);
|
||||
|
||||
String payParamStr = jsonObject.getStr("attach");
|
||||
String payParamJson = URLDecoder.decode(payParamStr, StandardCharsets.UTF_8);
|
||||
String payParamJson = URLDecoder.decode(transaction.getAttach(), StandardCharsets.UTF_8);
|
||||
PayParam payParam = JSONUtil.toBean(payParamJson, PayParam.class);
|
||||
|
||||
|
||||
String tradeNo = jsonObject.getStr("transaction_id");
|
||||
Double totalAmount = CurrencyUtil.reversalFen(jsonObject.getJSONObject("amount").getDouble("total"));
|
||||
Double totalAmount = CurrencyUtil.reversalFen(transaction.getAmount().getTotal());
|
||||
|
||||
PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
|
||||
PaymentMethodEnum.WECHAT.name(),
|
||||
tradeNo,
|
||||
transaction.getTransactionId(),
|
||||
totalAmount,
|
||||
payParam
|
||||
);
|
||||
|
||||
paymentService.success(paymentSuccessParams);
|
||||
log.info("微信支付回调:支付成功{}", plainText);
|
||||
} catch (ValidationException e) {
|
||||
// 签名验证失败,返回 401 UNAUTHORIZED 状态码
|
||||
log.error("sign verification failed", e);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -614,39 +623,33 @@ public class WechatPlugin implements Payment {
|
||||
|
||||
try {
|
||||
|
||||
Amount amount = new Amount().setRefund(CurrencyUtil.fen(refundLog.getTotalAmount()))
|
||||
.setTotal(CurrencyUtil.fen(orderService.getPaymentTotal(refundLog.getOrderSn())));
|
||||
|
||||
//退款参数准备
|
||||
RefundModel refundModel = new RefundModel()
|
||||
.setTransaction_id(refundLog.getPaymentReceivableNo())
|
||||
.setOut_refund_no(refundLog.getOutOrderNo())
|
||||
.setReason(refundLog.getRefundReason())
|
||||
.setAmount(amount)
|
||||
.setNotify_url(refundNotifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
|
||||
|
||||
AmountReq amount = new AmountReq();
|
||||
amount.setRefund(CurrencyUtil.getFenLong(refundLog.getTotalAmount()));
|
||||
amount.setTotal(CurrencyUtil.getFenLong(orderService.getPaymentTotal(refundLog.getOrderSn())));
|
||||
amount.setCurrency("CNY");
|
||||
//获取微信设置
|
||||
WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
|
||||
log.info("微信退款参数 {}", JSONUtil.toJsonStr(refundModel));
|
||||
PaymentHttpResponse response = WechatApi.v3(
|
||||
RequestMethodEnums.POST,
|
||||
WechatDomain.CHINA.toString(),
|
||||
WechatApiEnum.DOMESTIC_REFUNDS.toString(),
|
||||
setting.getMchId(),
|
||||
setting.getSerialNumber(),
|
||||
null,
|
||||
setting.getApiclient_key(),
|
||||
JSONUtil.toJsonStr(refundModel)
|
||||
);
|
||||
log.info("微信退款响应 {}", response);
|
||||
//退款申请成功
|
||||
if (response.getStatus() == 200) {
|
||||
refundLogService.save(refundLog);
|
||||
} else {
|
||||
//退款申请失败
|
||||
refundLog.setErrorMessage(response.getBody());
|
||||
refundLogService.save(refundLog);
|
||||
Config config =null;
|
||||
if(setting.getPublicType().equals("CERT")){
|
||||
config=this.getCertificateConfig(setting);
|
||||
}else {
|
||||
config=this.getPublicKeyConfig(setting);
|
||||
}
|
||||
// 构建service
|
||||
RefundService refundService = new RefundService.Builder().config(config).build();
|
||||
|
||||
CreateRequest request = new CreateRequest();
|
||||
request.setTransactionId(refundLog.getPaymentReceivableNo());
|
||||
request.setAmount(amount);
|
||||
request.setOutRefundNo(refundLog.getOutOrderNo());
|
||||
request.setReason(refundLog.getRefundReason());
|
||||
request.setNotifyUrl(refundNotifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
|
||||
|
||||
Refund refund=refundService.create(request);
|
||||
|
||||
log.info("微信退款响应 {}", refund);
|
||||
refundLogService.save(refundLog);
|
||||
} catch (Exception e) {
|
||||
log.error("微信退款申请失败", e);
|
||||
}
|
||||
@ -655,50 +658,46 @@ public class WechatPlugin implements Payment {
|
||||
|
||||
@Override
|
||||
public void refundNotify(HttpServletRequest request) {
|
||||
String timestamp = request.getHeader("Wechatpay-Timestamp");
|
||||
String nonce = request.getHeader("Wechatpay-Nonce");
|
||||
String serialNo = request.getHeader("Wechatpay-Serial");
|
||||
String signature = request.getHeader("Wechatpay-Signature");
|
||||
// 构造 RequestParam
|
||||
RequestParam requestParam = new RequestParam.Builder()
|
||||
.serialNumber(request.getHeader("Wechatpay-Serial"))
|
||||
.nonce(request.getHeader("Wechatpay-Nonce"))
|
||||
.signature(request.getHeader("Wechatpay-Signature"))
|
||||
.timestamp(request.getHeader("Wechatpay-Timestamp"))
|
||||
.body(HttpKit.readData(request))
|
||||
.build();
|
||||
|
||||
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
|
||||
String result = HttpKit.readData(request);
|
||||
log.info("微信退款通知密文 {}", result);
|
||||
JSONObject ciphertext = JSONUtil.parseObj(result);
|
||||
|
||||
try { //校验服务器端响应¬
|
||||
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
|
||||
wechatPaymentSetting().getApiKey3(), Objects.requireNonNull(getPlatformCert()));
|
||||
log.info("微信退款通知明文 {}", plainText);
|
||||
|
||||
if (("REFUND.SUCCESS").equals(ciphertext.getStr("event_type"))) {
|
||||
log.info("退款成功 {}", plainText);
|
||||
//校验服务器端响应
|
||||
JSONObject jsonObject = JSONUtil.parseObj(plainText);
|
||||
String transactionId = jsonObject.getStr("transaction_id");
|
||||
String refundId = jsonObject.getStr("refund_id");
|
||||
WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
NotificationConfig config=null;
|
||||
if(setting.getPublicType().equals("CERT")) {
|
||||
config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(setting.getMchId())
|
||||
.privateKey(setting.getApiclientKey())
|
||||
.merchantSerialNumber(setting.getSerialNumber())
|
||||
.apiV3Key(setting.getApiKey3())
|
||||
.build();
|
||||
}else{
|
||||
config = new RSAPublicKeyConfig.Builder()
|
||||
.merchantId(setting.getMchId())
|
||||
.apiV3Key(setting.getApiKey3())
|
||||
.privateKey(setting.getApiclientKey())
|
||||
.merchantSerialNumber(setting.getSerialNumber())
|
||||
.publicKeyId(setting.getPublicId())
|
||||
.publicKey(setting.getPublicKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
// 初始化 NotificationParser
|
||||
NotificationParser parser = new NotificationParser(config);
|
||||
try {
|
||||
Refund refund = parser.parse(requestParam, Refund.class);
|
||||
RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo,
|
||||
transactionId));
|
||||
refund.getTransactionId()));
|
||||
if (refundLog != null) {
|
||||
refundLog.setIsRefund(true);
|
||||
refundLog.setReceivableNo(refundId);
|
||||
refundLog.setReceivableNo(refund.getRefundId());
|
||||
refundLogService.saveOrUpdate(refundLog);
|
||||
}
|
||||
|
||||
} else {
|
||||
log.info("退款失败 {}", plainText);
|
||||
JSONObject jsonObject = JSONUtil.parseObj(plainText);
|
||||
String transactionId = jsonObject.getStr("transaction_id");
|
||||
String refundId = jsonObject.getStr("refund_id");
|
||||
|
||||
RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo,
|
||||
transactionId));
|
||||
if (refundLog != null) {
|
||||
refundLog.setReceivableNo(refundId);
|
||||
refundLog.setErrorMessage(ciphertext.getStr("summary"));
|
||||
refundLogService.saveOrUpdate(refundLog);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("微信退款失败", e);
|
||||
}
|
||||
@ -721,77 +720,34 @@ public class WechatPlugin implements Payment {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台公钥
|
||||
*
|
||||
* @return 平台公钥
|
||||
* 获取微信公钥配置
|
||||
* @param setting
|
||||
* @return
|
||||
*/
|
||||
private X509Certificate getPlatformCert() {
|
||||
//获取缓存中的平台公钥,如果有则直接返回,否则去微信请求
|
||||
String publicCert = cache.getString(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix());
|
||||
if (!StringUtils.isEmpty(publicCert)) {
|
||||
return PayKit.getCertificate(publicCert);
|
||||
}
|
||||
//获取平台证书列表
|
||||
try {
|
||||
|
||||
WechatPaymentSetting setting = wechatPaymentSetting();
|
||||
|
||||
PaymentHttpResponse response = WechatApi.v3(
|
||||
RequestMethodEnums.GET,
|
||||
WechatDomain.CHINA.toString(),
|
||||
WechatApiEnum.GET_CERTIFICATES.toString(),
|
||||
setting.getMchId(),
|
||||
setting.getSerialNumber(),
|
||||
null,
|
||||
setting.getApiclient_key(),
|
||||
""
|
||||
);
|
||||
String body = response.getBody();
|
||||
log.info("获取微信平台证书body: {}", body);
|
||||
if (response.getStatus() == 200) {
|
||||
JSONObject jsonObject = JSONUtil.parseObj(body);
|
||||
JSONArray dataArray = jsonObject.getJSONArray("data");
|
||||
//默认认为只有一个平台证书
|
||||
JSONObject encryptObject = dataArray.getJSONObject(0);
|
||||
JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
|
||||
String associatedData = encryptCertificate.getStr("associated_data");
|
||||
String cipherText = encryptCertificate.getStr("ciphertext");
|
||||
String nonce = encryptCertificate.getStr("nonce");
|
||||
publicCert = getPlatformCertStr(associatedData, nonce, cipherText);
|
||||
long second = (PayKit.getCertificate(publicCert).getNotAfter().getTime() - System.currentTimeMillis()) / 1000;
|
||||
cache.put(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix(), publicCert, second);
|
||||
} else {
|
||||
log.error("证书获取失败:{}" + body);
|
||||
throw new ServiceException(ResultCode.WECHAT_CERT_ERROR);
|
||||
}
|
||||
return PayKit.getCertificate(publicCert);
|
||||
} catch (Exception e) {
|
||||
log.error("证书获取失败", e);
|
||||
}
|
||||
return null;
|
||||
private RSAPublicKeyConfig getPublicKeyConfig(WechatPaymentSetting setting){
|
||||
return
|
||||
new RSAPublicKeyConfig.Builder()
|
||||
.merchantId(setting.getMchId())
|
||||
.privateKey(setting.getApiclientKey())
|
||||
.publicKey(setting.getPublicKey())
|
||||
.publicKeyId(setting.getPublicId())
|
||||
.merchantSerialNumber(setting.getSerialNumber())
|
||||
.apiV3Key(setting.getApiKey3())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台证书缓存的字符串
|
||||
* 下列各个密钥参数
|
||||
*
|
||||
* @param associatedData 密钥参数
|
||||
* @param nonce 密钥参数
|
||||
* @param cipherText 密钥参数
|
||||
* @return platform key
|
||||
* @throws GeneralSecurityException 密钥获取异常
|
||||
* 获取微信证书配置
|
||||
* @param setting
|
||||
* @return
|
||||
*/
|
||||
private String getPlatformCertStr(String associatedData, String nonce, String cipherText) throws GeneralSecurityException {
|
||||
|
||||
|
||||
AesUtil aesUtil = new AesUtil(wechatPaymentSetting().getApiKey3().getBytes(StandardCharsets.UTF_8));
|
||||
//平台证书密文解密
|
||||
//encrypt_certificate 中的 associated_data nonce ciphertext
|
||||
return aesUtil.decryptToString(
|
||||
associatedData.getBytes(StandardCharsets.UTF_8),
|
||||
nonce.getBytes(StandardCharsets.UTF_8),
|
||||
cipherText
|
||||
);
|
||||
private RSAAutoCertificateConfig getCertificateConfig(WechatPaymentSetting setting) {
|
||||
return new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(setting.getMchId())
|
||||
.privateKey(setting.getApiclientKey())
|
||||
.merchantSerialNumber(setting.getSerialNumber())
|
||||
.apiV3Key(setting.getApiKey3())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -37,22 +37,36 @@ public class WechatPaymentSetting {
|
||||
* 商户号
|
||||
*/
|
||||
private String mchId;
|
||||
/**
|
||||
* 私钥
|
||||
*/
|
||||
private String apiclient_key;
|
||||
/**
|
||||
* pem 证书
|
||||
*/
|
||||
private String apiclient_cert_pem;
|
||||
/**
|
||||
* p12 证书
|
||||
*/
|
||||
private String apiclient_cert_p12;
|
||||
/**
|
||||
* 商户证书序列号
|
||||
*/
|
||||
private String serialNumber;
|
||||
/**
|
||||
* 私钥
|
||||
*/
|
||||
private String apiclientKey;
|
||||
/**
|
||||
* 公钥ID
|
||||
*/
|
||||
private String publicId;
|
||||
/**
|
||||
* 公钥
|
||||
*/
|
||||
private String publicKey;
|
||||
|
||||
/**
|
||||
* 微信验证方式:公钥/证书(KEY/CERT)
|
||||
*/
|
||||
private String publicType;
|
||||
// /**
|
||||
// * pem 证书
|
||||
// */
|
||||
// private String apiclient_cert_pem;
|
||||
// /**
|
||||
// * p12 证书
|
||||
// */
|
||||
// private String apiclient_cert_p12;
|
||||
|
||||
/**
|
||||
* apiv3私钥
|
||||
*/
|
||||
|
||||
@ -19,11 +19,6 @@
|
||||
<artifactId>framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -2,8 +2,6 @@ package cn.lili;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
|
||||
/**
|
||||
@ -15,17 +13,4 @@ public class ImApiApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ImApiApplication.class, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果使用独立的servlet容器,
|
||||
* 而不是直接使用springboot的内置容器,
|
||||
* 就不要注入ServerEndpointExporter,
|
||||
* 因为它将由容器自己提供和管理
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
}
|
||||
|
||||
117
im-api/src/main/java/cn/lili/controller/im/FriendController.java
Normal file
117
im-api/src/main/java/cn/lili/controller/im/FriendController.java
Normal file
@ -0,0 +1,117 @@
|
||||
package cn.lili.controller.im;
|
||||
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.enums.ResultUtil;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.vo.ResultMessage;
|
||||
import cn.lili.modules.im.entity.dos.Friend;
|
||||
import cn.lili.modules.im.entity.vo.FriendVO;
|
||||
import cn.lili.modules.im.service.FriendService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Chopper
|
||||
*/
|
||||
@RestController
|
||||
@Api(tags = "关注管理接口")
|
||||
@RequestMapping("/im/friend")
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class FriendController {
|
||||
|
||||
private final FriendService friendService;
|
||||
|
||||
@GetMapping("/search")
|
||||
@ApiOperation(value = "搜索用户")
|
||||
public ResultMessage<List<FriendVO>> searchUsers(
|
||||
@ApiParam(value = "搜索关键词(用户名/手机号)") @RequestParam String keyword,
|
||||
@ApiParam(value = "是否只搜索店铺") @RequestParam(required = false) Boolean onlyStore) {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
List<FriendVO> users = friendService.searchUsers(keyword, onlyStore, currentUser.getId());
|
||||
return ResultUtil.data(users);
|
||||
}
|
||||
|
||||
@GetMapping("/following")
|
||||
@ApiOperation(value = "获取关注列表")
|
||||
public ResultMessage<List<FriendVO>> getFollowingList() {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
List<FriendVO> friends = friendService.getMutualFriends(currentUser.getId());
|
||||
return ResultUtil.data(friends);
|
||||
}
|
||||
|
||||
@GetMapping("/user/{userId}")
|
||||
@ApiOperation(value = "获取用户详情")
|
||||
public ResultMessage<FriendVO> getUserDetails(
|
||||
@ApiParam(value = "用户ID", required = true) @PathVariable String userId) {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
FriendVO friend = friendService.getFriendDetails(userId);
|
||||
return ResultUtil.data(friend);
|
||||
}
|
||||
|
||||
@PostMapping("/follow/{userId}")
|
||||
@ApiOperation(value = "关注用户")
|
||||
public ResultMessage<Object> followUser(
|
||||
@ApiParam(value = "要关注的用户ID", required = true) @PathVariable String userId) {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
friendService.addFriend(currentUser.getId(), userId);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/follow-by-mobile")
|
||||
@ApiOperation(value = "通过手机号关注用户")
|
||||
public ResultMessage<Object> followByMobile(
|
||||
@ApiParam(value = "手机号", required = true) @RequestParam String mobile,
|
||||
@ApiParam(value = "备注") @RequestParam(required = false) String remark) {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
friendService.addFriendByMobile(mobile, remark);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/unfollow/{userId}")
|
||||
@ApiOperation(value = "取消关注")
|
||||
public ResultMessage<Object> unfollowUser(
|
||||
@ApiParam(value = "要取消关注的用户ID", required = true) @PathVariable String userId) {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
friendService.removeFriend(currentUser.getId(), userId);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PutMapping("/remark/{userId}")
|
||||
@ApiOperation(value = "更新备注")
|
||||
public ResultMessage<Object> updateRemark(
|
||||
@ApiParam(value = "用户ID", required = true) @PathVariable String userId,
|
||||
@ApiParam(value = "备注", required = true) @RequestParam String remark) {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
friendService.updateRemark(currentUser.getId(), userId, remark);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,183 @@
|
||||
package cn.lili.controller.im;
|
||||
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.enums.ResultUtil;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.vo.ResultMessage;
|
||||
import cn.lili.common.vo.ResultPageVO;
|
||||
import cn.lili.modules.im.entity.dos.ImGroup;
|
||||
import cn.lili.modules.im.entity.vo.FriendVO;
|
||||
import cn.lili.modules.im.entity.vo.ImGroupVO;
|
||||
import cn.lili.modules.im.entity.vo.ImGroupMemberVO;
|
||||
import cn.lili.modules.im.entity.vo.ImMessageVO;
|
||||
import cn.lili.modules.im.service.ImGroupService;
|
||||
import cn.lili.modules.im.service.FriendService;
|
||||
import cn.lili.modules.im.service.ImMessageService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 群聊管理接口
|
||||
*/
|
||||
@RestController
|
||||
@Api(tags = "群聊管理接口")
|
||||
@RequestMapping("/im/group")
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class ImGroupController {
|
||||
|
||||
private final ImGroupService imGroupService;
|
||||
private final FriendService friendService;
|
||||
private final ImMessageService imMessageService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@ApiOperation(value = "获取我的群聊列表")
|
||||
public ResultMessage<List<ImGroupVO>> getMyGroups() {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
List<ImGroupVO> groups = imGroupService.getUserGroups(currentUser.getId());
|
||||
return ResultUtil.data(groups);
|
||||
}
|
||||
|
||||
@GetMapping("/{groupId}")
|
||||
@ApiOperation(value = "获取群聊详情")
|
||||
public ResultMessage<ImGroupVO> getGroupDetail(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId) {
|
||||
ImGroupVO group = imGroupService.getGroupDetail(groupId);
|
||||
return ResultUtil.data(group);
|
||||
}
|
||||
|
||||
@GetMapping("/{groupId}/members")
|
||||
@ApiOperation(value = "获取群成员列表")
|
||||
public ResultMessage<List<ImGroupMemberVO>> getGroupMembers(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId) {
|
||||
List<ImGroupMemberVO> members = imGroupService.getGroupMembers(groupId);
|
||||
return ResultUtil.data(members);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{groupId}/quit")
|
||||
@ApiOperation(value = "退出群聊")
|
||||
public ResultMessage<Void> quitGroup(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId) {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
imGroupService.quitGroup(groupId, currentUser.getId());
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PutMapping("/{groupId}")
|
||||
@ApiOperation(value = "修改群信息")
|
||||
public ResultMessage<Void> updateGroupInfo(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "群名称") @RequestParam(required = false) String groupName,
|
||||
@ApiParam(value = "群公告") @RequestParam(required = false) String notice,
|
||||
@ApiParam(value = "群头像") @RequestParam(required = false) String avatar) {
|
||||
imGroupService.updateGroupInfo(groupId, groupName, notice, avatar);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@GetMapping("/{groupId}/messages")
|
||||
@ApiOperation(value = "获取群聊消息历史")
|
||||
public ResultMessage<ResultPageVO<ImMessageVO>> getGroupMessages(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "页码") @RequestParam(defaultValue = "1") Integer pageNumber,
|
||||
@ApiParam(value = "每页大小") @RequestParam(defaultValue = "20") Integer pageSize) {
|
||||
ResultPageVO<ImMessageVO> messages = imMessageService.getGroupMessages(groupId, pageNumber, pageSize);
|
||||
return ResultUtil.data(messages);
|
||||
}
|
||||
|
||||
@PostMapping("/{groupId}/message")
|
||||
@ApiOperation(value = "发送群聊消息")
|
||||
public ResultMessage<ImMessageVO> sendGroupMessage(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "消息内容", required = true) @RequestParam String content,
|
||||
@ApiParam(value = "消息类型", required = true) @RequestParam String type) {
|
||||
ImMessageVO message = imMessageService.sendGroupMessage(groupId, content, type);
|
||||
return ResultUtil.data(message);
|
||||
}
|
||||
|
||||
@GetMapping("/friends")
|
||||
@ApiOperation(value = "获取可邀请的好友列表")
|
||||
public ResultMessage<List<FriendVO>> getInvitableFriends() {
|
||||
AuthUser currentUser = UserContext.getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
|
||||
}
|
||||
List<FriendVO> friends = friendService.getMutualFriends(currentUser.getId());
|
||||
return ResultUtil.data(friends);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ApiOperation(value = "创建群聊")
|
||||
public ResultMessage<ImGroup> createGroup(
|
||||
@ApiParam(value = "群名称", required = true) @RequestParam String groupName,
|
||||
@ApiParam(value = "初始成员ID列表") @RequestParam(required = false) List<String> memberIds) {
|
||||
ImGroup group = imGroupService.createGroup(groupName, memberIds);
|
||||
return ResultUtil.data(group);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{groupId}")
|
||||
@ApiOperation(value = "解散群聊")
|
||||
public ResultMessage<Void> dismissGroup(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId) {
|
||||
imGroupService.dismissGroup(groupId);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/{groupId}/invite")
|
||||
@ApiOperation(value = "邀请成员")
|
||||
public ResultMessage<Void> inviteMembers(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "成员ID列表", required = true) @RequestParam List<String> memberIds) {
|
||||
imGroupService.inviteMembers(groupId, memberIds);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/{groupId}/admin/{memberId}")
|
||||
@ApiOperation(value = "设置管理员")
|
||||
public ResultMessage<Void> setAdmin(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "成员ID", required = true) @PathVariable String memberId) {
|
||||
imGroupService.setAdmin(groupId, memberId);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{groupId}/admin/{memberId}")
|
||||
@ApiOperation(value = "取消管理员")
|
||||
public ResultMessage<Void> removeAdmin(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "成员ID", required = true) @PathVariable String memberId) {
|
||||
imGroupService.removeAdmin(groupId, memberId);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/{groupId}/mute/{memberId}")
|
||||
@ApiOperation(value = "禁言成员")
|
||||
public ResultMessage<Void> muteMember(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "成员ID", required = true) @PathVariable String memberId,
|
||||
@ApiParam(value = "禁言时长(分钟)", required = true) @RequestParam Integer duration) {
|
||||
imGroupService.muteMember(groupId, memberId, duration);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{groupId}/mute/{memberId}")
|
||||
@ApiOperation(value = "解除成员禁言")
|
||||
public ResultMessage<Void> unmuteMember(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "成员ID", required = true) @PathVariable String memberId) {
|
||||
imGroupService.unmuteMember(groupId, memberId);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
}
|
||||
@ -4,11 +4,14 @@ import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.enums.ResultUtil;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.vo.ResultMessage;
|
||||
import cn.lili.common.vo.ResultPageVO;
|
||||
import cn.lili.modules.im.entity.dos.ImMessage;
|
||||
import cn.lili.modules.im.entity.dto.MessageQueryParams;
|
||||
import cn.lili.modules.im.entity.vo.ImMessageVO;
|
||||
import cn.lili.modules.im.service.ImMessageService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -87,4 +90,24 @@ public class ImMessageController {
|
||||
imMessageService.cleanUnreadMessage();
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@GetMapping("/{groupId}/messages")
|
||||
@ApiOperation(value = "获取群聊消息历史")
|
||||
public ResultMessage<ResultPageVO<ImMessageVO>> getGroupMessages(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "页码") @RequestParam(defaultValue = "1") Integer pageNumber,
|
||||
@ApiParam(value = "每页大小") @RequestParam(defaultValue = "20") Integer pageSize) {
|
||||
ResultPageVO<ImMessageVO> messages = imMessageService.getGroupMessages(groupId, pageNumber, pageSize);
|
||||
return ResultUtil.data(messages);
|
||||
}
|
||||
|
||||
@PostMapping("/{groupId}/message")
|
||||
@ApiOperation(value = "发送群聊消息")
|
||||
public ResultMessage<ImMessageVO> sendGroupMessage(
|
||||
@ApiParam(value = "群ID", required = true) @PathVariable String groupId,
|
||||
@ApiParam(value = "消息内容", required = true) @RequestParam String content,
|
||||
@ApiParam(value = "消息类型", required = true) @RequestParam String type) {
|
||||
ImMessageVO message = imMessageService.sendGroupMessage(groupId, content, type);
|
||||
return ResultUtil.data(message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ spring:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password: lilishop
|
||||
# password: lilishop
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数(使用负值表示没有限制) 默认 8
|
||||
@ -64,9 +64,9 @@ spring:
|
||||
default-datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
url: jdbc:mysql://127.0.0.1:3306/wuzhongjie?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: lilishop
|
||||
password: 123
|
||||
maxActive: 20
|
||||
initialSize: 5
|
||||
maxWait: 60000
|
||||
@ -259,7 +259,7 @@ lili:
|
||||
after-sale-topic: lili_after_sale_topic
|
||||
after-sale-group: lili_after_sale_group
|
||||
rocketmq:
|
||||
name-server: 127.0.0.1:9876
|
||||
name-server: 82.156.121.2:9876
|
||||
producer:
|
||||
group: lili_group
|
||||
send-message-timeout: 30000
|
||||
@ -267,7 +267,7 @@ rocketmq:
|
||||
xxl:
|
||||
job:
|
||||
admin:
|
||||
addresses: http://127.0.0.1:9001/xxl-job-admin
|
||||
addresses: http://82.156.121.2:9001/xxl-job-admin
|
||||
executor:
|
||||
appname: xxl-job-executor-lilishop
|
||||
address:
|
||||
|
||||
@ -35,7 +35,7 @@ spring:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password: lilishop
|
||||
# password: lilishop
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数(使用负值表示没有限制) 默认 8
|
||||
|
||||
@ -35,7 +35,7 @@ spring:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password: lilishop
|
||||
# password: lilishop
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数(使用负值表示没有限制) 默认 8
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
### web
|
||||
server.port=9001
|
||||
server.port=30001
|
||||
server.servlet.context-path=/xxl-job-admin
|
||||
|
||||
### actuator
|
||||
@ -23,9 +23,9 @@ mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml
|
||||
#mybatis.type-aliases-package=com.xxl.job.admin.core.model
|
||||
|
||||
### xxl-job, datasource
|
||||
spring.datasource.url=jdbc:mysql://192.168.0.106:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
|
||||
spring.datasource.url=jdbc:mysql://localhost:3306/wuzhongjie?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
|
||||
spring.datasource.username=root
|
||||
spring.datasource.password=lilishop
|
||||
spring.datasource.password=123
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
|
||||
### datasource-pool
|
||||
@ -35,8 +35,8 @@ spring.datasource.hikari.maximum-pool-size=30
|
||||
spring.datasource.hikari.auto-commit=true
|
||||
spring.datasource.hikari.idle-timeout=30000
|
||||
spring.datasource.hikari.pool-name=HikariCP
|
||||
spring.datasource.hikari.max-lifetime=900000
|
||||
spring.datasource.hikari.connection-timeout=10000
|
||||
spring.datasource.hikari.max-lifetime=90000
|
||||
spring.datasource.hikari.connection-timeout=30000
|
||||
spring.datasource.hikari.connection-test-query=SELECT 1
|
||||
spring.datasource.hikari.validation-timeout=1000
|
||||
|
||||
|
||||
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-02-22.zip
Normal file
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-02-22.zip
Normal file
Binary file not shown.
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-02-24.zip
Normal file
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-02-24.zip
Normal file
Binary file not shown.
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-02-25.zip
Normal file
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-02-25.zip
Normal file
Binary file not shown.
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-02-26.zip
Normal file
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-02-26.zip
Normal file
Binary file not shown.
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-03-03.zip
Normal file
BIN
xxl-job/xxl-job/xxl-job-admin.log.2025-03-03.zip
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user