feat:【IoT 物联网】新版本同步
parent
a89b6d14a8
commit
92581e3b24
|
@ -13,6 +13,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@ import java.util.List;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class JsonUtils {
|
public class JsonUtils {
|
||||||
|
|
||||||
|
@Getter
|
||||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
|
@ -60,4 +60,8 @@ public class ObjectUtils {
|
||||||
return Arrays.asList(array).contains(obj);
|
return Arrays.asList(array).contains(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isNotAllEmpty(Object... objs) {
|
||||||
|
return !ObjectUtil.isAllEmpty(objs);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
package cn.iocoder.yudao.framework.mybatis.config;
|
package cn.iocoder.yudao.framework.mybatis.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
|
import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
|
||||||
import com.baomidou.mybatisplus.annotation.DbType;
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
||||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||||
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
|
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
import com.baomidou.mybatisplus.extension.incrementer.*;
|
import com.baomidou.mybatisplus.extension.incrementer.*;
|
||||||
import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
|
import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
|
||||||
import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache;
|
import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
@ -18,6 +22,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,4 +80,15 @@ public class YudaoMybatisAutoConfiguration {
|
||||||
throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType));
|
throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JacksonTypeHandler jacksonTypeHandler(List<ObjectMapper> objectMappers) {
|
||||||
|
// 特殊:设置 JacksonTypeHandler 的 ObjectMapper!
|
||||||
|
ObjectMapper objectMapper = CollUtil.getFirst(objectMappers);
|
||||||
|
if (objectMapper == null) {
|
||||||
|
objectMapper = JsonUtils.getObjectMapper();
|
||||||
|
}
|
||||||
|
JacksonTypeHandler.setObjectMapper(objectMapper);
|
||||||
|
return new JacksonTypeHandler(Object.class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class RateLimiterAspect {
|
||||||
|
|
||||||
@Before("@annotation(rateLimiter)")
|
@Before("@annotation(rateLimiter)")
|
||||||
public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) {
|
public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) {
|
||||||
// 获得 IdempotentKeyResolver 对象
|
// 获得 RateLimiterKeyResolver 对象
|
||||||
RateLimiterKeyResolver keyResolver = keyResolvers.get(rateLimiter.keyResolver());
|
RateLimiterKeyResolver keyResolver = keyResolvers.get(rateLimiter.keyResolver());
|
||||||
Assert.notNull(keyResolver, "找不到对应的 RateLimiterKeyResolver");
|
Assert.notNull(keyResolver, "找不到对应的 RateLimiterKeyResolver");
|
||||||
// 解析 Key
|
// 解析 Key
|
||||||
|
|
|
@ -1,174 +0,0 @@
|
||||||
# AiBoChaWebSearchClient 使用指南
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
`AiBoChaWebSearchClient` 是基于博查AI开放平台提供的网页搜索服务的Java客户端,实现了符合项目架构风格的HTTP客户端封装。
|
|
||||||
|
|
||||||
## 特性
|
|
||||||
|
|
||||||
- **统一的API调用风格**:参考 SunoApi 和 XunFeiPptApi 的实现方式
|
|
||||||
- **Record 类型数据结构**:使用 Record 类型定义请求和响应数据
|
|
||||||
- **简洁的响应数据模型**:包含网页搜索结果
|
|
||||||
- **灵活的搜索配置**:支持时间范围、域名过滤、结果数量等参数
|
|
||||||
- **错误处理机制**:统一的异常处理和日志记录
|
|
||||||
|
|
||||||
## 快速开始
|
|
||||||
|
|
||||||
### 1. 创建客户端实例
|
|
||||||
|
|
||||||
```java
|
|
||||||
// 使用默认base URL
|
|
||||||
AiBoChaWebSearchClient client = new AiBoChaWebSearchClient("your-api-key");
|
|
||||||
|
|
||||||
// 使用自定义base URL
|
|
||||||
AiBoChaWebSearchClient client = new AiBoChaWebSearchClient("https://custom.api.com", "your-api-key");
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 基本搜索
|
|
||||||
|
|
||||||
```java
|
|
||||||
// 基本搜索
|
|
||||||
WebSearchRequest request = new WebSearchRequest(
|
|
||||||
"Spring Boot 教程",
|
|
||||||
null, null, null, null, null
|
|
||||||
);
|
|
||||||
AiWebSearchResponse result = client.search(request);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 高级搜索
|
|
||||||
|
|
||||||
```java
|
|
||||||
// 构建详细的搜索请求
|
|
||||||
WebSearchRequest request = new WebSearchRequest(
|
|
||||||
"人工智能最新进展",
|
|
||||||
FreshnessType.ONE_WEEK.getValue(), // 搜索一周内的内容
|
|
||||||
true, // 显示摘要
|
|
||||||
"zhihu.com|csdn.net", // 只搜索指定域名
|
|
||||||
"spam.com", // 排除指定域名
|
|
||||||
20 // 返回20条结果
|
|
||||||
);
|
|
||||||
|
|
||||||
AiWebSearchResponse result = client.search(request);
|
|
||||||
```
|
|
||||||
|
|
||||||
## API参数说明
|
|
||||||
|
|
||||||
### 请求参数
|
|
||||||
|
|
||||||
| 参数名 | 类型 | 必填 | 说明 |
|
|
||||||
|--------|------|------|------|
|
|
||||||
| query | String | 是 | 用户的搜索词 |
|
|
||||||
| freshness | String | 否 | 搜索时间范围,默认为 noLimit |
|
|
||||||
| summary | Boolean | 否 | 是否显示文本摘要,默认为 false |
|
|
||||||
| include | String | 否 | 指定搜索的网站范围,多个域名使用\|或,分隔 |
|
|
||||||
| exclude | String | 否 | 排除搜索的网站范围,多个域名使用\|或,分隔 |
|
|
||||||
| count | Integer | 否 | 返回结果条数,范围1-50,默认为10 |
|
|
||||||
|
|
||||||
### 时间范围选项
|
|
||||||
|
|
||||||
使用 `FreshnessType` 枚举:
|
|
||||||
|
|
||||||
```java
|
|
||||||
FreshnessType.NO_LIMIT // 不限(默认)
|
|
||||||
FreshnessType.ONE_DAY // 一天内
|
|
||||||
FreshnessType.ONE_WEEK // 一周内
|
|
||||||
FreshnessType.ONE_MONTH // 一个月内
|
|
||||||
FreshnessType.ONE_YEAR // 一年内
|
|
||||||
```
|
|
||||||
|
|
||||||
也可以使用自定义日期范围:
|
|
||||||
- 日期范围:`"2025-01-01..2025-04-06"`
|
|
||||||
- 指定日期:`"2025-04-06"`
|
|
||||||
|
|
||||||
### 响应数据结构
|
|
||||||
|
|
||||||
```java
|
|
||||||
// 主要响应数据
|
|
||||||
AiWebSearchResponse result = client.search(request);
|
|
||||||
|
|
||||||
// 网页搜索结果
|
|
||||||
List<AiWebSearchResponse.WebPage> webPages = result.webPages();
|
|
||||||
for (AiWebSearchResponse.WebPage page : webPages) {
|
|
||||||
String title = page.title(); // 网页标题
|
|
||||||
String url = page.url(); // 网页URL
|
|
||||||
String snippet = page.snippet(); // 内容描述
|
|
||||||
String summary = page.summary(); // 文本摘要(如果请求了summary)
|
|
||||||
String siteName = page.siteName(); // 网站名称
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 示例1:搜索技术文档
|
|
||||||
|
|
||||||
```java
|
|
||||||
WebSearchRequest request = new WebSearchRequest(
|
|
||||||
"Spring Boot 3.x 新特性",
|
|
||||||
FreshnessType.ONE_MONTH.getValue(),
|
|
||||||
true,
|
|
||||||
"spring.io|baeldung.com|github.com",
|
|
||||||
null,
|
|
||||||
15
|
|
||||||
);
|
|
||||||
|
|
||||||
AiWebSearchResponse result = client.search(request);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 示例2:搜索新闻资讯
|
|
||||||
|
|
||||||
```java
|
|
||||||
WebSearchRequest request = new WebSearchRequest(
|
|
||||||
"AI大模型发展趋势",
|
|
||||||
FreshnessType.ONE_WEEK.getValue(),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"advertisement.com|spam.net",
|
|
||||||
30
|
|
||||||
);
|
|
||||||
|
|
||||||
AiWebSearchResponse result = client.search(request);
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **API密钥**:需要先到博查AI开放平台(https://open.bochaai.com)获取API KEY
|
|
||||||
2. **请求频率**:注意遵守平台的API调用频率限制
|
|
||||||
3. **时间范围**:建议使用 `noLimit` 以获得更好的搜索效果
|
|
||||||
4. **域名过滤**:include和exclude参数最多支持20个域名
|
|
||||||
5. **结果数量**:单次搜索最多返回50条结果
|
|
||||||
|
|
||||||
## 集成建议
|
|
||||||
|
|
||||||
在Spring Boot项目中,建议将客户端配置为Bean:
|
|
||||||
|
|
||||||
```java
|
|
||||||
@Configuration
|
|
||||||
public class AiConfiguration {
|
|
||||||
|
|
||||||
@Value("${ai.bocha.api-key}")
|
|
||||||
private String apiKey;
|
|
||||||
|
|
||||||
@Value("${ai.bocha.base-url:https://open.bochaai.com}")
|
|
||||||
private String baseUrl;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public AiBoChaWebSearchClient boChaWebSearchClient() {
|
|
||||||
return new AiBoChaWebSearchClient(baseUrl, apiKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 故障排查
|
|
||||||
|
|
||||||
1. **网络连接问题**:检查网络连接和防火墙设置
|
|
||||||
2. **API密钥错误**:确认API KEY正确且有效
|
|
||||||
3. **请求参数错误**:检查必填参数是否正确填写
|
|
||||||
4. **服务器响应错误**:查看日志中的详细错误信息
|
|
||||||
|
|
||||||
## 更新日志
|
|
||||||
|
|
||||||
- v2.0.0:重大重构,统一 Record 类型,简化 API 调用,支持新的响应结构
|
|
||||||
- v1.3.0:统一使用 Record 类型,移除 Lombok 注解,保持代码风格一致性
|
|
||||||
- v1.2.0:进一步简化,移除视频搜索功能,专注于网页搜索
|
|
||||||
- v1.1.0:使用 Lombok 简化代码,移除图片搜索功能
|
|
||||||
- v1.0.0:初始版本,实现基本的网页搜索功能
|
|
|
@ -19,6 +19,7 @@ public enum BpmReasonEnum {
|
||||||
CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程
|
CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程
|
||||||
CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程
|
CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程
|
||||||
CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS("子流程自动取消,原因:主流程已取消"),
|
CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS("子流程自动取消,原因:主流程已取消"),
|
||||||
|
REJECT_CHILD_PROCESS("子流程审批不通过"),
|
||||||
|
|
||||||
// ========== 流程任务的独有原因 ==========
|
// ========== 流程任务的独有原因 ==========
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.flowable.engine.history.HistoricActivityInstance;
|
||||||
import org.flowable.engine.history.HistoricProcessInstance;
|
import org.flowable.engine.history.HistoricProcessInstance;
|
||||||
import org.flowable.engine.history.HistoricProcessInstanceQuery;
|
import org.flowable.engine.history.HistoricProcessInstanceQuery;
|
||||||
import org.flowable.engine.repository.ProcessDefinition;
|
import org.flowable.engine.repository.ProcessDefinition;
|
||||||
|
import org.flowable.engine.runtime.Execution;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.flowable.engine.runtime.ProcessInstanceBuilder;
|
import org.flowable.engine.runtime.ProcessInstanceBuilder;
|
||||||
import org.flowable.task.api.Task;
|
import org.flowable.task.api.Task;
|
||||||
|
@ -949,6 +950,29 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||||
status);
|
status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1.3 如果子流程拒绝,设置其父流程也为拒绝状态,且结束父流程
|
||||||
|
// 相关问题链接:https://t.zsxq.com/kZhyb
|
||||||
|
if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())
|
||||||
|
&& StrUtil.isNotBlank(instance.getSuperExecutionId())) {
|
||||||
|
// 1.3.1 获取父流程实例 并标记为不通过
|
||||||
|
Execution execution = runtimeService.createExecutionQuery().executionId(instance.getSuperExecutionId()).singleResult();
|
||||||
|
ProcessInstance parentProcessInstance = getProcessInstance(execution.getProcessInstanceId());
|
||||||
|
updateProcessInstanceReject(parentProcessInstance, BpmReasonEnum.REJECT_CHILD_PROCESS.getReason());
|
||||||
|
|
||||||
|
// 1.3.2 结束父流程。需要在子流程结束事务提交后执行
|
||||||
|
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(int transactionStatus) {
|
||||||
|
// 回滚情况,直接返回
|
||||||
|
if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
taskService.moveTaskToEnd(parentProcessInstance.getId(), BpmReasonEnum.REJECT_CHILD_PROCESS.getReason());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 2. 发送对应的消息通知
|
// 2. 发送对应的消息通知
|
||||||
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
|
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
|
||||||
messageService.sendMessageWhenProcessInstanceApprove(
|
messageService.sendMessageWhenProcessInstanceApprove(
|
||||||
|
@ -996,17 +1020,18 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||||
if (ObjUtil.notEqual(instance.getName(), name)) {
|
if (ObjUtil.notEqual(instance.getName(), name)) {
|
||||||
runtimeService.setProcessInstanceName(instance.getProcessInstanceId(), name);
|
runtimeService.setProcessInstanceName(instance.getProcessInstanceId(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 流程前置通知:需要在流程启动后(事务提交后),保证 variables 已设置
|
||||||
|
// 相关问题链接:https://t.zsxq.com/DF7Kq
|
||||||
|
if (ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting();
|
||||||
|
BpmHttpRequestUtils.executeBpmHttpRequest(instance,
|
||||||
|
setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 流程前置通知
|
|
||||||
if (ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting();
|
|
||||||
BpmHttpRequestUtils.executeBpmHttpRequest(instance,
|
|
||||||
setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -804,7 +804,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
.setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
|
.setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 3.2 情况二:直接结束,审批不通过
|
|
||||||
|
// 3.2 情况二: 标记流程为不通过并结束流程
|
||||||
processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过
|
processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过
|
||||||
moveTaskToEnd(task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); // 结束流程
|
moveTaskToEnd(task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); // 结束流程
|
||||||
}
|
}
|
||||||
|
@ -986,6 +987,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void moveTaskToEnd(String processInstanceId, String reason) {
|
public void moveTaskToEnd(String processInstanceId, String reason) {
|
||||||
List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null);
|
List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null);
|
||||||
if (CollUtil.isEmpty(taskList)) {
|
if (CollUtil.isEmpty(taskList)) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package cn.iocoder.yudao.module.iot.controller.admin.ota;
|
package cn.iocoder.yudao.module.iot.controller.admin.ota;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
4import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||||
|
@ -21,7 +21,11 @@ import jakarta.annotation.Resource;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -44,8 +48,8 @@ public class IotOtaTaskRecordController {
|
||||||
@GetMapping("/get-status-statistics")
|
@GetMapping("/get-status-statistics")
|
||||||
@Operation(summary = "获得 OTA 升级记录状态统计")
|
@Operation(summary = "获得 OTA 升级记录状态统计")
|
||||||
@Parameters({
|
@Parameters({
|
||||||
@Parameter(name = "firmwareId", description = "固件编号", example = "1024"),
|
@Parameter(name = "firmwareId", description = "固件编号", example = "1024"),
|
||||||
@Parameter(name = "taskId", description = "升级任务编号", example = "2048")
|
@Parameter(name = "taskId", description = "升级任务编号", example = "2048")
|
||||||
})
|
})
|
||||||
@PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
|
@PreAuthorize("@ss.hasPermission('iot:ota-task-record:query')")
|
||||||
public CommonResult<Map<Integer, Long>> getOtaTaskRecordStatusStatistics(
|
public CommonResult<Map<Integer, Long>> getOtaTaskRecordStatusStatistics(
|
||||||
|
@ -64,17 +68,17 @@ public class IotOtaTaskRecordController {
|
||||||
return success(PageResult.empty());
|
return success(PageResult.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量查询固件信息
|
// 批量查询固件信息
|
||||||
Map<Long, IotOtaFirmwareDO> firmwareMap = otaFirmwareService.getOtaFirmwareMap(
|
Map<Long, IotOtaFirmwareDO> firmwareMap = otaFirmwareService.getOtaFirmwareMap(
|
||||||
convertSet(pageResult.getList(), IotOtaTaskRecordDO::getFromFirmwareId));
|
convertSet(pageResult.getList(), IotOtaTaskRecordDO::getFromFirmwareId));
|
||||||
Map<Long, IotDeviceDO> deviceMap = deviceService.getDeviceMap(
|
Map<Long, IotDeviceDO> deviceMap = deviceService.getDeviceMap(
|
||||||
convertSet(pageResult.getList(), IotOtaTaskRecordDO::getDeviceId));
|
convertSet(pageResult.getList(), IotOtaTaskRecordDO::getDeviceId));
|
||||||
// 转换为响应 VO
|
// 转换为响应 VO
|
||||||
return success(BeanUtils.toBean(pageResult, IotOtaTaskRecordRespVO.class, (vo) -> {
|
return success(BeanUtils.toBean(pageResult, IotOtaTaskRecordRespVO.class, (vo) -> {
|
||||||
MapUtils.findAndThen(firmwareMap, vo.getFromFirmwareId(), firmware ->
|
MapUtils.findAndThen(firmwareMap, vo.getFromFirmwareId(), firmware ->
|
||||||
vo.setFromFirmwareVersion(firmware.getVersion()));
|
vo.setFromFirmwareVersion(firmware.getVersion()));
|
||||||
MapUtils.findAndThen(deviceMap, vo.getDeviceId(), device ->
|
MapUtils.findAndThen(deviceMap, vo.getDeviceId(), device ->
|
||||||
vo.setDeviceName(device.getDeviceName()));
|
vo.setDeviceName(device.getDeviceName()));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -226,7 +226,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||||
}
|
}
|
||||||
|
|
||||||
private PayOrderRespDTO doGetOrderV3(String outTradeNo) throws WxPayException {
|
private PayOrderRespDTO doGetOrderV3(String outTradeNo) throws WxPayException {
|
||||||
fixV3HttpClientConnectionPoolShutDown();
|
|
||||||
// 构建 WxPayUnifiedOrderRequest 对象
|
// 构建 WxPayUnifiedOrderRequest 对象
|
||||||
WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request()
|
WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request()
|
||||||
.setOutTradeNo(outTradeNo);
|
.setOutTradeNo(outTradeNo);
|
||||||
|
@ -299,7 +298,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||||
}
|
}
|
||||||
|
|
||||||
private PayRefundRespDTO doUnifiedRefundV3(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
private PayRefundRespDTO doUnifiedRefundV3(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||||
fixV3HttpClientConnectionPoolShutDown();
|
|
||||||
// 1. 构建 WxPayRefundRequest 请求
|
// 1. 构建 WxPayRefundRequest 请求
|
||||||
WxPayRefundV3Request request = new WxPayRefundV3Request()
|
WxPayRefundV3Request request = new WxPayRefundV3Request()
|
||||||
.setOutTradeNo(reqDTO.getOutTradeNo())
|
.setOutTradeNo(reqDTO.getOutTradeNo())
|
||||||
|
@ -415,7 +413,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||||
}
|
}
|
||||||
|
|
||||||
private PayRefundRespDTO doGetRefundV3(String outTradeNo, String outRefundNo) throws WxPayException {
|
private PayRefundRespDTO doGetRefundV3(String outTradeNo, String outRefundNo) throws WxPayException {
|
||||||
fixV3HttpClientConnectionPoolShutDown();
|
|
||||||
// 1. 构建 WxPayRefundRequest 请求
|
// 1. 构建 WxPayRefundRequest 请求
|
||||||
WxPayRefundQueryV3Request request = new WxPayRefundQueryV3Request();
|
WxPayRefundQueryV3Request request = new WxPayRefundQueryV3Request();
|
||||||
request.setOutRefundNo(outRefundNo);
|
request.setOutRefundNo(outRefundNo);
|
||||||
|
@ -439,7 +436,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws WxPayException {
|
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws WxPayException {
|
||||||
fixV3HttpClientConnectionPoolShutDown();
|
|
||||||
// 1. 构建 TransferBillsRequest 请求
|
// 1. 构建 TransferBillsRequest 请求
|
||||||
TransferBillsRequest request = TransferBillsRequest.newBuilder()
|
TransferBillsRequest request = TransferBillsRequest.newBuilder()
|
||||||
.appid(this.config.getAppId())
|
.appid(this.config.getAppId())
|
||||||
|
@ -485,7 +481,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PayTransferRespDTO doGetTransfer(String outTradeNo) throws WxPayException {
|
protected PayTransferRespDTO doGetTransfer(String outTradeNo) throws WxPayException {
|
||||||
fixV3HttpClientConnectionPoolShutDown();
|
|
||||||
// 1. 执行请求
|
// 1. 执行请求
|
||||||
TransferBillsGetResult response = client.getTransferService().getBillsByOutBillNo(outTradeNo);
|
TransferBillsGetResult response = client.getTransferService().getBillsByOutBillNo(outTradeNo);
|
||||||
|
|
||||||
|
@ -558,11 +553,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||||
return headers.get(lowercaseKey);
|
return headers.get(lowercaseKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @芋艿:可能是 wxjava 的 bug:https://github.com/binarywang/WxJava/issues/1557
|
|
||||||
private void fixV3HttpClientConnectionPoolShutDown() {
|
|
||||||
client.getConfig().setApiV3HttpClient(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static String formatDateV2(LocalDateTime time) {
|
static String formatDateV2(LocalDateTime time) {
|
||||||
return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN);
|
return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN);
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,9 +357,11 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest {
|
||||||
when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> {
|
when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> {
|
||||||
assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo());
|
assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo());
|
||||||
assertThat(payOrderUnifiedReqDTO)
|
assertThat(payOrderUnifiedReqDTO)
|
||||||
.extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime")
|
// .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime") // TODO @芋艿:win11 下,时间不太准
|
||||||
|
.extracting("subject", "body", "notifyUrl", "returnUrl", "price")
|
||||||
.containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10",
|
.containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10",
|
||||||
reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime());
|
// reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime());
|
||||||
|
reqVO.getReturnUrl(), order.getPrice());
|
||||||
return true;
|
return true;
|
||||||
}))).thenReturn(unifiedOrderResp);
|
}))).thenReturn(unifiedOrderResp);
|
||||||
|
|
||||||
|
@ -411,9 +413,11 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest {
|
||||||
when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> {
|
when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> {
|
||||||
assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo());
|
assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo());
|
||||||
assertThat(payOrderUnifiedReqDTO)
|
assertThat(payOrderUnifiedReqDTO)
|
||||||
.extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime")
|
// .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime") // TODO @芋艿:win11 下,时间不太准
|
||||||
|
.extracting("subject", "body", "notifyUrl", "returnUrl", "price")
|
||||||
.containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10",
|
.containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10",
|
||||||
reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime());
|
// reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime());
|
||||||
|
reqVO.getReturnUrl(), order.getPrice());
|
||||||
return true;
|
return true;
|
||||||
}))).thenReturn(unifiedOrderResp);
|
}))).thenReturn(unifiedOrderResp);
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateUpdateRole_success() {
|
public void testValidateUpdateRole_success() {
|
||||||
RoleDO roleDO = randomPojo(RoleDO.class);
|
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType()));
|
||||||
roleMapper.insert(roleDO);
|
roleMapper.insert(roleDO);
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Long id = roleDO.getId();
|
Long id = roleDO.getId();
|
||||||
|
|
Loading…
Reference in New Issue