feat:云音箱
This commit is contained in:
1 parent
434cb1b258
commit
5f343be27d
8 files changed
+316
No files matched your search
+2
@@ -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;
|
||||
|
||||
+26
@@ -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;
|
||||
|
||||
}
|
||||
+41
@@ -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<Boolean> send() {
|
||||
// yyxService.send(2030260730023755777L, 100, "17683843144");
|
||||
// return R.ok(true);
|
||||
// }
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.bsd.pjcloud.hard.service;
|
||||
|
||||
/**
|
||||
* 云音箱
|
||||
*/
|
||||
public interface YyxService {
|
||||
|
||||
void send(Long mchId, Integer payPrice, String phone);
|
||||
|
||||
}
|
||||
+2
@@ -134,6 +134,7 @@ public class HardManagerServiceImpl extends ServiceImpl<HardManagerMapper, HardM
|
||||
}
|
||||
dto.setHardGroupId(deptHardGroup.getHardGroupId());
|
||||
dto.setDeptId(mch.getDeptId());
|
||||
dto.setUniqueCode(dto.getUniqueCode());
|
||||
// mq地址和端口
|
||||
dto.setMqSocketAddress(mqttConfig.getAndroidAddress());
|
||||
dto.setMqSocketPort(mqttConfig.getAndroidPort());
|
||||
@@ -169,6 +170,7 @@ public class HardManagerServiceImpl extends ServiceImpl<HardManagerMapper, HardM
|
||||
throw new RuntimeException("请设置顶级部门硬件分组");
|
||||
}
|
||||
dto.setDeptId(mch.getDeptId());
|
||||
dto.setUniqueCode(dto.getUniqueCode());
|
||||
dto.setHardGroupId(deptHardGroup.getHardGroupId());
|
||||
if (!ObjectUtil.equals(dto.getMerchantId(), detail.getMerchantId())) {
|
||||
// topic
|
||||
|
||||
+8
@@ -175,6 +175,7 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
private final HardThirdMiOrderMapper hardThirdMiOrderMapper;
|
||||
private final RemoteCanteenDeliveryOrderService remoteCanteenDeliveryOrderService;
|
||||
private final YyxService yyxService;
|
||||
|
||||
@Override
|
||||
public Page<HardOrderVo> pagejoin(Page page, HardOrderDto dto) {
|
||||
@@ -401,6 +402,13 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
}
|
||||
// 发送mq消息
|
||||
getSelf().createOrderAfterSendMqMsg(orderObj.getId(), userDetail);
|
||||
// 云音箱播报
|
||||
String userPhone = Optional.of(userDetail).map(UserInfo::getSysUser).map(SysUser -> SysUser.getPhone()).orElse(null);
|
||||
CompletableFuture.runAsync(() -> {
|
||||
if (ObjectUtil.isNotEmpty(userPhone)) {
|
||||
yyxService.send(orderObj.getMerchantId(), orderObj.getPayPrice(), userPhone);
|
||||
}
|
||||
});
|
||||
return orderObj.getId();
|
||||
}
|
||||
|
||||
|
||||
+216
@@ -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<HardManagerEntity> yyxList = hardManagerMapper.selectList(new LambdaQueryWrapper<HardManagerEntity>().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<String, Object> body = new HashMap<>();
|
||||
body.put("devid", deviceId);
|
||||
body.put("time", time);
|
||||
body.put("reqid", uniqueId);
|
||||
body.put("message", msg);
|
||||
// 可选参数
|
||||
// body.put("type", "1");
|
||||
Map<String, List<String>> 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<String, Object> body = new HashMap<>();
|
||||
body.put("devid", deviceId);
|
||||
body.put("time", time);
|
||||
body.put("reqid", uniqueId);
|
||||
body.put("message", msg);
|
||||
// 可选参数
|
||||
// body.put("type", "1");
|
||||
Map<String, List<String>> 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<String, Object> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
Reference in new issue
Block a user