From 5f343be27dce3e87f660fdc6783eae3495050793 Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Thu, 28 May 2026 14:30:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E4=BA=91=E9=9F=B3=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hard/api/constant/HardTypeEnum.java | 2 + .../com/bsd/pjcloud/hard/config/YyxConf.java | 26 +++ .../controller/yyx/HardYyxController.java | 41 ++++ .../bsd/pjcloud/hard/service/YyxService.java | 10 + .../impl/manager/HardManagerServiceImpl.java | 2 + .../impl/order/HardOrderServiceImpl.java | 8 + .../hard/service/impl/yyx/YyxServiceImpl.java | 216 ++++++++++++++++++ .../src/main/resources/application.yml | 11 + 8 files changed, 316 insertions(+) create mode 100644 pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/config/YyxConf.java create mode 100644 pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/yyx/HardYyxController.java create mode 100644 pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/YyxService.java create mode 100644 pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/yyx/YyxServiceImpl.java diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardTypeEnum.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardTypeEnum.java index 3de623c28..db2bd88f1 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardTypeEnum.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardTypeEnum.java @@ -25,6 +25,8 @@ public enum HardTypeEnum { MERCHANT_ACT("7", "商户活动"), FARMER("8", "共富商城(助农)"), YUN_XFJ("9", "云版消费机"), + YYX_4G("10", "4G-云音响"), + YYX_WIFI("11", "wifi-云音响"), ; private final String type; private final String description; diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/config/YyxConf.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/config/YyxConf.java new file mode 100644 index 000000000..f31d13a93 --- /dev/null +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/config/YyxConf.java @@ -0,0 +1,26 @@ +package com.bsd.pjcloud.hard.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "yyx") +@Data +public class YyxConf { + + private String baseUrl; + + private String username; + + private String secret; + + /** + * 模板 + * ${price}-价格 + * ${phone-last}-手机尾号 + * 浦江智慧后勤,收款${price}元 + */ + private String template; + +} diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/yyx/HardYyxController.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/yyx/HardYyxController.java new file mode 100644 index 000000000..3a2d2b86d --- /dev/null +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/yyx/HardYyxController.java @@ -0,0 +1,41 @@ +package com.bsd.pjcloud.hard.controller.yyx; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bsd.pjcloud.common.core.constant.enums.YesNoEnum; +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.security.annotation.Inner; +import com.bsd.pjcloud.hard.api.entity.HardApptEntity; +import com.bsd.pjcloud.hard.service.HardApptService; +import com.bsd.pjcloud.hard.service.YyxService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 设备预约管理 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/yyx") +@Tag(description = "yyx", name = "云音箱") +@SecurityRequirement(name = HttpHeaders.AUTHORIZATION) +public class HardYyxController { + + private final YyxService yyxService; + +// @Operation(summary = "app预约时间列表", description = "app预约时间列表") +// @GetMapping("/send") +// @Inner(value = false) +// public R send() { +// yyxService.send(2030260730023755777L, 100, "17683843144"); +// return R.ok(true); +// } + +} diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/YyxService.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/YyxService.java new file mode 100644 index 000000000..e3e2987b8 --- /dev/null +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/YyxService.java @@ -0,0 +1,10 @@ +package com.bsd.pjcloud.hard.service; + +/** + * 云音箱 + */ +public interface YyxService { + + void send(Long mchId, Integer payPrice, String phone); + +} diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/manager/HardManagerServiceImpl.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/manager/HardManagerServiceImpl.java index 71aba6dcf..358217f8f 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/manager/HardManagerServiceImpl.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/manager/HardManagerServiceImpl.java @@ -134,6 +134,7 @@ public class HardManagerServiceImpl extends ServiceImpl pagejoin(Page page, HardOrderDto dto) { @@ -401,6 +402,13 @@ public class HardOrderServiceImpl extends ServiceImpl SysUser.getPhone()).orElse(null); + CompletableFuture.runAsync(() -> { + if (ObjectUtil.isNotEmpty(userPhone)) { + yyxService.send(orderObj.getMerchantId(), orderObj.getPayPrice(), userPhone); + } + }); return orderObj.getId(); } diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/yyx/YyxServiceImpl.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/yyx/YyxServiceImpl.java new file mode 100644 index 000000000..30cb82298 --- /dev/null +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/yyx/YyxServiceImpl.java @@ -0,0 +1,216 @@ +package com.bsd.pjcloud.hard.service.impl.yyx; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSON; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.bsd.pjcloud.hard.api.constant.HardTypeEnum; +import com.bsd.pjcloud.hard.api.entity.manager.HardManagerEntity; +import com.bsd.pjcloud.hard.config.YyxConf; +import com.bsd.pjcloud.hard.mapper.manager.HardManagerMapper; +import com.bsd.pjcloud.hard.service.YyxService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Validated +@Slf4j +@RequiredArgsConstructor +@Service +public class YyxServiceImpl implements YyxService { + + private final String TAG = "4G云音箱"; + + private final YyxConf yyxConf; + + private final HardManagerMapper hardManagerMapper; + + private final StringRedisTemplate stringRedisTemplate; + + private static final String YYX_TOKEN_CACHE_KEY = "hard:yyx:token"; + + private static final long YYX_TOKEN_EXPIRE_HOURS = 1; + + @Override + public void send(Long mchId, Integer payPrice, String phone) { + try { + if (!ObjectUtil.isAllNotEmpty(mchId, payPrice, phone)) { + return; + } + // 查询商户绑定的云音箱设备 + List yyxList = hardManagerMapper.selectList(new LambdaQueryWrapper().eq(HardManagerEntity::getMerchantId, mchId) + .in(HardManagerEntity::getType, List.of(HardTypeEnum.YYX_4G.getType(), HardTypeEnum.YYX_WIFI.getType())) + ); + if (ObjectUtil.isEmpty(yyxList)) { + return; + } + String yuanPrice = fenToYuan(payPrice); + String lastPhone = phoneToLastPhone(phone); + yyxList.forEach(v -> { + // 4G版云音箱 + if (ObjectUtil.equals(v.getType(), HardTypeEnum.YYX_4G.getType())) { + String template = yyxConf.getTemplate(); + // 把`template`中的`${price}`替换为`yuanPrice` + template = template.replace("${price}", yuanPrice); + // 把`template`中的`${phone}`替换为`lastPhone` + template = template.replace("${phone-last}", lastPhone); + yyx4gHttpSend(v.getUniqueCode(), template); + } + // WIFI版云音箱 + else if (ObjectUtil.equals(v.getType(), HardTypeEnum.YYX_WIFI.getType())) { + yyxWifiHttpSend(v.getUniqueCode(), ""); + } + }); + } catch (Exception e) { + log.error("|{}|发送失败,异常:{}", TAG, e.getMessage()); + } + } + + // 4G版云音箱发送语音 + private boolean yyx4gHttpSend(String deviceId, String msg) { + try { + String token = getYyxGetToken(); + if (ObjectUtil.isEmpty(token)) { + return false; + } + String uniqueId = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) + RandomUtil.randomString(8); + String time = LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN); + + HashMap body = new HashMap<>(); + body.put("devid", deviceId); + body.put("time", time); + body.put("reqid", uniqueId); + body.put("message", msg); + // 可选参数 + // body.put("type", "1"); + Map> headers = new HashMap<>(); + headers.put("Content-Type", List.of("application/json")); + headers.put("token", List.of(token)); + + String res = HttpUtil.createPost(yyxConf.getBaseUrl() + "/api/message/sendMsg").body(JSONUtil.toJsonStr(body)).header(headers).execute().body(); + if (ObjectUtil.isEmpty(res)) { + log.error("|{}|4G版云音箱发送语音,发送失败,返回结果错误res:|{}|", res, TAG); + } + JSON resJson = JSONUtil.parse(res); + String code = resJson.getByPath("code", String.class); + + return true; + } catch (Exception e) { + e.printStackTrace(); + log.error("|{}|4G版云音箱发送语音,发送失败,异常:{}", TAG, e.getMessage()); + return false; + } + } + + // wifi版云音箱发送语音 + private boolean yyxWifiHttpSend(String deviceId, String msg) { + try { + String token = getYyxGetToken(); + if (ObjectUtil.isEmpty(token)) { + return false; + } + String uniqueId = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) + RandomUtil.randomString(8); + String time = LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN); + + HashMap body = new HashMap<>(); + body.put("devid", deviceId); + body.put("time", time); + body.put("reqid", uniqueId); + body.put("message", msg); + // 可选参数 + // body.put("type", "1"); + Map> headers = new HashMap<>(); + headers.put("Content-Type", List.of("application/json")); + headers.put("token", List.of(token)); + + String res = HttpUtil.createPost(yyxConf.getBaseUrl() + "/api/message/sendMsg").body(JSONUtil.toJsonStr(body)).header(headers).execute().body(); + if (ObjectUtil.isEmpty(res)) { + log.error("|{}|获取云音箱token,发送失败,返回结果错误res:|{}|", res, TAG); + } + + return true; + } catch (Exception e) { + e.printStackTrace(); + log.error("|{}|wifi版云音箱发送语音,发送失败,异常:{}", TAG, e.getMessage()); + return false; + } + } + + // 获取云音箱token + private String getYyxGetToken() { + String cachedToken = stringRedisTemplate.opsForValue().get(YYX_TOKEN_CACHE_KEY); + if (ObjectUtil.isNotEmpty(cachedToken)) { + log.debug("|{}|从Redis缓存获取云音箱token", TAG); + return cachedToken; + } + long timestamp = System.currentTimeMillis(); + String orgSign = "secret=" + yyxConf.getSecret() + "×=" + timestamp + "&username=" + yyxConf.getUsername(); + // md5加密 + String digestHex = DigestUtils.md5Hex(orgSign.getBytes(StandardCharsets.UTF_8)).toUpperCase(); + try { + HashMap body = new HashMap<>(); + body.put("username", yyxConf.getUsername()); + body.put("password", digestHex); + body.put("times", timestamp); + String res = HttpUtil.post(yyxConf.getBaseUrl() + "/api/getToken", JSONUtil.toJsonStr(body)); + if (ObjectUtil.isEmpty(res)) { + log.error("|{}|获取云音箱token,发送失败,返回结果为空", TAG); + return null; + } + JSON resJson = JSONUtil.parse(res); + String data = resJson.getByPath("data", String.class); + if (ObjectUtil.isEmpty(data)) { + log.error("|{}|获取云音箱token,发送失败,返回结果错误res:|{}|", res, TAG); + } + JSON resData = JSONUtil.parse(data); + if (ObjectUtil.isEmpty(resData)) { + log.error("|{}|获取云音箱token,发送失败,返回结果错误res:|{}|", res, TAG); + } + String token = resData.getByPath("token", String.class); + if (ObjectUtil.isEmpty(token)) { + log.error("|{}|获取云音箱token,发送失败,返回结果错误,token为空,res:|{}|", res, TAG); + } + // redis缓存 + stringRedisTemplate.opsForValue().set(YYX_TOKEN_CACHE_KEY, token, YYX_TOKEN_EXPIRE_HOURS, TimeUnit.HOURS); + log.debug("|{}|云音箱token已缓存到Redis,过期时间:{}小时", TAG, YYX_TOKEN_EXPIRE_HOURS); + return token; + } catch (Exception e) { + e.printStackTrace(); + log.error("|{}|获取云音箱token,发送失败,异常:{}", TAG, e.getMessage()); + return null; + } + } + + private String phoneToLastPhone(String phone) { + if (ObjectUtil.isEmpty(phone)) { + return ""; + } + return phone.substring(phone.length() - 4); + } + + private String fenToYuan(Integer payPrice) { + if (ObjectUtil.isEmpty(payPrice)) { + return "0"; + } + BigDecimal fen = new BigDecimal(payPrice); + return fen.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString(); + } + +} diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/resources/application.yml b/pjcloud-hardware/pjcloud-hardware-biz/src/main/resources/application.yml index dfd0a4f96..3ac0ed91c 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/resources/application.yml +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/resources/application.yml @@ -52,3 +52,14 @@ mini-ali-pay: alipay-public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqnCB+N172m5gxf1Rolfx5LErSiVwQCh0XQdDrVy6aTuVF5EtdqahSHtADyEPsBWudMBhzCfqpoZvfnvH7a+PED6uZ3DoXt7ETh8TmNM5yc+WTnD8pSphQvuHDJ3zMks489KNxiX6sXag71k9H1MXklJGPOUaljHqy/QlRn2ZLT7pFgaFH9LEcVoQBPIOurUn95078h848RFD30jZFW3fakxUGAGVVZaHH62z5OEWOmcA+CwxsEDMa1XMt02RGXgAAq4WOj+BqOGiFmhM8rnrUMchTyvx4umOzGvbF2Y9irN3aBZ5lfg+bzYcJpIOMzlM/V00LnEWzCblJzXNyH4hJQIDAQAB callback-url: https://rj4jv5rwqs.by.takin.cc/hard/hard-ali-pay/callback # callback-url: https://stgl.pj.gov.cn/api/hard/hard-ali-pay/callback + +yyx: + base-url: https://open.sw-aiot.com + username: zianxin + secret: 67ed0e41427f754f6c7bc572b60f4162 + template: 购买成功总金额{price}元 + +#yyx: +# base-url: https://open.sw-aiot.com +# username: ahuang +# secret: 742c07b682cd6de9851f35b80adb61fd \ No newline at end of file