feat(unisoftiot): 新增设备和产品相关功能
- 添加 Account、Device、Product 等实体类
- 实现设备控制、产品列表等 API 接口- 集成 UniSoftAccount 配置和请求处理
- 新增 JSON 解析和 OkHttp 工具类
- 更新数据库配置和 Mapper 接口
24 files added
2 files deleted
3 files modified
| | |
| | | </properties> |
| | | |
| | | <dependencies> |
| | | |
| | | <dependency> |
| | | <groupId>org.json</groupId> |
| | | <artifactId>json</artifactId> |
| | | <version>20230618</version> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>com.github.wechatpay-apiv3</groupId> |
| | | <artifactId>wechatpay-apache-httpclient</artifactId> |
| | |
| | | @EnableScheduling |
| | | @SpringBootApplication |
| | | @EnableTransactionManagement |
| | | @MapperScan("cc.mrbird.febs.*.mapper") |
| | | @MapperScan({"cc.mrbird.febs.*.mapper", "cc.mrbird.febs.unisoftiot.*.mapper"}) |
| | | public class FebsShiroApplication { |
| | | |
| | | public static void main(String[] args) { |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.entity; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class Account { |
| | | |
| | | private String appId; |
| | | |
| | | private String appSecret; |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.entity; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class Device { |
| | | |
| | | private String deviceId; |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.entity; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * Product类代表一个产品实体,用于在平台中存储和管理产品相关信息 |
| | | * 它包含了产品的基本属性,如产品ID、型号、名称、图标、生产商、设备数量和备注 |
| | | */ |
| | | @Data |
| | | public class Product { |
| | | /** |
| | | * 产品ID,平台唯一,固定不变 |
| | | */ |
| | | private int id; |
| | | /** |
| | | * 产品型号 |
| | | */ |
| | | private String model; |
| | | /** |
| | | * 产品名称 |
| | | */ |
| | | private String title; |
| | | /** |
| | | * 产品图标 |
| | | */ |
| | | private String icon; |
| | | /** |
| | | * 生产商,Unisoft或自定义 |
| | | */ |
| | | private String producer; |
| | | /** |
| | | * 工作台(控制台)下此产品类型的设备数量 |
| | | */ |
| | | private Integer device; |
| | | /** |
| | | * 备注 |
| | | */ |
| | | private String remark; |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.mapper; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Account; |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | |
| | | public interface AccountMapper extends BaseMapper<Account> { |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.mapper; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Device; |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | |
| | | public interface DeviceMapper extends BaseMapper<Device> { |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.mapper; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Product; |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | |
| | | public interface ProductMapper extends BaseMapper<Product> { |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.service; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Device; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | public interface DeviceService extends IService<Device> { |
| | | |
| | | //控制设备,向设备下发指令 |
| | | String controlDevice(Device device); |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.service; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Account; |
| | | import cc.mrbird.febs.unisoftiot.config.UniSoftAccount; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | public interface InitAccountService extends IService<Account> { |
| | | |
| | | UniSoftAccount initAccount(Account account); |
| | | |
| | | UniSoftAccount initAccountByAppInfo(String appId, String appSecret); |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.service; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Product; |
| | | import cc.mrbird.febs.unisoftiot.api.vo.ApiProductVo; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface ProductService extends IService<Product> { |
| | | |
| | | /** |
| | | * 获取产品列表 |
| | | */ |
| | | List<ApiProductVo> getProductList(); |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.service.impl; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Account; |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Device; |
| | | import cc.mrbird.febs.unisoftiot.api.mapper.DeviceMapper; |
| | | import cc.mrbird.febs.unisoftiot.api.service.DeviceService; |
| | | import cc.mrbird.febs.unisoftiot.api.service.InitAccountService; |
| | | import cc.mrbird.febs.unisoftiot.config.UniSoftAccount; |
| | | import cc.mrbird.febs.unisoftiot.enums.DeviceRequestUrlEnum; |
| | | import cc.mrbird.febs.unisoftiot.utils.UrlUtils; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.LinkedHashMap; |
| | | |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService { |
| | | |
| | | private final InitAccountService initAccountService; |
| | | |
| | | /** |
| | | * 控制设备的方法 |
| | | * 该方法用于向设备发送控制指令,例如开关设备等 |
| | | * |
| | | * @param device 设备对象,包含设备的相关信息 |
| | | * @return 返回控制设备的结果,以字符串形式表示 |
| | | */ |
| | | @Override |
| | | public String controlDevice(Device device) { |
| | | // 初始化账户服务,为设备控制做准备 |
| | | UniSoftAccount uniSoftAccount = initAccountService.initAccountByAppInfo(UrlUtils.APP_ID,UrlUtils.APP_SECRET); |
| | | |
| | | // 创建参数集合,用于设备控制 |
| | | LinkedHashMap<String, Object> parameters = new LinkedHashMap<>(); |
| | | // 添加控制设备的参数,这里的例子是开启设备的某个功能 |
| | | parameters.put("device", "1438"); |
| | | parameters.put("power3", "1"); |
| | | |
| | | log.info("parameters",parameters.toString()); |
| | | |
| | | // 发送签名请求,控制设备 |
| | | // 这里使用了签名请求来确保设备控制指令的安全性 |
| | | return uniSoftAccount.requestHandler.sendSignedRequest( |
| | | UrlUtils.BASE_URL, |
| | | DeviceRequestUrlEnum.DEVICE_CONTROL.getRequestUrl(), |
| | | parameters, |
| | | DeviceRequestUrlEnum.DEVICE_CONTROL.getRequestMethod()); |
| | | } |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.service.impl; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.api.service.InitAccountService; |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Account; |
| | | import cc.mrbird.febs.unisoftiot.api.mapper.AccountMapper; |
| | | import cc.mrbird.febs.unisoftiot.config.UniSoftAccount; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class InitAccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements InitAccountService { |
| | | @Override |
| | | public UniSoftAccount initAccount(Account account) { |
| | | return new UniSoftAccount(account.getAppId(),account.getAppSecret()); |
| | | } |
| | | |
| | | @Override |
| | | public UniSoftAccount initAccountByAppInfo(String appId, String appSecret) { |
| | | Account account = new Account(); |
| | | account.setAppId(appId); |
| | | account.setAppSecret(appSecret); |
| | | return this.initAccount(account); |
| | | } |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.service.impl; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.api.entity.Product; |
| | | import cc.mrbird.febs.unisoftiot.api.mapper.ProductMapper; |
| | | import cc.mrbird.febs.unisoftiot.api.service.InitAccountService; |
| | | import cc.mrbird.febs.unisoftiot.api.service.ProductService; |
| | | import cc.mrbird.febs.unisoftiot.api.vo.ApiProductVo; |
| | | import cc.mrbird.febs.unisoftiot.config.UniSoftAccount; |
| | | import cc.mrbird.febs.unisoftiot.enums.ProductRequestUrlEnum; |
| | | import cc.mrbird.febs.unisoftiot.utils.UrlUtils; |
| | | import cn.hutool.json.JSONObject; |
| | | import cn.hutool.json.JSONUtil; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService { |
| | | |
| | | private final InitAccountService initAccountService; |
| | | @Override |
| | | public List<ApiProductVo> getProductList() { |
| | | UniSoftAccount uniSoftAccount = initAccountService.initAccountByAppInfo(UrlUtils.APP_ID, UrlUtils.APP_SECRET); |
| | | |
| | | // 创建参数集合 |
| | | LinkedHashMap<String, Object> parameters = new LinkedHashMap<>(); |
| | | parameters.put("q", 1); |
| | | String bodyStr = uniSoftAccount.requestHandler.sendSignedRequest( |
| | | UrlUtils.BASE_URL, ProductRequestUrlEnum.PRODUCT_LIST.getRequestUrl(), |
| | | parameters, ProductRequestUrlEnum.PRODUCT_LIST.getRequestMethod()); |
| | | |
| | | JSONObject jsonObject = JSONUtil.parseObj(bodyStr); |
| | | return jsonObject.getJSONArray("data").toList(ApiProductVo.class); |
| | | } |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.api.vo; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class ApiProductVo { |
| | | /** |
| | | * 产品ID,平台唯一,固定不变 |
| | | */ |
| | | private int id; |
| | | /** |
| | | * 产品型号 |
| | | */ |
| | | private String model; |
| | | /** |
| | | * 产品名称 |
| | | */ |
| | | private String title; |
| | | /** |
| | | * 产品图标 |
| | | */ |
| | | private String icon; |
| | | /** |
| | | * 生产商,Unisoft或自定义 |
| | | */ |
| | | private String producer; |
| | | /** |
| | | * 工作台(控制台)下此产品类型的设备数量 |
| | | */ |
| | | private Integer device; |
| | | /** |
| | | * 备注 |
| | | */ |
| | | private String remark; |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.config; |
| | | |
| | | import cc.mrbird.febs.common.exception.FebsException; |
| | | import cc.mrbird.febs.unisoftiot.enums.HttpMethod; |
| | | import okhttp3.MediaType; |
| | | import okhttp3.Request; |
| | | import okhttp3.RequestBody; |
| | | |
| | | /** |
| | | * 请求构建器类,用于构建HTTP请求 |
| | | */ |
| | | public final class RequestBuilder { |
| | | |
| | | // 定义JSON类型的媒体类型常量 |
| | | private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8"); |
| | | |
| | | // 私有构造方法,防止实例化 |
| | | private RequestBuilder() { |
| | | } |
| | | |
| | | /** |
| | | * 根据提供的参数构建HTTP请求 |
| | | * |
| | | * @param fullUrl 完整的请求URL |
| | | * @param body 请求体内容 |
| | | * @param httpMethod HTTP方法枚举类型 |
| | | * @return 构建好的Request对象 |
| | | * @throws FebsException 如果URL无效或HTTP方法不支持,则抛出此异常 |
| | | */ |
| | | public static Request buildRequest(String fullUrl, String body, HttpMethod httpMethod) { |
| | | // 校验 URL |
| | | if (fullUrl == null || fullUrl.isEmpty()) { |
| | | throw new FebsException("Invalid URL: URL cannot be null or empty"); |
| | | } |
| | | |
| | | // 创建Request.Builder对象用于配置请求 |
| | | Request.Builder builder = new Request.Builder(); |
| | | try { |
| | | // 根据HTTP方法构建不同的请求 |
| | | final Request request; |
| | | switch (httpMethod) { |
| | | case POST: |
| | | // 构建POST请求,并设置请求体为JSON类型 |
| | | request = builder |
| | | .url(fullUrl) |
| | | .post(RequestBody.create(JSON_TYPE, body)) |
| | | .addHeader("X-APISpace-Token","") |
| | | .addHeader("Content-Type","") |
| | | .build(); |
| | | break; |
| | | case GET: |
| | | // 构建GET请求 |
| | | request = builder |
| | | .url(fullUrl) |
| | | .get() |
| | | .addHeader("Content-Type", "application/x-www-form-urlencoded") |
| | | .build(); |
| | | break; |
| | | default: |
| | | // 如果HTTP方法不支持,抛出异常 |
| | | throw new FebsException("Invalid HTTP method: " + httpMethod); |
| | | } |
| | | // 返回构建好的请求对象 |
| | | return request; |
| | | } catch (IllegalArgumentException e) { |
| | | // 如果URL格式错误,抛出异常 |
| | | throw new FebsException("Invalid URL: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.config; |
| | | |
| | | import cc.mrbird.febs.common.exception.FebsException; |
| | | import cc.mrbird.febs.unisoftiot.enums.HttpMethod; |
| | | import cc.mrbird.febs.unisoftiot.utils.UrlUtils; |
| | | import cn.hutool.json.JSONUtil; |
| | | import com.alibaba.fastjson.JSON; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import okhttp3.Request; |
| | | |
| | | import java.util.LinkedHashMap; |
| | | |
| | | /** |
| | | * 请求处理器类,用于构建和发送API请求 |
| | | */ |
| | | @Slf4j |
| | | public class RequestHandler { |
| | | |
| | | private String appId; |
| | | private String appSecret; |
| | | |
| | | /** |
| | | * 构造函数,初始化appId和appSecret |
| | | * |
| | | * @param appId 应用ID |
| | | * @param appSecret 应用密钥 |
| | | */ |
| | | public RequestHandler(String appId,String appSecret){ |
| | | this.appId = appId; |
| | | this.appSecret = appSecret; |
| | | } |
| | | |
| | | /** |
| | | * 发送API请求 |
| | | * |
| | | * @param baseUrl 基础URL |
| | | * @param urlPath URL路径 |
| | | * @param parameters 请求参数 |
| | | * @param httpMethod HTTP方法 |
| | | * @return 请求结果字符串 |
| | | * @throws FebsException 当HTTP方法不支持时抛出异常 |
| | | */ |
| | | private String sendApiRequest(String baseUrl, |
| | | String urlPath, |
| | | LinkedHashMap<String, Object> parametersw, |
| | | HttpMethod httpMethod) { |
| | | |
| | | // 创建参数集合,用于设备控制 |
| | | LinkedHashMap<String, Object> parameters = new LinkedHashMap<>(); |
| | | // 添加控制设备的参数,这里的例子是开启设备的某个功能 |
| | | parameters.put("device", "1438"); |
| | | parameters.put("power3", "1"); |
| | | log.info("parameters",parameters.toString()); |
| | | String fullUrl = ""; |
| | | Request request; |
| | | // 根据HTTP方法构建请求 |
| | | switch (httpMethod) { |
| | | case POST: |
| | | // 对于POST请求,将参数序列化为JSON体 |
| | | fullUrl = UrlUtils.getUrl(baseUrl,appId,appSecret,urlPath,null); |
| | | String body = JSON.toJSONString(parameters); |
| | | log.info("parametersJson:", JSONUtil.parseObj(parameters)); |
| | | log.info("body:",body); |
| | | request = RequestBuilder.buildRequest(fullUrl, body,httpMethod); |
| | | break; |
| | | case GET: |
| | | // 对于GET请求,将参数直接拼接到URL中 |
| | | fullUrl = UrlUtils.getUrl(baseUrl,appId,appSecret,urlPath,parameters); |
| | | request = RequestBuilder.buildRequest(fullUrl, null,httpMethod); |
| | | break; |
| | | default: |
| | | // 不支持的HTTP方法抛出异常 |
| | | throw new FebsException("[RequestHandler] HttpMethod 不支持: " + httpMethod); |
| | | } |
| | | |
| | | // 记录请求信息 |
| | | log.info("{} {}", httpMethod, fullUrl); |
| | | |
| | | // 发送请求并处理响应 |
| | | return ResponseHandler.handleResponse(request); |
| | | } |
| | | |
| | | /** |
| | | * 发送签名请求,确保appId和appSecret不为空 |
| | | * |
| | | * @param baseUrl 基础URL |
| | | * @param urlPath URL路径 |
| | | * @param parameters 请求参数 |
| | | * @param httpMethod HTTP方法 |
| | | * @return 请求结果字符串 |
| | | * @throws FebsException 当appId或appSecret为空时抛出异常 |
| | | */ |
| | | public String sendSignedRequest(String baseUrl, |
| | | String urlPath, |
| | | LinkedHashMap<String, Object> parameters, |
| | | HttpMethod httpMethod) { |
| | | // 校验appId和appSecret |
| | | if (null == appId || appId.isEmpty() || null == appSecret || appSecret.isEmpty()) { |
| | | throw new FebsException("[RequestHandler] appId/appSecret 不能为空!"); |
| | | } |
| | | // 调用sendApiRequest方法发送请求 |
| | | return sendApiRequest(baseUrl, urlPath, parameters, httpMethod); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.config; |
| | | |
| | | import cc.mrbird.febs.common.exception.FebsException; |
| | | import cc.mrbird.febs.unisoftiot.enums.ResponseCodeEnum; |
| | | import cc.mrbird.febs.unisoftiot.utils.OkHttpUtils; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.hutool.json.JSONObject; |
| | | import okhttp3.Request; |
| | | import okhttp3.Response; |
| | | import okhttp3.ResponseBody; |
| | | import org.json.JSONException; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * 响应处理器类,用于处理OKHTTP请求的响应 |
| | | */ |
| | | public final class ResponseHandler { |
| | | |
| | | // 错误信息前缀 |
| | | private static final String ERROR_PREFIX = "[ResponseHandler] OKHTTP Error: "; |
| | | |
| | | // 私有构造方法,防止实例化 |
| | | private ResponseHandler() { |
| | | } |
| | | |
| | | /** |
| | | * 处理HTTP响应 |
| | | * @param request 请求对象 |
| | | * @return 响应体字符串 |
| | | * @throws FebsException 当响应处理失败时抛出 |
| | | */ |
| | | public static String handleResponse(Request request) { |
| | | try ( |
| | | // 执行HTTP请求并获取响应 |
| | | Response response = OkHttpUtils.builder().okHttpClient.newCall(request).execute() |
| | | ) { |
| | | // 获取响应体内容 |
| | | String responseBodyStr = getResponseBodyAsString(response.body()); |
| | | if (ResponseCodeEnum.isFail(response.code())) { |
| | | throw new FebsException(ERROR_PREFIX + responseBodyStr); |
| | | } |
| | | // 如果响应内容为空,则抛出异常 |
| | | if(StrUtil.isEmpty(responseBodyStr)){ |
| | | throw new FebsException(ERROR_PREFIX + "请求返回参数为空"); |
| | | } |
| | | // 解析响应内容为JSON对象 |
| | | JSONObject jsonObject = new JSONObject(responseBodyStr); |
| | | // 获取响应码 |
| | | int code = jsonObject.getInt("code"); |
| | | // 如果响应码表示失败,则处理错误响应 |
| | | if(ResponseCodeEnum.isFail(code)){ |
| | | throw handleErrorResponse(code, responseBodyStr); |
| | | } |
| | | // 获取响应消息 |
| | | String msg = jsonObject.getStr("msg"); |
| | | // 如果响应消息不正确,则抛出异常 |
| | | if(!ResponseCodeEnum.SUCCESS.getMsg().equals(msg)){ |
| | | throw new FebsException(ERROR_PREFIX + "请求返回参数msg不正确"); |
| | | } |
| | | // 返回响应内容 |
| | | return responseBodyStr; |
| | | } catch (IOException | IllegalStateException | JSONException e) { |
| | | // 捕获异常并抛出自定义异常 |
| | | throw new FebsException(ERROR_PREFIX + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理错误响应 |
| | | * @param code 响应码 |
| | | * @param responseBodyStr 响应体字符串 |
| | | * @return 自定义异常 |
| | | * @throws JSONException 当JSON解析失败时抛出 |
| | | */ |
| | | private static FebsException handleErrorResponse(int code, String responseBodyStr) { |
| | | try { |
| | | // 获取错误描述和消息 |
| | | String desc = ResponseCodeEnum.getDesc(code); |
| | | String msg = ResponseCodeEnum.getMsg(code); |
| | | // 构造并返回自定义异常 |
| | | return new FebsException(ERROR_PREFIX + "{" + msg + "},{" + desc + "}"); |
| | | } catch (JSONException e) { |
| | | // 如果JSON解析失败,构造并返回自定义异常 |
| | | throw new FebsException(ERROR_PREFIX + "{" + responseBodyStr + "}"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取响应体内容作为字符串 |
| | | * @param body 响应体 |
| | | * @return 响应体字符串 |
| | | * @throws IOException 当读取响应体失败时抛出 |
| | | */ |
| | | private static String getResponseBodyAsString(ResponseBody body) throws IOException { |
| | | if (body != null) { |
| | | // 如果响应体不为空,读取并返回内容 |
| | | return body.string(); |
| | | } else { |
| | | // 如果响应体为空,返回空字符串 |
| | | return ""; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.config; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 类说明:统一软账号配置类 |
| | | * 字段说明: |
| | | * - appId: 应用ID,用于标识一个应用 |
| | | * - appSecret: 应用密钥,用于验证应用的身份 |
| | | * - requestHandler: 处理HTTP请求的处理器,用于封装HTTP请求的处理逻辑 |
| | | */ |
| | | @Data |
| | | public class UniSoftAccount { |
| | | |
| | | private String appId; |
| | | |
| | | private String appSecret; |
| | | |
| | | // 处理HTTP请求的处理器 |
| | | public RequestHandler requestHandler; |
| | | |
| | | /** |
| | | * 构造方法说明:初始化UniSoftAccount实例 |
| | | * 参数说明: |
| | | * - appId: 应用ID,用于标识一个应用 |
| | | * - appSecret: 应用密钥,用于验证应用的身份 |
| | | * - requestHandler: 处理HTTP请求的处理器,用于封装HTTP请求的处理逻辑 |
| | | */ |
| | | public UniSoftAccount(String appId, String appSecret){ |
| | | this.appId = appId; |
| | | this.appSecret = appSecret; |
| | | this.requestHandler = new RequestHandler(appId, appSecret); |
| | | } |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * 设备请求URL枚举类 |
| | | * 用于定义与设备相关的API请求的URL和方法 |
| | | */ |
| | | @Getter |
| | | public enum DeviceRequestUrlEnum { |
| | | |
| | | /** |
| | | * 设备控制请求配置 |
| | | * 使用POST方法发送设备控制指令 |
| | | */ |
| | | DEVICE_CONTROL(HttpMethod.POST,"/device/control"), |
| | | |
| | | /** |
| | | * 设备分组请求配置 |
| | | * 使用POST方法对设备进行分组操作 |
| | | */ |
| | | DEVICE_GROUP(HttpMethod.POST,"/device/group"), |
| | | |
| | | /** |
| | | * 设备标签请求配置 |
| | | * 使用POST方法添加或修改设备标签 |
| | | */ |
| | | DEVICE_TAG(HttpMethod.POST,"/device/tag"), |
| | | |
| | | /** |
| | | * 设备列表请求配置 |
| | | * 使用POST方法获取设备列表 |
| | | */ |
| | | DEVICE_LIST(HttpMethod.POST,"/device/list"), |
| | | |
| | | /** |
| | | * 设备信息请求配置 |
| | | * 使用POST方法获取特定设备的信息 |
| | | */ |
| | | DEVICE_INFO(HttpMethod.POST,"/device/info"); |
| | | |
| | | // 请求方法,如POST、GET等 |
| | | private HttpMethod requestMethod; |
| | | |
| | | // 请求的URL路径 |
| | | private String requestUrl; |
| | | |
| | | /** |
| | | * 构造函数,初始化设备请求的URL和方法 |
| | | * |
| | | * @param requestMethod HTTP请求方法,例如POST |
| | | * @param requestUrl HTTP请求的URL路径 |
| | | */ |
| | | DeviceRequestUrlEnum(HttpMethod requestMethod, String requestUrl) { |
| | | this.requestMethod = requestMethod; |
| | | this.requestUrl = requestUrl; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.enums; |
| | | |
| | | public enum HttpMethod { |
| | | POST, |
| | | GET, |
| | | PUT, |
| | | DELETE, |
| | | INVALID |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | @Getter |
| | | public enum ProductRequestUrlEnum { |
| | | |
| | | |
| | | PRODUCT_LIST(HttpMethod.POST,"/product/list"); |
| | | |
| | | // 请求方法,如POST、GET等 |
| | | private HttpMethod requestMethod; |
| | | |
| | | // 请求的URL路径 |
| | | private String requestUrl; |
| | | |
| | | /** |
| | | * 构造函数,初始化设备请求的URL和方法 |
| | | * |
| | | * @param requestMethod HTTP请求方法,例如POST |
| | | * @param requestUrl HTTP请求的URL路径 |
| | | */ |
| | | ProductRequestUrlEnum(HttpMethod requestMethod, String requestUrl) { |
| | | this.requestMethod = requestMethod; |
| | | this.requestUrl = requestUrl; |
| | | } |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * 响应码枚举类,用于定义API响应的各种状态码及其含义 |
| | | */ |
| | | @Getter |
| | | public enum ResponseCodeEnum { |
| | | |
| | | // 定义各种错误响应码和成功响应码 |
| | | FAIL_5009(5009, "too many request", "单个设备访问最高限制1次/秒,请勿超过此限制"), |
| | | FAIL_5008(5008, "ip:*.*.*.* is not in white list", "开启了IP检查,但接口访问IP不在白名单内,请在控制台的开发设置中,将IP加入白名单"), |
| | | FAIL_5007(5007, "bad debug key", "指定了debug参数,但未开启调试模式,或者是debug参数不正确或已过期"), |
| | | FAIL_5006(5006, "bad sign", "签名错误,正确签名为 md5(md5(开发者密码) + (url中的ts参数))"), |
| | | FAIL_5005(5005, "please set app secret", "未设置开发者密码,请在控制台的开发设置中进行设置"), |
| | | FAIL_5004(5004, "miss param sign", "请在请求接口中指定sign(签名),这是一个计算值"), |
| | | FAIL_5003(5003, "bad ts", "时间戳错误,ts为请求发生时的时间,是一个动态的值,必须为中国时间"), |
| | | FAIL_5002(5002, "miss param ts", "请在请求接口中指定ts(时间戳),取值为请求时的时间戳,10位数字"), |
| | | FAIL_5001(5001, "miss app id", "请求地址为 https://iot-api.unisoft.cn/{APPID}/,缺少{APPID},请在控制台查看您的appid"), |
| | | SUCCESS(200, "ok", ""); |
| | | |
| | | // 响应码 |
| | | private int code; |
| | | // 简短消息 |
| | | private String msg; |
| | | // 描述信息 |
| | | private String desc; |
| | | |
| | | /** |
| | | * 构造函数,初始化响应码、消息和描述 |
| | | * |
| | | * @param code 响应码 |
| | | * @param msg 简短消息 |
| | | * @param desc 描述信息 |
| | | */ |
| | | ResponseCodeEnum(int code, String msg, String desc) { |
| | | this.code = code; |
| | | this.msg = msg; |
| | | this.desc = desc; |
| | | } |
| | | |
| | | /** |
| | | * 根据响应代码获取描述信息 |
| | | * 该方法用于将系统内部的响应代码转换为人类可读的描述信息 |
| | | * 主要通过遍历枚举类ResponseCodeEnum来匹配传入的代码,并返回对应的描述 |
| | | * 如果没有找到匹配的代码,则返回空字符串 |
| | | * |
| | | * @param code 响应代码,用于查找对应的描述信息 |
| | | * @return 描述信息如果找到匹配的代码,否则返回空字符串 |
| | | */ |
| | | public static String getDesc(int code) { |
| | | for (ResponseCodeEnum item : ResponseCodeEnum.values()) { |
| | | if (item.getCode() == code) { |
| | | return item.getDesc(); |
| | | } |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | /** |
| | | * 根据响应代码获取消息信息 |
| | | * 该方法用于将系统内部的响应代码转换为消息信息 |
| | | * 主要通过遍历枚举类ResponseCodeEnum来匹配传入的代码,并返回对应的消息 |
| | | * 如果没有找到匹配的代码,则返回空字符串 |
| | | * |
| | | * @param code 响应代码,用于查找对应的消息信息 |
| | | * @return 消息信息如果找到匹配的代码,否则返回空字符串 |
| | | */ |
| | | public static String getMsg(int code) { |
| | | for (ResponseCodeEnum item : ResponseCodeEnum.values()) { |
| | | if (item.getCode() == code) { |
| | | return item.getMsg(); |
| | | } |
| | | } |
| | | return ""; |
| | | } |
| | | /** |
| | | * 验证响应码是200 |
| | | */ |
| | | public static boolean isSuccess(int code) { |
| | | return SUCCESS.getCode() == code; |
| | | } |
| | | |
| | | /** |
| | | * 验证响应码不是200 |
| | | */ |
| | | public static boolean isFail(int code) { |
| | | return !isSuccess(code); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.utils; |
| | | |
| | | import org.json.JSONArray; |
| | | import org.json.JSONException; |
| | | import org.json.JSONObject; |
| | | import java.util.ArrayList; |
| | | |
| | | public final class JSONParser { |
| | | |
| | | private JSONParser() { |
| | | } |
| | | |
| | | public static String getJSONStringValue(String json, String key) { |
| | | try { |
| | | JSONObject obj = new JSONObject(json); |
| | | return obj.getString(key); |
| | | } catch (JSONException e) { |
| | | throw new JSONException(String.format("[JSONParser] Failed to get \"%s\" from JSON object", key)); |
| | | } |
| | | } |
| | | |
| | | public static int getJSONIntValue(String json, String key) { |
| | | try { |
| | | JSONObject obj = new JSONObject(json); |
| | | return obj.getInt(key); |
| | | } catch (JSONException e) { |
| | | throw new JSONException(String.format("[JSONParser] Failed to get \"%s\" from JSON object", key)); |
| | | } |
| | | } |
| | | |
| | | public static String getJSONArray(ArrayList<?> symbols, String key) { |
| | | try { |
| | | JSONArray arr = new JSONArray(symbols); |
| | | return arr.toString(); |
| | | } catch (JSONException e) { |
| | | throw new JSONException(String.format("[JSONParser] Failed to convert \"%s\" to JSON array", key)); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.utils; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import okhttp3.*; |
| | | import org.json.JSONObject; |
| | | |
| | | import javax.net.ssl.SSLContext; |
| | | import javax.net.ssl.SSLSocketFactory; |
| | | import javax.net.ssl.TrustManager; |
| | | import javax.net.ssl.X509TrustManager; |
| | | import java.io.IOException; |
| | | import java.net.URLEncoder; |
| | | import java.security.SecureRandom; |
| | | import java.security.cert.X509Certificate; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | import java.util.concurrent.Semaphore; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * OkHttp请求工具封装 |
| | | * 参考:https://blog.csdn.net/DwZ735660836/article/details/119976068 |
| | | */ |
| | | @Slf4j |
| | | public class OkHttpUtils { |
| | | public static volatile OkHttpClient okHttpClient = null; |
| | | private static volatile Semaphore semaphore = null; |
| | | private Map<String, String> headerMap; |
| | | private Map<String, String> paramMap; |
| | | private String url; |
| | | private Request.Builder request; |
| | | // 开发环境用的 ShadowsocksR-dotnet4.0 免费版本 正式环境得使用外网服务器 |
| | | // 安易代理 http://127.0.0.1:10809/ http://127.0.0.1:10808/ |
| | | |
| | | /** |
| | | * 初始化okHttpClient,并且允许https访问 |
| | | */ |
| | | private OkHttpUtils() { |
| | | if (okHttpClient == null) { |
| | | synchronized (OkHttpUtils.class) { |
| | | if (okHttpClient == null) { |
| | | TrustManager[] trustManagers = buildTrustManagers(); |
| | | okHttpClient = new OkHttpClient.Builder() |
| | | .connectTimeout(30, TimeUnit.SECONDS) |
| | | .writeTimeout(20, TimeUnit.SECONDS) |
| | | .readTimeout(60, TimeUnit.SECONDS) |
| | | .sslSocketFactory(createSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0]) |
| | | //.hostnameVerifier((hostName, session) -> true) |
| | | //配置自定义连接池参数 |
| | | .connectionPool(new ConnectionPool(5, 60, TimeUnit.SECONDS)) |
| | | .retryOnConnectionFailure(true) |
| | | .build(); |
| | | addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"); |
| | | addHeader("Connection", "close"); |
| | | addHeader("Accept-Encoding", "identity"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 用于异步请求时,控制访问线程数,返回结果 |
| | | * |
| | | * @return |
| | | */ |
| | | private static Semaphore getSemaphoreInstance() { |
| | | //只能1个线程同时访问 |
| | | synchronized (OkHttpUtils.class) { |
| | | if (semaphore == null) { |
| | | semaphore = new Semaphore(0); |
| | | } |
| | | } |
| | | return semaphore; |
| | | } |
| | | |
| | | /** |
| | | * 创建OkHttpUtils |
| | | * |
| | | * @return |
| | | */ |
| | | public static OkHttpUtils builder() { |
| | | return new OkHttpUtils(); |
| | | } |
| | | |
| | | /** |
| | | * 添加url |
| | | * |
| | | * @param url |
| | | * @return |
| | | */ |
| | | public OkHttpUtils url(String url) { |
| | | this.url = url; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 添加参数 |
| | | * |
| | | * @param key 参数名 |
| | | * @param value 参数值 |
| | | * @return |
| | | */ |
| | | public OkHttpUtils addParam(String key, String value) { |
| | | if (paramMap == null) { |
| | | paramMap = new LinkedHashMap<>(16); |
| | | } |
| | | paramMap.put(key, value); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 添加请求头 |
| | | * |
| | | * @param key 参数名 |
| | | * @param value 参数值 |
| | | * @return |
| | | */ |
| | | public OkHttpUtils addHeader(String key, String value) { |
| | | if (headerMap == null) { |
| | | headerMap = new LinkedHashMap<>(16); |
| | | } |
| | | headerMap.put(key, value); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 初始化get方法 |
| | | * |
| | | * @return |
| | | */ |
| | | public OkHttpUtils get() { |
| | | request = new Request.Builder().get(); |
| | | StringBuilder urlBuilder = new StringBuilder(url); |
| | | if (paramMap != null) { |
| | | urlBuilder.append("?"); |
| | | try { |
| | | for (Map.Entry<String, String> entry : paramMap.entrySet()) { |
| | | urlBuilder.append(URLEncoder.encode(entry.getKey(), "utf-8")). |
| | | append("="). |
| | | append(URLEncoder.encode(entry.getValue(), "utf-8")). |
| | | append("&"); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | urlBuilder.deleteCharAt(urlBuilder.length() - 1); |
| | | } |
| | | request.url(urlBuilder.toString()); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 初始化post方法 |
| | | * |
| | | * @param isJsonPost true等于json的方式提交数据,类似postman里post方法的raw |
| | | * false等于普通的表单提交 |
| | | * @return |
| | | */ |
| | | public OkHttpUtils post(boolean isJsonPost) { |
| | | RequestBody requestBody; |
| | | if (isJsonPost) { |
| | | String json = ""; |
| | | if (paramMap != null) { |
| | | json = JSONObject.valueToString(paramMap); |
| | | } |
| | | requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json); |
| | | } else { |
| | | FormBody.Builder formBody = new FormBody.Builder(); |
| | | if (paramMap != null) { |
| | | paramMap.forEach(formBody::add); |
| | | } |
| | | requestBody = formBody.build(); |
| | | } |
| | | request = new Request.Builder().post(requestBody).url(url); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 同步请求 |
| | | * |
| | | * @return |
| | | */ |
| | | public Request.Builder sync() { |
| | | return setHeader(request); |
| | | } |
| | | |
| | | /** |
| | | * 同步请求 |
| | | * |
| | | * @return |
| | | */ |
| | | public String syncStr() { |
| | | setHeader(request); |
| | | try { |
| | | Response response = okHttpClient.newCall(request.build()).execute(); |
| | | assert response.body() != null; |
| | | return response.body().string(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | return "请求失败:" + e.getMessage(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 异步请求,有返回值 |
| | | */ |
| | | public String async() { |
| | | StringBuilder buffer = new StringBuilder(""); |
| | | setHeader(request); |
| | | okHttpClient.newCall(request.build()).enqueue(new Callback() { |
| | | @Override |
| | | public void onFailure(Call call, IOException e) { |
| | | buffer.append("请求出错:").append(e.getMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public void onResponse(Call call, Response response) throws IOException { |
| | | assert response.body() != null; |
| | | buffer.append(response.body().string()); |
| | | getSemaphoreInstance().release(); |
| | | } |
| | | }); |
| | | try { |
| | | getSemaphoreInstance().acquire(); |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return buffer.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 异步请求,带有接口回调 |
| | | * |
| | | * @param callBack |
| | | */ |
| | | public void async(ICallBack callBack) { |
| | | setHeader(request); |
| | | okHttpClient.newCall(request.build()).enqueue(new Callback() { |
| | | @Override |
| | | public void onFailure(Call call, IOException e) { |
| | | callBack.onFailure(call, e.getMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public void onResponse(Call call, Response response) throws IOException { |
| | | assert response.body() != null; |
| | | callBack.onSuccessful(call, response.body().string()); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 为request添加请求头 |
| | | * |
| | | * @param request |
| | | */ |
| | | private Request.Builder setHeader(Request.Builder request) { |
| | | if (headerMap != null) { |
| | | try { |
| | | for (Map.Entry<String, String> entry : headerMap.entrySet()) { |
| | | request.addHeader(entry.getKey(), entry.getValue()); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | return request; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 生成安全套接字工厂,用于https请求的证书跳过 |
| | | * |
| | | * @return |
| | | */ |
| | | private static SSLSocketFactory createSSLSocketFactory(TrustManager[] trustAllCerts) { |
| | | SSLSocketFactory ssfFactory = null; |
| | | try { |
| | | SSLContext sc = SSLContext.getInstance("SSL"); |
| | | sc.init(null, trustAllCerts, new SecureRandom()); |
| | | ssfFactory = sc.getSocketFactory(); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return ssfFactory; |
| | | } |
| | | |
| | | private static TrustManager[] buildTrustManagers() { |
| | | return new TrustManager[]{ |
| | | new X509TrustManager() { |
| | | @Override |
| | | public void checkClientTrusted(X509Certificate[] chain, String authType) { |
| | | } |
| | | |
| | | @Override |
| | | public void checkServerTrusted(X509Certificate[] chain, String authType) { |
| | | } |
| | | |
| | | @Override |
| | | public X509Certificate[] getAcceptedIssuers() { |
| | | return new X509Certificate[]{}; |
| | | } |
| | | } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * 自定义一个接口回调 |
| | | */ |
| | | public interface ICallBack { |
| | | |
| | | void onSuccessful(Call call, String data); |
| | | |
| | | void onFailure(Call call, String errorMsg); |
| | | |
| | | } |
| | | |
| | | public static void main(String[] args) { |
| | | String url = "https://api2.binance.com/api/v3/ticker/24hr?symbol=BNBUSDT&type=MINI"; |
| | | String result = OkHttpUtils.builder() |
| | | .url(url) |
| | | .addHeader("Content-Type", "application/x-www-form-urlencoded") |
| | | .get() |
| | | .syncStr(); |
| | | System.out.println(result); |
| | | } |
| | | } |
| | | |
| | | |
New file |
| | |
| | | package cc.mrbird.febs.unisoftiot.utils; |
| | | |
| | | import cc.mrbird.febs.unisoftiot.enums.DeviceRequestUrlEnum; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.date.DateUtil; |
| | | import cn.hutool.crypto.SecureUtil; |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * URL工具类,用于构造请求物联网控制台接口的URL |
| | | */ |
| | | public class UrlUtils { |
| | | |
| | | /** |
| | | * http(s)://iot-api.unisoft.cn/{AppID}/{接口列表中的path}/?{其他参数}&sign={sign}&ts={ts} |
| | | * |
| | | * sign 签名 所有请求物联网控制台接口,均需在url中携带此参数sign={sign} |
| | | * 取值方法:md5(md5(开发者密码) + 上面的ts参数),32位字符串 |
| | | * ts 时间戳 所有请求物联网控制台接口,均需在url中携带此参数ts={timestap} |
| | | * 取值方法:请求时间(timezone,东八区),10位数字 |
| | | */ |
| | | public static void main(String[] args) { |
| | | System.out.println(getUrl(BASE_URL,APP_ID,APP_SECRET, DeviceRequestUrlEnum.DEVICE_CONTROL.getRequestUrl(), null)); |
| | | LinkedHashMap<String, Object> parameters = new LinkedHashMap<>(); |
| | | parameters.put("group", "123456789"); |
| | | parameters.put("product", "test"); |
| | | System.out.println(getUrl(BASE_URL,APP_ID,APP_SECRET,"device/info",parameters)); |
| | | } |
| | | // todo 优化到后台配置中 |
| | | public static final String BASE_URL = " http://iot-api.unisoft.cn/rtyVWcgmlr"; |
| | | public static final String APP_ID = "rtyVWcgmlr"; |
| | | public static final String APP_SECRET = "rtyVWcgmlr"; |
| | | /** |
| | | * 构造请求物联网控制台接口的URL |
| | | * |
| | | * @param baseUrl 基础URL地址 |
| | | * @param appId 应用ID,用于标识应用 |
| | | * @param appSecret 应用密钥,用于生成签名 |
| | | * @param requestPath 请求路径,标识具体的接口 |
| | | * @param parameters 请求参数,包含除sign和ts之外的其他参数 |
| | | * @return 返回构造完成的URL字符串 |
| | | * |
| | | * 该方法首先会构建基础URL,然后添加appId和请求路径,接着添加其他参数, |
| | | * 最后通过appSecret和时间戳生成签名(sign),以及添加时间戳(ts)到URL中。 |
| | | */ |
| | | public static String getUrl(String baseUrl, |
| | | String appId, |
| | | String appSecret, |
| | | String requestPath, |
| | | LinkedHashMap<String, Object> parameters){ |
| | | StringBuilder fullUrl = new StringBuilder(baseUrl); |
| | | // fullUrl.append("/"+appId); |
| | | fullUrl.append(requestPath); |
| | | fullUrl.append("?"); |
| | | //判断parameters参数是否为空,不为空则拼接在fullUrl |
| | | parametersSetToUrl(fullUrl,parameters); |
| | | getSignAndTs(appSecret,fullUrl,parameters); |
| | | return fullUrl.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 将查询参数附加到URL路径上 |
| | | * 此方法接受一个URL路径和一个查询参数的映射,然后将这些参数附加到URL路径上 |
| | | * 如果没有查询参数,URL路径保持不变 |
| | | * |
| | | * @param urlPath URL路径的StringBuilder对象,用于构建最终的URL |
| | | * @param parameters 查询参数的LinkedHashMap,键为参数名,值为参数值 |
| | | * @return 返回包含查询参数的URL路径的StringBuilder对象 |
| | | */ |
| | | public static StringBuilder parametersSetToUrl(StringBuilder urlPath, LinkedHashMap<String, Object> parameters) { |
| | | // 检查参数是否为空,如果为空则直接返回URL路径 |
| | | if (parameters == null || parameters.isEmpty()) { |
| | | return urlPath; |
| | | } |
| | | joinQueryParameters(urlPath, parameters); |
| | | // 返回包含查询参数的URL路径 |
| | | return urlPath; |
| | | } |
| | | |
| | | /** |
| | | * 将查询参数拼接到URL路径后 |
| | | * |
| | | * @param urlPath URL路径 |
| | | * @param parameters 查询参数 |
| | | * @return 拼接后的URL路径 |
| | | */ |
| | | public static StringBuilder joinQueryParameters(StringBuilder urlPath, LinkedHashMap<String, Object> parameters) { |
| | | if (parameters == null || parameters.isEmpty()) { |
| | | return urlPath; |
| | | } |
| | | |
| | | boolean isFirst = true; |
| | | for (Map.Entry<String, Object> mapElement : parameters.entrySet()) { |
| | | Object value = mapElement.getValue(); |
| | | // 根据是否是第一个参数来决定是否添加& |
| | | if (isFirst) { |
| | | isFirst = false; |
| | | } else { |
| | | urlPath.append('&'); |
| | | } |
| | | |
| | | // 拼接参数到URL路径 |
| | | urlPath.append(mapElement.getKey()) |
| | | .append('=') |
| | | .append(urlEncode(value.toString())); |
| | | } |
| | | return urlPath; |
| | | } |
| | | |
| | | /** |
| | | * 对URL参数进行编码 |
| | | * |
| | | * @param s 参数字符串 |
| | | * @return 编码后的参数字符串 |
| | | */ |
| | | public static String urlEncode(String s) { |
| | | try { |
| | | return URLEncoder.encode(s, StandardCharsets.UTF_8.name()); |
| | | } catch (UnsupportedEncodingException e) { |
| | | throw new RuntimeException(StandardCharsets.UTF_8.name() + " is unsupported", e); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 生成签名和时间戳参数 |
| | | * 该方法用于给定的URL路径添加签名(sign)和时间戳(ts)参数,以确保请求的安全性和有效性 |
| | | * 签名是通过将appSecret进行MD5加密,然后与当前时间戳拼接后再进行MD5加密生成 |
| | | * 这种方法确保了每个请求都有一个唯一的、基于时间的签名,从而防止了重放攻击 |
| | | * |
| | | * @param appSecret 应用密钥,用于生成签名 |
| | | * @param urlPath StringBuilder对象,用于拼接URL路径和查询参数 |
| | | * @param parameters 请求参数,用于判断是否需要添加额外的连接符 |
| | | * @return 返回包含签名和时间戳参数的URL路径字符串 |
| | | */ |
| | | public static String getSignAndTs(String appSecret,StringBuilder urlPath, LinkedHashMap<String, Object> parameters) { |
| | | //使用MD5加密appSecret |
| | | String md5AppSecret = SecureUtil.md5(appSecret); |
| | | //获取当前时间戳 |
| | | String timestamp = String.valueOf(DateUtil.currentSeconds()); |
| | | //生成sign参数,通过md5加密(appSecret的md5值 + 时间戳) |
| | | String md5Sign = SecureUtil.md5(md5AppSecret + timestamp); |
| | | //返回包含sign和ts参数的字符串 |
| | | if (CollUtil.isNotEmpty(parameters)) { |
| | | urlPath.append("&"); |
| | | } |
| | | urlPath.append("sign="); |
| | | urlPath.append(md5Sign); |
| | | urlPath.append("&ts="); |
| | | urlPath.append(timestamp); |
| | | return urlPath.toString(); |
| | | } |
| | | |
| | | } |
| | |
| | | datasource: |
| | | # 数据源-1,名称为 base |
| | | base: |
| | | username: blnka |
| | | password: blnka!@#123 |
| | | username: db_e2 |
| | | password: db_e20806123!@# |
| | | driver-class-name: com.mysql.cj.jdbc.Driver |
| | | url: jdbc:mysql://127.0.0.1:3306/db_blnka?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8 |
| | | url: jdbc:mysql://127.0.0.1:3306/db_e2?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8 |
| | | |
| | | redis: |
| | | # Redis数据库索引(默认为 0) |
| | |
| | | # Redis服务器地址 |
| | | host: 127.0.0.1 |
| | | # Redis服务器连接端口 |
| | | port: 6279 |
| | | port: 6379 |
| | | # Redis 密码 |
| | | password: blnka!@#123 |
| | | password: lianghua1!qaz2@WSX |
| | | lettuce: |
| | | pool: |
| | | # 连接池中的最小空闲连接 |
| | |
| | | rabbitmq: |
| | | host: 127.0.0.1 |
| | | port: 5672 |
| | | username: blnka |
| | | password: blnka123 |
| | | username: e20210816 |
| | | password: e20210816 |
| | | publisher-confirm-type: correlated |
| | | |
| | | pay: |