mirror of
https://github.com/RemainderTime/spring-boot-base-demo.git
synced 2026-03-02 18:10:44 +08:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # src/main/resources/application-dev.yml
This commit is contained in:
59
README.md
59
README.md
@@ -1,21 +1,49 @@
|
||||
# 拿来即用springboot基础项目
|
||||
## 拿来即用springboot基础脚手架
|
||||
|
||||
---
|
||||
### 项目介绍
|
||||
|
||||
技术栈 | 名称
|
||||
-------- | -----
|
||||
Spring boot | 3.3.3
|
||||
JWT | 身份验证和授权
|
||||
RSA | 数据加密
|
||||
MybatisPlus | 持久层框架
|
||||
MYSQL | 数据库
|
||||
Hikari | 数据库连接池
|
||||
Reids | 数据缓存
|
||||
springdoc-openapi | 接口文档
|
||||
thymeleaf | 模版引擎
|
||||
docker | 容器(Dockerfile)
|
||||
[](https://blog.csdn.net/qq_39818325?type=blog)
|
||||
[](https://github.com/RemainderTime/spring-boot-base-demo)
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
---
|
||||
> 这是一个基于 **Spring Boot 3.3.3** 的快速构建单体架构脚手架,旨在帮助开发者快速搭建高效、稳定的项目基础框架。项目集成了多种常用的技术组件与功能,涵盖从用户认证到数据加密、从全局异常处理到搜索引擎操作,适合个人学习与企业级单体应用开发。
|
||||
|
||||
- #### 版本更新 2024-10-12
|
||||
### 集成技术与功能亮点
|
||||
|
||||
- 身份认证与授权(JWT):基于 JWT 实现用户认证与授权,确保系统安全性。
|
||||
- 数据加密(RSA):提供 RSA 非对称加密支持,保障敏感数据安全。
|
||||
- 持久层框架(MyBatis Plus):简化数据库操作,提供高效的 CRUD 支持。
|
||||
- 数据库(MySQL):采用 MySQL 作为默认数据库,易于扩展和维护。
|
||||
- 数据连接池(Hikari):高性能数据源管理,优化数据库连接效率。
|
||||
- 缓存(Redis):支持分布式缓存,提升系统响应速度与并发能力。
|
||||
- 接口文档(springdoc-openapi):自动生成标准化 API 文档,便于调试与集成。
|
||||
- 模板引擎(Thymeleaf):支持动态页面渲染,提升前后端协同效率。
|
||||
- 容器化支持(Docker):内置 Dockerfile,轻松实现环境部署与迁移。
|
||||
- 搜索引擎(Elasticsearch 8.x):集成最新版本 Elasticsearch Java 客户端,提供高效的全文检索与复杂查询功能。
|
||||
- 全局异常处理:统一管理异常,提升代码可维护性与调试效率。
|
||||
- 拦截器支持:轻松实现请求拦截与权限控制。
|
||||
|
||||
### 项目优势
|
||||
**全面适配 Spring Boot 3.x**
|
||||
- 所有组件已全面升级为支持 Spring Boot 3.x 的最新版本。解决了开发者在版本升级中遇到的各种不兼容和适配问题,大大减少了升级带来的额外工作量,让项目开发更加顺畅。
|
||||
|
||||
**初学者友好**
|
||||
- 提供清晰的代码结构与详细的配置说明,帮助初学者快速上手微服务与单体架构的开发实践。
|
||||
|
||||
**高扩展性**
|
||||
- 丰富的功能集成,涵盖了开发中常见的场景,减少重复开发工作量,同时为定制化需求预留了扩展空间。
|
||||
|
||||
**稀缺的最新技术操作示例**
|
||||
- 最新版本的 Elasticsearch 8.x 集成、Java 客户端操作示例和现代化 API 设计,让开发者能够轻松掌握分布式搜索引擎的使用。
|
||||
|
||||
### 版本更新 2024-10-12
|
||||
|
||||
---
|
||||
1. springboot版本升级3.x
|
||||
2. mybatis plus版本升级3.x
|
||||
3. dynamic mybatis plus版本升级3.x
|
||||
@@ -27,6 +55,5 @@ docker | 容器(Dockerfile)
|
||||
9. 新增本地日志配置文件
|
||||
|
||||
---
|
||||
编码不易,欢迎点击右上角star支持
|
||||
|
||||
如果这个项目对你有帮助,请随手点个 Star ⭐ 支持一下吧!🎉✨ 你的支持是我持续优化的动力!❤️
|
||||
|
||||
|
||||
34
pom.xml
34
pom.xml
@@ -83,7 +83,6 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<version>3.3.3</version>
|
||||
</dependency>
|
||||
<!--redis连接池需要依赖-->
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
|
||||
@@ -145,6 +144,31 @@
|
||||
<scope>provided</scope> <!-- 在Web服务器环境中由服务器提供 -->
|
||||
</dependency>
|
||||
|
||||
<!-- elasticsearch8.x 搜索引擎 -->
|
||||
<dependency>
|
||||
<groupId>co.elastic.clients</groupId>
|
||||
<artifactId>elasticsearch-java</artifactId>
|
||||
<version>8.16.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--微信支付-->
|
||||
<dependency>
|
||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||
<artifactId>wechatpay-java</artifactId>
|
||||
<version>0.2.15</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||
<artifactId>wechatpay-apache-httpclient</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
<!--支付宝-->
|
||||
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>4.40.0.ALL</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -162,6 +186,14 @@
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>14</source>
|
||||
<target>14</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
42
src/main/java/cn/xf/basedemo/common/model/EsBaseModel.java
Normal file
42
src/main/java/cn/xf/basedemo/common/model/EsBaseModel.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package cn.xf.basedemo.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* packageName cn.xf.basedemo.common.model
|
||||
* @author remaindertime
|
||||
* @className EsModel
|
||||
* @date 2024/12/10
|
||||
* @description es基础模型
|
||||
*/
|
||||
@Data
|
||||
public class EsBaseModel<T> {
|
||||
|
||||
public EsBaseModel(String indexName, String documentId, T documentModel, Class<T> clazz) {
|
||||
this.indexName = indexName;
|
||||
this.documentId = documentId;
|
||||
this.documentModel = documentModel;
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* 索引名称
|
||||
*/
|
||||
private String indexName;
|
||||
|
||||
/**
|
||||
* 文档id
|
||||
*/
|
||||
private String documentId;
|
||||
|
||||
/**
|
||||
* 映射对象
|
||||
*/
|
||||
private T documentModel;
|
||||
|
||||
/**
|
||||
* 映射对象类对象
|
||||
*/
|
||||
private Class<T> clazz;
|
||||
|
||||
}
|
||||
81
src/main/java/cn/xf/basedemo/common/model/EsSearchModel.java
Normal file
81
src/main/java/cn/xf/basedemo/common/model/EsSearchModel.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package cn.xf.basedemo.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* packageName cn.xf.basedemo.common.model
|
||||
* @author remaindertime
|
||||
* @className EsSearchModel
|
||||
* @date 2024/12/11
|
||||
* @description es 搜索模型
|
||||
*/
|
||||
@Data
|
||||
public class EsSearchModel<T> {
|
||||
|
||||
public EsSearchModel() {
|
||||
// 使用 LinkedHashMap 保持插入顺序
|
||||
this.sort = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 索引名称
|
||||
*/
|
||||
private String indexName;
|
||||
|
||||
/**
|
||||
* 文档类型
|
||||
*/
|
||||
private Class<T> clazz;
|
||||
|
||||
/**
|
||||
* 页数
|
||||
*/
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 每页数量
|
||||
*/
|
||||
private Integer pageSize;
|
||||
/**
|
||||
* 精准查询字段
|
||||
*/
|
||||
private Map<String, Object> termQuery;
|
||||
|
||||
/**
|
||||
* 模糊查询字段(一般是text类型)
|
||||
*/
|
||||
private Map<String, Object> matchQuery;
|
||||
|
||||
/**
|
||||
* 排序字段规则 ({"age":"desc"})
|
||||
*/
|
||||
private Map<String, String> sort;
|
||||
|
||||
/**
|
||||
* 分组去重字段(支持的字段类型:keyword、numeric、date 和 boolean )
|
||||
*/
|
||||
private String repeatField;;
|
||||
|
||||
/**
|
||||
* 分组嵌套查询别名
|
||||
*/
|
||||
private String innerAlias;
|
||||
|
||||
/**
|
||||
* 分组嵌套查询数量
|
||||
*/
|
||||
private Integer innerSize;
|
||||
|
||||
/**
|
||||
* 指定需要返回的字段
|
||||
*/
|
||||
private List<String> includes;
|
||||
/**
|
||||
* 指定需要排除的字段
|
||||
*/
|
||||
private List<String> excludes;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.xf.basedemo.common.model.pay;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AlipayTransRsqVo {
|
||||
private boolean success = false;
|
||||
private String code;
|
||||
private String subCode;
|
||||
private String msg;
|
||||
private String orderId;
|
||||
private String status;
|
||||
private String payFundOrderID;
|
||||
private String outBizNo;
|
||||
private String transDate;
|
||||
private String settleSerialNo;
|
||||
private String amount;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.xf.basedemo.common.model.pay;
|
||||
|
||||
import com.wechat.pay.java.service.refund.model.Refund;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RefundInfoRsqDTO {
|
||||
|
||||
private boolean isSuccess;
|
||||
|
||||
private String msg;
|
||||
|
||||
private Refund refund;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package cn.xf.basedemo.common.model.pay;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TransferQueryVO {
|
||||
|
||||
private boolean isSuccess;
|
||||
private String resCode;
|
||||
private String resCodeDes;
|
||||
private WechatCashQueryVo cashQueryVo;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.xf.basedemo.common.model.pay;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Data
|
||||
public class WechatCashQueryVo {
|
||||
private String appid;
|
||||
private String batch_id;
|
||||
private String detail_id;
|
||||
private String detail_status;
|
||||
private OffsetDateTime initiate_time;
|
||||
private String mchid;
|
||||
private String openid;
|
||||
private String out_batch_no;
|
||||
private String out_detail_no;
|
||||
private long transfer_amount;
|
||||
private String transfer_remark;
|
||||
private OffsetDateTime update_time;
|
||||
private String user_name;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package cn.xf.basedemo.common.model.pay;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class WxPayTransRsqVo {
|
||||
private boolean success = false;
|
||||
private String code;
|
||||
private String msg;
|
||||
|
||||
}
|
||||
499
src/main/java/cn/xf/basedemo/common/utils/EsUtil.java
Normal file
499
src/main/java/cn/xf/basedemo/common/utils/EsUtil.java
Normal file
@@ -0,0 +1,499 @@
|
||||
package cn.xf.basedemo.common.utils;
|
||||
|
||||
import cn.xf.basedemo.common.model.EsBaseModel;
|
||||
import cn.xf.basedemo.common.model.EsSearchModel;
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch._types.*;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.*;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
import co.elastic.clients.elasticsearch.core.search.*;
|
||||
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
|
||||
import co.elastic.clients.json.JsonData;
|
||||
import co.elastic.clients.transport.endpoints.BooleanResponse;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* packageName cn.xf.basedemo.common.utils
|
||||
* @author remaindertime
|
||||
* @className EsUtil
|
||||
* @date 2024/12/10
|
||||
* @description elasticsearch工具类
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EsUtil {
|
||||
|
||||
public static ElasticsearchClient esClient;
|
||||
|
||||
{
|
||||
esClient = (ElasticsearchClient) ApplicationContextUtils.getBean("elasticsearchClient");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断索引是否存在
|
||||
* @param indexName
|
||||
* @return
|
||||
*/
|
||||
public static boolean existIndex(String indexName) {
|
||||
try {
|
||||
// 创建 ExistsRequest 请求
|
||||
ExistsRequest request = new ExistsRequest.Builder()
|
||||
.index(indexName)
|
||||
.build();
|
||||
// 发送请求并获取响应
|
||||
BooleanResponse response = esClient.indices().exists(request);
|
||||
// 返回索引是否存在
|
||||
return response.value();
|
||||
} catch (Exception e) {
|
||||
// 处理异常
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除索引
|
||||
*
|
||||
* @param indexName
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static void delIndex(String indexName) {
|
||||
if (existIndex(indexName)) {
|
||||
return;
|
||||
}
|
||||
esClient.indices().delete(d -> d.index(indexName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建索引
|
||||
*
|
||||
* @param indexName
|
||||
* @return
|
||||
*/
|
||||
public static void createIndex(String indexName) {
|
||||
if (existIndex(indexName)) {
|
||||
throw new RuntimeException("索引已经存在");
|
||||
}
|
||||
try {
|
||||
CreateIndexResponse createIndexResponse = esClient.indices().create(c -> c.index(indexName));
|
||||
// 处理响应
|
||||
if (createIndexResponse.acknowledged()) {
|
||||
log.info(" indexed create successfully.");
|
||||
} else {
|
||||
log.info("Failed to create index.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 捕获异常并打印详细错误信息
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("创建索引失败,索引名:" + indexName + ",错误信息:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增文档
|
||||
* @param esBaseModel
|
||||
* @return
|
||||
*/
|
||||
public static boolean addDocument(EsBaseModel esBaseModel) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String jsonString = objectMapper.writeValueAsString(esBaseModel.getDocumentModel());
|
||||
log.info("es新增文档,文档内容:{}", jsonString);
|
||||
// 创建 IndexRequest 实例
|
||||
IndexRequest request = new IndexRequest.Builder()
|
||||
.index(esBaseModel.getIndexName())
|
||||
.id(esBaseModel.getDocumentId()) //指定文档id,不指定会自动生成
|
||||
.document(esBaseModel.getDocumentModel())
|
||||
.opType(OpType.Create) // 只会在文档 ID 不存在时创建文档
|
||||
.build();
|
||||
|
||||
IndexResponse response = esClient.index(request);
|
||||
if ("created".equals(response.result())) {
|
||||
log.info("Document created: " + response.id());
|
||||
return true;
|
||||
} else {
|
||||
log.info("Document already exists or failed to create.");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("es新增文档失败", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文档
|
||||
* @param esBaseModel
|
||||
* @return
|
||||
*/
|
||||
public boolean updateDocument(EsBaseModel esBaseModel) {
|
||||
try {
|
||||
UpdateRequest updateRequest = new UpdateRequest.Builder<>()
|
||||
.index(esBaseModel.getIndexName())
|
||||
.id(esBaseModel.getDocumentId())
|
||||
.doc(esBaseModel.getDocumentModel()).build();
|
||||
UpdateResponse updateResponse = esClient.update(updateRequest, esBaseModel.getClazz());
|
||||
log.info("Document updated: " + updateResponse.id());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文档指定字段(script 脚本)
|
||||
* @param esBaseModel
|
||||
* @param script 脚本内容
|
||||
* @param params 传递参数内容
|
||||
*/
|
||||
public void updateDocumentWithScript(EsBaseModel esBaseModel, String script, Map<String, JsonData> params) {
|
||||
try {
|
||||
UpdateRequest updateRequest = new UpdateRequest.Builder<>()
|
||||
.index(esBaseModel.getIndexName())
|
||||
.id(esBaseModel.getDocumentId())
|
||||
.script(s ->
|
||||
s.source(script)// 脚本内容:.source("ctx._source.age += params.increment")
|
||||
.params(params)) // 传递参数内容:.params("increment",sonData.of(5))
|
||||
.build();
|
||||
UpdateResponse updateResponse = esClient.update(updateRequest, esBaseModel.getClazz());
|
||||
log.info("Document updated: " + updateResponse.id());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id查询文档
|
||||
* @param esBaseModel
|
||||
* @return
|
||||
*/
|
||||
public static <T> T getDocumentById(EsBaseModel esBaseModel) {
|
||||
try {
|
||||
GetRequest getRequest = new GetRequest.Builder()
|
||||
.index(esBaseModel.getIndexName())
|
||||
.id(esBaseModel.getDocumentId())
|
||||
.build();
|
||||
GetResponse<T> getResponse = esClient.get(getRequest, esBaseModel.getClazz());
|
||||
if (getResponse.found()) {
|
||||
return getResponse.source();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("es列表查询失败", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文档列表
|
||||
* @param searchModel
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> getDocumentList(EsSearchModel searchModel) {
|
||||
List<T> eslist = new ArrayList<>();
|
||||
try {
|
||||
SearchResponse<T> search = esClient.search(buildSearchRequest(searchModel), searchModel.getClazz());
|
||||
if (Objects.isNull(search)) {
|
||||
return eslist;
|
||||
}
|
||||
HitsMetadata<T> hits = search.hits();
|
||||
if (Objects.isNull(hits)) {
|
||||
return eslist;
|
||||
}
|
||||
List<Hit<T>> sourceHitList = hits.hits();
|
||||
if (CollectionUtils.isEmpty(sourceHitList)) {
|
||||
return eslist;
|
||||
}
|
||||
sourceHitList.forEach(item -> {
|
||||
// 处理每个命中
|
||||
eslist.add(item.source());
|
||||
});
|
||||
return eslist;
|
||||
} catch (Exception e) {
|
||||
log.error("es列表查询失败", e);
|
||||
}
|
||||
return eslist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文档数量
|
||||
* @param searchModel
|
||||
* @return
|
||||
*/
|
||||
public static long getDocumentCount(EsSearchModel searchModel) {
|
||||
try {
|
||||
CountRequest.Builder countRequest = new CountRequest.Builder();
|
||||
countRequest.index(searchModel.getIndexName());
|
||||
countRequest.query(createBoolQuery(searchModel.getTermQuery(), searchModel.getMatchQuery()));
|
||||
CountResponse count = esClient.count(countRequest.build());
|
||||
if (Objects.isNull(count)) {
|
||||
log.info("es列表数量查询异常{}", searchModel);
|
||||
return 0;
|
||||
}
|
||||
return count.count();
|
||||
} catch (Exception e) {
|
||||
log.error("es列表数量查询失败", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id删除文档
|
||||
* @param esBaseModel
|
||||
* @return
|
||||
*/
|
||||
public static Boolean deleteDocumentById(EsBaseModel esBaseModel) {
|
||||
try {
|
||||
DeleteRequest deleteRequest = new DeleteRequest.Builder()
|
||||
.index(esBaseModel.getDocumentId())
|
||||
.id(esBaseModel.getDocumentId())
|
||||
.build();
|
||||
DeleteResponse deleteResponse = esClient.delete(deleteRequest);
|
||||
if ("deleted".equals(deleteResponse.result())) {
|
||||
log.info("Document deleted: " + deleteResponse.id());
|
||||
return true;
|
||||
} else {
|
||||
log.info("Document delete failed: " + deleteResponse.id());
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("es列表删除失败", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件删除文档
|
||||
* @param searchModel
|
||||
* @return 删除数量
|
||||
*/
|
||||
public static long deleteDocumentByQuery(EsSearchModel searchModel) {
|
||||
try {
|
||||
DeleteByQueryRequest.Builder deleteRequest = new DeleteByQueryRequest.Builder();
|
||||
deleteRequest.index(searchModel.getIndexName());
|
||||
deleteRequest.query(createBoolQuery(searchModel.getTermQuery(), searchModel.getMatchQuery()));
|
||||
deleteRequest.refresh(true); //设置删除操作后是否立即刷新索引,使删除结果立即可见
|
||||
deleteRequest.timeout(new Time.Builder().time("2s").build()); //设置删除操作的超时时间
|
||||
deleteRequest.conflicts(Conflicts.Proceed); //Conflicts.Proceed:在版本冲突时继续删除操作;Conflicts.Abort:在版本冲突时中止删除操作
|
||||
DeleteByQueryResponse dResponse = esClient.deleteByQuery(deleteRequest.build());
|
||||
if (Objects.nonNull(dResponse)) {
|
||||
log.info("es条件删除成功,删除数量:{}", dResponse.deleted());
|
||||
return dResponse.deleted();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("es条件删除数据失败", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建搜索请求对象
|
||||
* @param searchModel
|
||||
* @return
|
||||
*/
|
||||
private static SearchRequest buildSearchRequest(EsSearchModel searchModel) {
|
||||
//定义查询对象
|
||||
SearchRequest.Builder searchRequest = new SearchRequest.Builder();
|
||||
//设置索引名称
|
||||
searchRequest.index(searchModel.getIndexName());
|
||||
//分组去重
|
||||
if (StringUtils.isNotBlank(searchModel.getRepeatField())) {
|
||||
searchRequest.collapse(buildCollapse(searchModel));
|
||||
}
|
||||
//设置查询条件
|
||||
searchRequest.query(createBoolQuery(searchModel.getTermQuery(), searchModel.getMatchQuery()));
|
||||
//设置排序规则
|
||||
if (searchModel.getSort() != null) {
|
||||
searchRequest.sort(buildSort(searchModel.getSort()));
|
||||
}
|
||||
//设置分页参数
|
||||
if (searchModel.getPageSize() != null && searchModel.getPageSize() != null) {
|
||||
searchRequest.from(searchModel.getPageSize() * (searchModel.getPageNum() - 1));
|
||||
searchRequest.size(searchModel.getPageSize());
|
||||
}
|
||||
//设置查询字段/排查字段
|
||||
SourceConfig sourceConfig = buildSourceConfig(searchModel.getIncludes(), searchModel.getExcludes());
|
||||
if (Objects.nonNull(sourceConfig)) {
|
||||
searchRequest.source(sourceConfig);
|
||||
}
|
||||
return searchRequest.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建查询条件
|
||||
* @param termQuery
|
||||
* @param matchQuery
|
||||
* @return
|
||||
*/
|
||||
private static Query createBoolQuery(Map<String, Object> termQuery, Map<String, Object> matchQuery) {
|
||||
BoolQuery.Builder cQuery = new BoolQuery.Builder();
|
||||
// TermQuery 精准匹配
|
||||
if (termQuery != null) {
|
||||
for (Map.Entry<String, Object> entry : termQuery.entrySet()) {
|
||||
if (Objects.isNull(entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value.getClass().isArray()) { //数组查询,使用 TermsQuery
|
||||
Object[] values = (Object[]) entry.getValue();
|
||||
List<FieldValue> objs = Arrays.stream(values)
|
||||
.map(v -> FieldValue.of(v)) // 将每个对象转换为 FieldValue
|
||||
.collect(Collectors.toList());
|
||||
cQuery.must(new TermsQuery.Builder()
|
||||
.field(key)
|
||||
.terms(t -> t.value(objs))
|
||||
.build()
|
||||
._toQuery());
|
||||
} else if (value.toString().contains(" ")) { // 短语查询,使用 MatchPhraseQuery (要严格按照单词顺序字符串中有空格,短信需匹配)
|
||||
cQuery.must(new MatchPhraseQuery.Builder()
|
||||
.field(key)
|
||||
.query(value.toString())
|
||||
.build()
|
||||
._toQuery());
|
||||
} else { // 其他情况,使用 TermQuery 精准匹配
|
||||
cQuery.must(new TermQuery.Builder()
|
||||
.field(key)
|
||||
.value(value.toString())
|
||||
.build()
|
||||
._toQuery());
|
||||
}
|
||||
}
|
||||
}
|
||||
// MatchQuery 模糊匹配全文检索分词查询
|
||||
if (matchQuery != null) {
|
||||
for (Map.Entry<String, Object> entry : matchQuery.entrySet()) {
|
||||
if (Objects.isNull(entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
cQuery.must(new MatchQuery.Builder()
|
||||
.field(entry.getKey())
|
||||
.query(entry.getValue().toString())
|
||||
.build()
|
||||
._toQuery());
|
||||
}
|
||||
}
|
||||
return cQuery.build()._toQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建时间区间查询
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param fieldName 时间字段
|
||||
* @return
|
||||
*/
|
||||
public static Query createTimeQuery(String startTime, String endTime, String fieldName) {
|
||||
DateRangeQuery dataQuery = new DateRangeQuery.Builder()
|
||||
.field(fieldName)
|
||||
.build();
|
||||
// 时间区间查询
|
||||
dataQuery.of(o -> o.gte(startTime));
|
||||
dataQuery.of(o -> o.lte(endTime));
|
||||
return dataQuery._toRangeQuery()._toQuery();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置查询字段/排查字段
|
||||
* @param includes 需要字段
|
||||
* @param excludes 排除字段
|
||||
* @return
|
||||
*/
|
||||
private static SourceConfig buildSourceConfig(List<String> includes, List<String> excludes) {
|
||||
boolean isIncludes = CollectionUtils.isEmpty(includes);
|
||||
boolean isExcludes = CollectionUtils.isEmpty(excludes);
|
||||
//设置查询字段/排查字段
|
||||
if (isIncludes || isExcludes) {
|
||||
SourceFilter.Builder sourceFilter = new SourceFilter.Builder();
|
||||
if (isIncludes)
|
||||
sourceFilter.includes(includes);
|
||||
if (isExcludes)
|
||||
sourceFilter.excludes(excludes);
|
||||
return new SourceConfig.Builder().filter(sourceFilter.build()).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建分组去重
|
||||
* @param searchModel
|
||||
* @return
|
||||
*/
|
||||
private static FieldCollapse buildCollapse(EsSearchModel searchModel) {
|
||||
FieldCollapse.Builder fieldCollapse = new FieldCollapse.Builder();
|
||||
//设置分组字段
|
||||
fieldCollapse.field(searchModel.getRepeatField());
|
||||
//设置嵌套配置
|
||||
if (StringUtils.isNotBlank(searchModel.getInnerAlias())) {
|
||||
InnerHits.Builder innerHits = new InnerHits.Builder();
|
||||
//设置别名
|
||||
innerHits.name(searchModel.getInnerAlias());
|
||||
//设置查询数量
|
||||
if (searchModel.getInnerSize() != null) {
|
||||
innerHits.size(searchModel.getInnerSize());
|
||||
}
|
||||
fieldCollapse.innerHits(InnerHits.of(i -> i.name(searchModel.getInnerAlias()).size(10)));
|
||||
}
|
||||
return fieldCollapse.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建排序规则
|
||||
* @param sortMap
|
||||
* @return
|
||||
*/
|
||||
private static List<SortOptions> buildSort(Map<String, String> sortMap) {
|
||||
if (sortMap == null) {
|
||||
return null;
|
||||
}
|
||||
List<SortOptions> sortList = new ArrayList<>();
|
||||
for (Map.Entry<String, String> sort : sortMap.entrySet()) {
|
||||
sortList.add(new SortOptions.Builder().field(f -> f.field(sort.getKey()).order(SortOrder.valueOf(sort.getValue()))).build());
|
||||
}
|
||||
return sortList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 案例:组合多条件查询(关于 must、mustNot、should 条件的使用)
|
||||
*/
|
||||
public Query combinationQueryTest() {
|
||||
//query.must():and 文档必须满足该条件,如果不满足,文档将不匹配。 and
|
||||
//query.should():or 文档可以不满足该条件,但满足该条件时会得分更高;即使不满足,文档也会出现在查询结果中,只是查询结果靠后。
|
||||
|
||||
//场景1:文档必须符合所有 must 条件和 mustNot 条件,同时至少满足一个 should 条件。如果 should 条件都不满足,文档将被排除不查询出来。
|
||||
BoolQuery.Builder query = new BoolQuery.Builder();
|
||||
//数字范围查询
|
||||
NumberRangeQuery.Builder numberQuery = new NumberRangeQuery.Builder();
|
||||
numberQuery.field("age").lte(30.0).build();
|
||||
// 构建查询条件
|
||||
query.must(o -> o.term(t -> t.field("status").value("active"))) // 必须满足的条件
|
||||
.mustNot(o -> o.term(t -> t.field("country").value("China"))) // 不能满足的条件
|
||||
.filter(f -> f.bool(bo -> bo
|
||||
.should(so -> so.range(r -> r.number(numberQuery.build()))) // 至少满足一个 should 条件
|
||||
.should(so -> so.term(t -> t.field("gender").value("male"))) // 至少满足一个 should 条件
|
||||
.minimumShouldMatch("1") // 至少满足一个 should 条件 也可设置百分比 “50%”
|
||||
));
|
||||
//场景2:文档必须符合所有 must 条件和 mustNot 条件,同时至少满足一个 should 条件。如果 should 条件都不满足,不用做额外的过滤(按照should原生特性处理)。
|
||||
query.must(o -> o.bool(bo -> bo
|
||||
.should(so -> so.range(r -> r.number(numberQuery.build()))) // 至少满足一个 should 条件
|
||||
.should(so -> so.term(t -> t.field("gender").value("male"))) // 至少满足一个 should 条件
|
||||
.minimumShouldMatch("1") // 至少满足一个 should 条件
|
||||
))
|
||||
.must(o -> o.term(t -> t.field("status").value("active"))) // 必须满足的条件
|
||||
.mustNot(o -> o.term(t -> t.field("country").value("China"))); // 不能满足的条件
|
||||
|
||||
return query.build()._toQuery();
|
||||
}
|
||||
|
||||
}
|
||||
29
src/main/java/cn/xf/basedemo/common/utils/StringUtil.java
Normal file
29
src/main/java/cn/xf/basedemo/common/utils/StringUtil.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package cn.xf.basedemo.common.utils;
|
||||
|
||||
/**
|
||||
* packageName cn.xf.basedemo.common.utils
|
||||
* @author remaindertime
|
||||
* @className StringUtil
|
||||
* @date 2024/12/11
|
||||
* @description 字符串工具类
|
||||
*/
|
||||
public class StringUtil {
|
||||
|
||||
/**
|
||||
* 驼峰命名法转下划线命名法
|
||||
*
|
||||
* @param camelCase 驼峰命名法字符串
|
||||
* @return 下划线命名法字符串
|
||||
*/
|
||||
public static String camelToKebabCase(String camelCase) {
|
||||
if (camelCase == null || camelCase.isEmpty()) {
|
||||
return camelCase;
|
||||
}
|
||||
|
||||
// 使用正则表达式将大写字母前插入一个"-"
|
||||
String result = camelCase.replaceAll("([a-z])([A-Z])", "$1-$2");
|
||||
|
||||
// 转换为小写
|
||||
return result.toLowerCase();
|
||||
}
|
||||
}
|
||||
210
src/main/java/cn/xf/basedemo/common/utils/pay/AliPayUtil.java
Normal file
210
src/main/java/cn/xf/basedemo/common/utils/pay/AliPayUtil.java
Normal file
@@ -0,0 +1,210 @@
|
||||
package cn.xf.basedemo.common.utils.pay;
|
||||
|
||||
import cn.xf.basedemo.common.model.pay.AlipayTransRsqVo;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.AlipayClient;
|
||||
import com.alipay.api.CertAlipayRequest;
|
||||
import com.alipay.api.DefaultAlipayClient;
|
||||
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
|
||||
import com.alipay.api.domain.Participant;
|
||||
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
|
||||
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
|
||||
import lombok.Data;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
|
||||
@Slf4j
|
||||
public class AliPayUtil {
|
||||
|
||||
//支付路径
|
||||
private static String payPath;
|
||||
private static PayInfo payInfo;
|
||||
private static String gatewayUrl = "https://openapi.alipay.com/gateway.do";//"https://openapi.alipaydev.com/gateway.do";
|
||||
private static String format = "json";
|
||||
private static String charset = "utf-8";
|
||||
private static String signType = "RSA2";
|
||||
|
||||
@Data
|
||||
static class PayInfo {
|
||||
String appId;
|
||||
String appCertPath;
|
||||
String alipayCertPath;
|
||||
String rootCertPath;
|
||||
String rsaPrivateKey;
|
||||
}
|
||||
|
||||
public AliPayUtil(String path) {
|
||||
payPath = path;
|
||||
String path1 = "d:/pay/";
|
||||
path1 = "";
|
||||
if (path.equals("a")) {
|
||||
payInfo = new PayInfo();
|
||||
payInfo.setAppId("");
|
||||
payInfo.setAppCertPath(path1 + "15080.crt");
|
||||
payInfo.setAlipayCertPath(path1 + "alipayCertPublicKey_RSA2.crt");
|
||||
payInfo.setRootCertPath(path1 + "alipayRootCert.crt");
|
||||
payInfo.setRsaPrivateKey(readFileContent(path1 + "private.key"));
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public AlipayTransRsqVo transfer(String trans_no, String account, BigDecimal money, int type, String trueName, String mark) {
|
||||
AlipayTransRsqVo resVo = new AlipayTransRsqVo();
|
||||
// 创建API客户端实例
|
||||
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
|
||||
certAlipayRequest.setServerUrl(gatewayUrl);
|
||||
certAlipayRequest.setAppId(payInfo.appId);
|
||||
certAlipayRequest.setPrivateKey(payInfo.rsaPrivateKey);
|
||||
certAlipayRequest.setFormat(format);
|
||||
certAlipayRequest.setCharset(charset);
|
||||
certAlipayRequest.setSignType(signType);
|
||||
//设置应用公钥证书路径
|
||||
certAlipayRequest.setCertPath(payInfo.getAppCertPath());
|
||||
//设置支付宝公钥证书路径
|
||||
certAlipayRequest.setAlipayPublicCertPath(payInfo.alipayCertPath);
|
||||
//certAlipayRequest.setAlipayPublicCertContent(AlipaySignature.getAlipayPublicKey(payInfo.alipayCertPath));
|
||||
//设置支付宝根证书路径
|
||||
certAlipayRequest.setRootCertPath(payInfo.getRootCertPath());
|
||||
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
|
||||
|
||||
|
||||
/** 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称alipay.fund.trans.uni.transfer(单笔转账接口) **/
|
||||
AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
|
||||
|
||||
AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
|
||||
|
||||
/******必传参数******/
|
||||
// 商户端的唯一订单号,对于同一笔转账请求,商户需保证该订单号唯一
|
||||
model.setOutBizNo(trans_no);
|
||||
|
||||
// 转账金额,TRANS_ACCOUNT_NO_PWD产品取值最低0.1
|
||||
model.setTransAmount(String.valueOf(money.setScale(2, RoundingMode.HALF_UP)));
|
||||
|
||||
// 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD。
|
||||
model.setProductCode("TRANS_ACCOUNT_NO_PWD");
|
||||
|
||||
// 业务场景。单笔无密转账固定为 DIRECT_TRANSFER
|
||||
model.setBizScene("DIRECT_TRANSFER");
|
||||
|
||||
// 转账业务的标题,用于在支付宝用户的账单里显示。
|
||||
model.setOrderTitle("订单标题");
|
||||
|
||||
// 收款方信息
|
||||
Participant payeeInfo = new Participant();
|
||||
// 参与方的标识类型,设置ALIPAY_USER_ID或者ALIPAY_LOGON_ID
|
||||
// ALIPAY_USER_ID:支付宝会员的用户 ID,可通过 获取会员信息 获取:https://opendocs.alipay.com/open/284/106000
|
||||
// ALIPAY_LOGON_ID:支付宝登录号,支持邮箱和手机号格式。
|
||||
if (type == 1) {
|
||||
payeeInfo.setIdentityType("ALIPAY_USER_ID");
|
||||
}
|
||||
if (type == 2) {
|
||||
payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
|
||||
}
|
||||
|
||||
// 参与方的标识 ID,根据identity_type类型选择对应信息
|
||||
// 当 identity_type=ALIPAY_USER_ID 时,填写支付宝用户 UID。示例值:2088123412341234。
|
||||
// 当 identity_type=ALIPAY_LOGON_ID 时,填写支付宝登录号。示例值:186xxxxxxxx。
|
||||
payeeInfo.setIdentity(account);
|
||||
// 参与方真实姓名。如果非空,将校验收款支付宝账号姓名一致性。
|
||||
// 当 identity_type=ALIPAY_LOGON_ID 时,本字段必填。
|
||||
payeeInfo.setName(trueName);
|
||||
model.setPayeeInfo(payeeInfo);
|
||||
|
||||
/******可选参数******/
|
||||
// 业务备注
|
||||
model.setRemark(mark);
|
||||
|
||||
// 转账业务请求的扩展参数
|
||||
// payer_show_name_use_alias:是否展示付款方别名,可选,收款方在支付宝账单中可见。枚举支持:
|
||||
// * true:展示别名,将展示商家支付宝在商家中心 商户信息 > 商户基本信息 页面配置的 商户别名。
|
||||
// * false:不展示别名。默认为 false。
|
||||
// model.setBusinessParams("{\"payer_show_name_use_alias\":\"true\"}");
|
||||
|
||||
request.setBizModel(model);
|
||||
|
||||
try {
|
||||
// 发送请求并获取响应
|
||||
// AlipayFundTransToaccountTransferResponse response = alipayClient.certificateExecute(request);
|
||||
AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);
|
||||
// 处理响应,通常需要检查out_biz_no,trade_no,和resultCode
|
||||
if (response.isSuccess()) {
|
||||
resVo.setSuccess(true);
|
||||
resVo.setMsg(response.getSubMsg());
|
||||
//resVo.setBody(JSON.parseObject(response.getBody(), AliPayBodyVo.class));
|
||||
resVo.setOrderId(response.getOrderId());
|
||||
resVo.setStatus(response.getStatus());
|
||||
resVo.setPayFundOrderID(response.getPayFundOrderId());
|
||||
resVo.setTransDate(response.getTransDate());
|
||||
resVo.setOutBizNo(response.getOutBizNo());
|
||||
resVo.setSettleSerialNo(response.getSettleSerialNo());
|
||||
resVo.setAmount(response.getAmount());
|
||||
|
||||
|
||||
log.info("转账成功: " + response.getBody());
|
||||
} else {
|
||||
resVo.setSuccess(false);
|
||||
resVo.setMsg(response.getSubMsg());
|
||||
resVo.setCode(response.getCode());
|
||||
resVo.setSubCode(response.getSubCode());
|
||||
log.info("转账失败: " + response.getSubCode() + " - " + response.getSubMsg());
|
||||
}
|
||||
} catch (AlipayApiException e) {
|
||||
//e.printStackTrace();
|
||||
resVo.setSuccess(false);
|
||||
resVo.setMsg(e.getMessage());
|
||||
}
|
||||
|
||||
return resVo;
|
||||
}
|
||||
|
||||
public static PrivateKey getPrivateKeyFromPath(String path) {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream(path);
|
||||
byte[] keyBytes = new byte[fis.available()];
|
||||
fis.read(keyBytes);
|
||||
fis.close();
|
||||
|
||||
String privateKeyPEM = new String(keyBytes);
|
||||
// 移除PEM头部和尾部
|
||||
privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----", "");
|
||||
privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
|
||||
// 对于Windows平台,请使用"\r\n"作为分隔符;对于Linux/Mac平台,应使用"\n"
|
||||
privateKeyPEM = privateKeyPEM.replaceAll("\\r\\n|\\n|\\r", "");
|
||||
|
||||
byte[] decoded = Base64.decodeBase64(privateKeyPEM);
|
||||
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
|
||||
return kf.generatePrivate(keySpec);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String readFileContent(String filePath) {
|
||||
StringBuilder content = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
content.append(line).append("\n");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
}
|
||||
270
src/main/java/cn/xf/basedemo/common/utils/pay/WxPayUtil.java
Normal file
270
src/main/java/cn/xf/basedemo/common/utils/pay/WxPayUtil.java
Normal file
@@ -0,0 +1,270 @@
|
||||
package cn.xf.basedemo.common.utils.pay;
|
||||
|
||||
import cn.xf.basedemo.common.model.pay.RefundInfoRsqDTO;
|
||||
import cn.xf.basedemo.common.model.pay.TransferQueryVO;
|
||||
import cn.xf.basedemo.common.model.pay.WechatCashQueryVo;
|
||||
import cn.xf.basedemo.common.model.pay.WxPayTransRsqVo;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.wechat.pay.java.core.Config;
|
||||
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||
import com.wechat.pay.java.core.exception.HttpException;
|
||||
import com.wechat.pay.java.core.exception.MalformedMessageException;
|
||||
import com.wechat.pay.java.core.exception.ServiceException;
|
||||
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.QueryByOutRefundNoRequest;
|
||||
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.*;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class WxPayUtil {
|
||||
|
||||
private static String payPath;
|
||||
private static String appId;
|
||||
private static String wxMerchantId;
|
||||
private static String wxApiV3Key;
|
||||
private static String wxApiSerialNo;
|
||||
private static String wxMerchantApiCertificate;
|
||||
private static String wxMerchantApiPrivateKey;
|
||||
private static String plantSerialNo;
|
||||
private static String notifyUrl; //通知URL
|
||||
private static String refundUrl; //退款充值URL
|
||||
|
||||
public WxPayUtil(String path, int type) {
|
||||
payPath = path;
|
||||
type = type;
|
||||
String path1 = "d:/pay";
|
||||
path1 = "";
|
||||
refundUrl = "" + payPath;
|
||||
switch (payPath) {
|
||||
case "a"://提现打款用-文撩
|
||||
appId = "";
|
||||
wxMerchantId = "";
|
||||
wxApiV3Key = "";
|
||||
wxApiSerialNo = "";
|
||||
wxMerchantApiCertificate = path1 + "9DE56.pem";
|
||||
wxMerchantApiPrivateKey = path1 + "apiclient_key.pem";
|
||||
plantSerialNo = "1A1D8C3A474A29DE56";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public WxPayTransRsqVo transfer(String trans_no, String account, BigDecimal money, String trueName, String mark) {
|
||||
WxPayTransRsqVo rsqVo = new WxPayTransRsqVo();
|
||||
rsqVo.setSuccess(false);
|
||||
log.info("开始退款,{}-{}-{}-{}-{}", wxMerchantId, wxMerchantApiPrivateKey, wxApiSerialNo, wxApiV3Key, money);
|
||||
Config config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(wxMerchantId)
|
||||
.privateKeyFromPath(wxMerchantApiPrivateKey)
|
||||
.merchantSerialNumber(wxApiSerialNo)
|
||||
.apiV3Key(wxApiV3Key)
|
||||
.build();
|
||||
|
||||
TransferBatchService service = new TransferBatchService.Builder().config(config).build();
|
||||
//数据封装
|
||||
InitiateBatchTransferRequest initiateBatchTransferRequest = new InitiateBatchTransferRequest();
|
||||
initiateBatchTransferRequest.setAppid(appId);
|
||||
initiateBatchTransferRequest.setOutBatchNo(trans_no);
|
||||
initiateBatchTransferRequest.setBatchName(mark);
|
||||
initiateBatchTransferRequest.setBatchRemark(mark);
|
||||
initiateBatchTransferRequest.setTotalAmount(money.multiply(BigDecimal.valueOf(100)).longValue());
|
||||
initiateBatchTransferRequest.setTotalNum(1);
|
||||
|
||||
//initiateBatchTransferRequest.setTransferSceneId("1001");
|
||||
{
|
||||
List<TransferDetailInput> transferDetailListList = new ArrayList<>();
|
||||
{
|
||||
TransferDetailInput transferDetailInput = new TransferDetailInput();
|
||||
transferDetailInput.setTransferAmount(money.multiply(BigDecimal.valueOf(100)).longValue());//金额为分 需要乘以100
|
||||
transferDetailInput.setOutDetailNo(trans_no);
|
||||
transferDetailInput.setOpenid(account);
|
||||
transferDetailInput.setUserName(trueName);
|
||||
transferDetailInput.setTransferRemark(mark);
|
||||
transferDetailListList.add(transferDetailInput);
|
||||
}
|
||||
initiateBatchTransferRequest.setTransferDetailList(
|
||||
transferDetailListList);
|
||||
}
|
||||
//发起商家转账
|
||||
InitiateBatchTransferResponse response;
|
||||
try {
|
||||
response = service.initiateBatchTransfer(initiateBatchTransferRequest);
|
||||
log.info("转账:", response.toString());
|
||||
// if (response.getBatchStatus().equals("ACCEPTED")) {
|
||||
// log.info("initiateBatchTransfer:", response.getBatchStatus());
|
||||
// }
|
||||
// log.error("initiateBatchTransfer:", response.getBatchStatus());
|
||||
rsqVo.setSuccess(true);
|
||||
|
||||
} catch (ServiceException e) {
|
||||
log.info("出错了:", e.getErrorMessage());
|
||||
// e.printStackTrace();
|
||||
|
||||
|
||||
rsqVo.setCode(e.getErrorCode());
|
||||
rsqVo.setMsg(e.getErrorMessage());
|
||||
}
|
||||
|
||||
|
||||
return rsqVo;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public TransferQueryVO transferQuery(String outBatchNo, String outDetailNo) {
|
||||
TransferQueryVO transferQueryVO = new TransferQueryVO();
|
||||
//商家转账批次单号
|
||||
//商家转账明细单号
|
||||
transferQueryVO.setSuccess(false);
|
||||
Config config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(wxMerchantId)
|
||||
.privateKeyFromPath(wxMerchantApiPrivateKey)
|
||||
.merchantSerialNumber(wxApiSerialNo)
|
||||
.apiV3Key(wxApiV3Key)
|
||||
.build();
|
||||
try {
|
||||
TransferBatchService transferBatchService = new TransferBatchService.Builder().config(config).build();
|
||||
GetTransferDetailByOutNoRequest request = new GetTransferDetailByOutNoRequest();
|
||||
request.setOutBatchNo(outBatchNo);
|
||||
request.setOutDetailNo(outDetailNo);
|
||||
TransferDetailEntity response = transferBatchService.getTransferDetailByOutNo(request);
|
||||
log.info("转账返回信息:{}", JSON.toJSONString(response));
|
||||
transferQueryVO.setCashQueryVo(JSON.parseObject(JSON.toJSONString(response), WechatCashQueryVo.class));
|
||||
transferQueryVO.setResCode(response.getDetailStatus());
|
||||
transferQueryVO.setResCodeDes(response.getDetailStatus());
|
||||
if ("FAIL".equals(response.getDetailStatus())) {
|
||||
// transferQueryVO.setResCodeDes(getErrorMsg(String.valueOf(response.getFailReason())));
|
||||
transferQueryVO.setSuccess(false);
|
||||
} else {
|
||||
transferQueryVO.setSuccess(true);
|
||||
}
|
||||
|
||||
log.info("转账返回信息II:{}", JSON.toJSONString(transferQueryVO));
|
||||
} catch (HttpException e) { // 发送HTTP请求失败
|
||||
// 调用e.getHttpRequest()获取请求打印日志或上报监控,更多方法见HttpException定义
|
||||
log.error(e.getMessage());
|
||||
transferQueryVO.setResCodeDes(e.getMessage());
|
||||
// throw new RuntimeException("微信转账失败");
|
||||
} catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
|
||||
// 调用e.getResponseBody()获取返回体打印日志或上报监控,更多方法见ServiceException定义
|
||||
log.error(e.getMessage());
|
||||
transferQueryVO.setResCodeDes(e.getMessage());
|
||||
//throw new RuntimeException("微信转账失败");
|
||||
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
|
||||
// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
|
||||
log.error(e.getMessage());
|
||||
transferQueryVO.setResCodeDes(e.getMessage());
|
||||
//throw new RuntimeException("微信转账失败");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
// e.printStackTrace();
|
||||
transferQueryVO.setResCodeDes(e.getMessage());
|
||||
//throw new RuntimeException("微信转账失败");
|
||||
}
|
||||
|
||||
return transferQueryVO;
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public RefundInfoRsqDTO refundUser(String outTradeNo, String outRefundNo, long totalFee, BigDecimal refundFee) {
|
||||
RefundInfoRsqDTO refundInfoRsqDTO = new RefundInfoRsqDTO();
|
||||
refundInfoRsqDTO.setSuccess(false);
|
||||
Config config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(wxMerchantId)
|
||||
.privateKeyFromPath(wxMerchantApiPrivateKey)
|
||||
.merchantSerialNumber(wxApiSerialNo)
|
||||
.apiV3Key(wxApiV3Key)
|
||||
.build();
|
||||
|
||||
RefundService service = new RefundService.Builder().config(config).build();
|
||||
CreateRequest request = new CreateRequest();
|
||||
request.setOutTradeNo(outTradeNo);
|
||||
request.setOutRefundNo(outRefundNo);
|
||||
request.setNotifyUrl(refundUrl);
|
||||
AmountReq amount = new AmountReq();
|
||||
amount.setRefund(refundFee.longValue());
|
||||
amount.setTotal(totalFee);
|
||||
amount.setCurrency("CNY");
|
||||
request.setAmount(amount);
|
||||
try {
|
||||
Refund refund = service.create(request);
|
||||
log.info("refund:{}", refund.toString());
|
||||
if (refund.getStatus().toString().equals("PROCESSING")) {
|
||||
refundInfoRsqDTO.setSuccess(true);
|
||||
}
|
||||
refundInfoRsqDTO.setRefund(refund);
|
||||
} catch (ServiceException e) {
|
||||
log.error("退款失败:{}------{}-----{}", e.getErrorMessage(), e.getErrorCode(), e.getResponseBody());
|
||||
e.printStackTrace();
|
||||
|
||||
refundInfoRsqDTO.setMsg(e.getErrorMessage());
|
||||
}
|
||||
|
||||
return refundInfoRsqDTO;
|
||||
}
|
||||
|
||||
public static RefundInfoRsqDTO queryRefund(String outRefundNo) {
|
||||
RefundInfoRsqDTO refundInfoRsqDTO = new RefundInfoRsqDTO();
|
||||
refundInfoRsqDTO.setSuccess(false);
|
||||
Config config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(wxMerchantId)
|
||||
.privateKeyFromPath(wxMerchantApiPrivateKey)
|
||||
.merchantSerialNumber(wxApiSerialNo)
|
||||
.apiV3Key(wxApiV3Key)
|
||||
.build();
|
||||
|
||||
RefundService service = new RefundService.Builder().config(config).build();
|
||||
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
|
||||
request.setOutRefundNo(outRefundNo);
|
||||
try {
|
||||
Refund refund = service.queryByOutRefundNo(request);
|
||||
log.info("refund:{}", refund.toString());
|
||||
if (refund.getStatus().toString().equals("SUCCESS")) {
|
||||
refundInfoRsqDTO.setSuccess(true);
|
||||
}
|
||||
refundInfoRsqDTO.setRefund(refund);
|
||||
} catch (ServiceException e) {
|
||||
log.error("查询失败:{}------{}-----{}", e.getErrorMessage(), e.getErrorCode(), e.getResponseBody());
|
||||
e.printStackTrace();
|
||||
|
||||
refundInfoRsqDTO.setMsg(e.getErrorMessage());
|
||||
}
|
||||
|
||||
return refundInfoRsqDTO;
|
||||
}
|
||||
|
||||
// private String getErrorMsg(String errCode) {
|
||||
// return switch (errCode) {
|
||||
// case "ACCOUNT_FROZEN" -> "账户冻结";
|
||||
// case "REAL_NAME_CHECK_FAIL" -> "用户未实名";
|
||||
// case "NAME_NOT_CORRECT" -> "用户姓名校验失败";
|
||||
// case "OPENID_INVALID" -> "Openid校验失败";
|
||||
// case "TRANSFER_QUOTA_EXCEED" -> "超过用户单笔收款额度";
|
||||
// case "DAY_RECEIVED_QUOTA_EXCEED" -> "超过用户单日收款额度";
|
||||
// case "MONTH_RECEIVED_QUOTA_EXCEED" -> "超过用户单月收款额度";
|
||||
// case "DAY_RECEIVED_COUNT_EXCEED" -> "超过用户单日收款次数";
|
||||
// case "PRODUCT_AUTH_CHECK_FAIL" -> "产品权限校验失败";
|
||||
// case "OVERDUE_CLOSE" -> "转账关闭";
|
||||
// case "ID_CARD_NOT_CORRECT" -> "用户身份证校验失败";
|
||||
// case "ACCOUNT_NOT_EXIST" -> "用户账户不存在";
|
||||
// case "TRANSFER_RISK" -> "转账存在风险";
|
||||
// case "REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED" -> "用户账户收款受限,请引导用户在微信支付查看详情";
|
||||
// case "RECEIVE_ACCOUNT_NOT_PERMMIT" -> "未配置该用户为转账收款人";
|
||||
// case "PAYER_ACCOUNT_ABNORMAL" -> "商户账户付款受限,可前往商户平台-违约记录获取解除功能限制指引";
|
||||
// case "PAYEE_ACCOUNT_ABNORMAL" -> "用户账户收款异常,请引导用户完善其在微信支付的身份信息以继续收款";
|
||||
// case "TRANSFER_REMARK_SET_FAIL" -> "转账备注设置失败,请调整对应文案后重新再试";
|
||||
// default -> "内部错误,请联系管理人员";
|
||||
// };
|
||||
// }
|
||||
|
||||
}
|
||||
79
src/main/java/cn/xf/basedemo/config/EsConfig.java
Normal file
79
src/main/java/cn/xf/basedemo/config/EsConfig.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package cn.xf.basedemo.config;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||
import co.elastic.clients.transport.rest_client.RestClientTransport;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.auth.AuthScope;
|
||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||
import org.apache.http.client.CredentialsProvider;
|
||||
import org.apache.http.impl.client.BasicCredentialsProvider;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.client.RestClientBuilder;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* packageName cn.xf.basedemo.config
|
||||
* @author remaindertime
|
||||
* @className ElasticsearchConfig
|
||||
* @date 2024/12/9
|
||||
* @description es工具类
|
||||
*/
|
||||
@Component
|
||||
public class EsConfig {
|
||||
|
||||
@Value("${elasticsearch.host}")
|
||||
private String elasticsearchHost;
|
||||
@Value("${elasticsearch.port}")
|
||||
private int elasticsearchPort;
|
||||
@Value("${elasticsearch.username}")
|
||||
private String username;
|
||||
@Value("${elasticsearch.password}")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
-最大连接数 (maxConnTotal):设置总的最大连接数,取决于业务的并发量。500-2000 之间较为合理。
|
||||
-每个节点的最大连接数 (maxConnPerRoute):控制每个节点的最大连接数,建议 50-100 之间。
|
||||
-IO 线程数 (setIoThreadCount):根据 CPU 核心数设置,通常为 2-4 倍 CPU 核心数。
|
||||
-连接超时、套接字超时、获取连接超时:一般设置为 10-30 秒,复杂查询或大数据量操作可适当增加到 20-60 秒。
|
||||
-失败监听器 (setFailureListener):自定义重试和故障处理逻辑,确保高可用性。
|
||||
*/
|
||||
@Bean
|
||||
public ElasticsearchClient elasticsearchClient() {
|
||||
|
||||
// 创建凭证提供者
|
||||
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
||||
credentialsProvider.setCredentials(
|
||||
AuthScope.ANY,
|
||||
new UsernamePasswordCredentials(username, password)
|
||||
);
|
||||
|
||||
// 自定义 RestClientBuilder 配置
|
||||
RestClientBuilder restClientBuilder = RestClient.builder(
|
||||
new HttpHost(elasticsearchHost, elasticsearchPort, "http")
|
||||
).setHttpClientConfigCallback(httpClientBuilder ->
|
||||
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider) // 配置认证信息
|
||||
);
|
||||
// 配置连接超时、套接字超时、获取连接超时
|
||||
restClientBuilder.setRequestConfigCallback(builder ->
|
||||
builder.setConnectTimeout(20000)
|
||||
.setSocketTimeout(20000)
|
||||
.setConnectionRequestTimeout(20000)
|
||||
);
|
||||
// 创建 RestClientTransport 和 ElasticsearchClient
|
||||
RestClient restClient = restClientBuilder.build();
|
||||
ElasticsearchTransport transport = new RestClientTransport(
|
||||
restClient,
|
||||
new JacksonJsonpMapper() // 使用 Jackson 进行 JSON 处理
|
||||
);
|
||||
|
||||
return new ElasticsearchClient(transport);
|
||||
}
|
||||
|
||||
/**
|
||||
window系统本地启动 es8.x 重置密码命令:.\elasticsearch-reset-password -u elastic
|
||||
*/
|
||||
}
|
||||
42
src/main/java/cn/xf/basedemo/config/SwaggerGroupApi.java
Normal file
42
src/main/java/cn/xf/basedemo/config/SwaggerGroupApi.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package cn.xf.basedemo.config;
|
||||
|
||||
import io.swagger.v3.oas.models.ExternalDocumentation;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* packageName cn.xf.basedemo.config
|
||||
* @author remaindertime
|
||||
* @className SwaggerGroupApi
|
||||
* @date 2024/12/9
|
||||
* @description swagger分组配置
|
||||
*/
|
||||
@Component
|
||||
public class SwaggerGroupApi {
|
||||
|
||||
@Bean
|
||||
public OpenAPI springShopOpenAPI() {
|
||||
return new OpenAPI()
|
||||
.info(new Info().title("Spring boot脚手架 API")
|
||||
.description("开箱即用的Spring boot脚手架 API")
|
||||
.version("v0.0.1")
|
||||
.contact(new Contact().name("remaindertime").url("https://blog.csdn.net/qq_39818325"))
|
||||
.license(new License().name("Apache 2.0").url("http://springdoc.org")))
|
||||
.externalDocs(new ExternalDocumentation()
|
||||
.description("Spring boot脚手架 Wiki Documentation")
|
||||
.url("https://springshop.wiki.github.org/docs"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi publicApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("用戶相关分组")
|
||||
.pathsToMatch("/user/**")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,9 @@ import cn.xf.basedemo.interceptor.SessionContext;
|
||||
import cn.xf.basedemo.model.res.LoginInfoRes;
|
||||
import cn.xf.basedemo.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* @program: xf-boot-base
|
||||
@@ -21,6 +19,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
**/
|
||||
@RestController(value = "用户控制器")
|
||||
@RequestMapping("/user")
|
||||
@Tag(name = "用户控制器")
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
@@ -39,4 +38,17 @@ public class UserController {
|
||||
LoginUser loginUser = SessionContext.getInstance().get();
|
||||
return RetObj.success(loginUser);
|
||||
}
|
||||
|
||||
@Operation(summary = "es同步用户信息", description = "用户信息")
|
||||
@GetMapping("/syncEs")
|
||||
public RetObj syncEs(Long userId){
|
||||
return userService.syncEs(userId);
|
||||
}
|
||||
|
||||
@Operation(summary = "es查询用户信息", description = "用户信息")
|
||||
@GetMapping("/getEsId")
|
||||
public RetObj getEsId(Long userId){
|
||||
return userService.getEsId(userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,13 +32,15 @@ public class TokenInterceptor implements HandlerInterceptor {
|
||||
|
||||
|
||||
//不拦截的请求列表
|
||||
private static final List<String> EXCLUDE_PATH_LIST = Arrays.asList("/user/login", "/web/login");
|
||||
private static final List<String> EXCLUDE_PATH_LIST = Arrays.asList("/user/login", "/web/login","/swagger-ui.html","/v3/api-docs","/swagger-ui/index.html");
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
|
||||
String requestURI = request.getRequestURI();
|
||||
if (EXCLUDE_PATH_LIST.contains(requestURI)) {
|
||||
if (EXCLUDE_PATH_LIST.contains(requestURI) ||
|
||||
requestURI.contains("/swagger-ui") ||
|
||||
requestURI.contains("/v3/api-docs")) {
|
||||
return true;
|
||||
}
|
||||
//登录处理
|
||||
|
||||
@@ -13,4 +13,8 @@ import cn.xf.basedemo.model.res.LoginInfoRes;
|
||||
public interface UserService {
|
||||
|
||||
RetObj login(LoginInfoRes res);
|
||||
|
||||
RetObj syncEs(Long userId);
|
||||
|
||||
RetObj getEsId(Long userId);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package cn.xf.basedemo.service.impl;
|
||||
|
||||
import cn.xf.basedemo.common.model.EsBaseModel;
|
||||
import cn.xf.basedemo.common.model.LoginInfo;
|
||||
import cn.xf.basedemo.common.model.LoginUser;
|
||||
import cn.xf.basedemo.common.model.RetObj;
|
||||
import cn.xf.basedemo.common.utils.EsUtil;
|
||||
import cn.xf.basedemo.common.utils.JwtTokenUtils;
|
||||
import cn.xf.basedemo.common.utils.RSAUtils;
|
||||
import cn.xf.basedemo.common.utils.StringUtil;
|
||||
import cn.xf.basedemo.config.GlobalConfig;
|
||||
import cn.xf.basedemo.mappers.UserMapper;
|
||||
import cn.xf.basedemo.model.domain.User;
|
||||
@@ -90,4 +93,27 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
return RetObj.success(loginUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetObj syncEs(Long userId) {
|
||||
User user = userMapper.selectById(userId);
|
||||
if (Objects.isNull(user)) {
|
||||
return RetObj.error("用户不存在");
|
||||
}
|
||||
String index = StringUtil.camelToKebabCase(user.getClass().getSimpleName());
|
||||
if (!EsUtil.existIndex(index)) {
|
||||
EsUtil.createIndex(index);
|
||||
}
|
||||
EsUtil.addDocument(new EsBaseModel(index, String.valueOf(user.getId()), user, user.getClass()));
|
||||
return RetObj.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetObj getEsId(Long userId) {
|
||||
Object user = EsUtil.getDocumentById(new EsBaseModel("user", String.valueOf(userId), null, User.class));
|
||||
if(Objects.nonNull(user)){
|
||||
return RetObj.success(user);
|
||||
}
|
||||
return RetObj.error("es中不存在该用户");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,12 @@ spring:
|
||||
WRITE_DATES_AS_TIMESTAMPS: false
|
||||
FAIL_ON_EMPTY_BEANS: false
|
||||
|
||||
elasticsearch:
|
||||
host: localhost
|
||||
port: 9200
|
||||
username: elastic
|
||||
password: kVgA7eeLyNQKh_IyV*mW #window系统本地启动 es8.x 重置密码命令:.\elasticsearch-reset-password -u elastic
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
path: /v3/api-docs # 自定义 API 文档路径
|
||||
@@ -46,6 +52,9 @@ mybatis-plus:
|
||||
|
||||
# 参考文章 https://zhuanlan.zhihu.com/p/145359625
|
||||
management:
|
||||
health:
|
||||
elasticsearch: #禁用健康检查
|
||||
enabled: false
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
@@ -55,6 +64,6 @@ management:
|
||||
show-details: always
|
||||
|
||||
# 日志设置
|
||||
logging:
|
||||
level:
|
||||
root:
|
||||
#logging:
|
||||
# level:
|
||||
# root: DEBUG
|
||||
@@ -9,10 +9,5 @@ spring:
|
||||
file-extension: yml
|
||||
namespace: 34f368d5-a6c6-4f57-a80a-5402de295695
|
||||
group: DEFAULT_GROUP
|
||||
username:
|
||||
password:
|
||||
discovery:
|
||||
server-addr: 9.9.9.9:9
|
||||
namespace: 34f368d5-a6c6-4f57-a80a-5402de295695
|
||||
username:
|
||||
password:
|
||||
username: nacos
|
||||
password:
|
||||
@@ -54,7 +54,7 @@
|
||||
var json = JSON.stringify(data);
|
||||
var cipher = this.encryptByPublicKey(json);
|
||||
console.log("密文 :" + cipher);
|
||||
var url = "http://117.72.35.70:8089/user/login";
|
||||
var url = "http://localhost:8089/user/login";
|
||||
axios.post(url, {
|
||||
encryptedData: cipher,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user