1、增加每天23:59:50定时任务,将当天的工单 - 已开始未结束的结束时间设置为23:59:59

2、生成工单定时任务增加逻辑处理:手动触发定时任务后,如果已经派发过对应工单不再重复派发
3、涂鸦相关接口
This commit is contained in:
1378012178@qq.com 2026-04-02 14:52:23 +08:00
parent 9957093846
commit bc3463cdd3
10 changed files with 425 additions and 433 deletions

View File

@ -1,7 +1,9 @@
package com.nu.modules.commonutils;
import com.fasterxml.jackson.databind.JsonNode;
import com.nu.utils.TuyaApiUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nu.connector.AirConditionerConnector;
import com.nu.connector.DeviceConnector;
import com.nu.entity.CommandResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -12,112 +14,271 @@ import java.util.*;
public class TuyaAirconApi {
@Autowired
private TuyaApiUtil tuyaApiUtil;
private DeviceConnector deviceConnector;
private String deviceId = "6c36cb2d2c352683bfnsm1";
@Autowired
private AirConditionerConnector airConditionerConnector;
/**
* 测试连接
* 获取设备信息
*
* @param deviceId 设备ID
*/
@GetMapping("/test")
public Map<String, Object> test() {
Map<String, Object> result = new HashMap<>();
@GetMapping("/getDeviceById")
public Map<String, Object> getDeviceById(@RequestParam String deviceId) {
try {
String token = tuyaApiUtil.getAccessToken();
result.put("success", true);
result.put("message", "连接成功");
result.put("token", token);
Map<String, Object> device = deviceConnector.getDeviceById(deviceId);
return Map.of(
"success", true,
"message", "连接成功",
"data", device
);
} catch (Exception e) {
result.put("success", false);
result.put("message", "连接失败: " + e.getMessage());
e.printStackTrace();
return Map.of(
"success", false,
"message", "连接失败: " + e.getMessage()
);
}
return result;
}
/**
* 获取设备状态
* 获取设备属性
*
* @param deviceId 设备ID
*/
@GetMapping("/status")
public Map<String, Object> getDeviceStatus() {
Map<String, Object> result = new HashMap<>();
@GetMapping("/getDeviceProperties")
public Map<String, Object> getDeviceProperties(@RequestParam String deviceId) {
try {
JsonNode response = tuyaApiUtil.getDeviceStatus(deviceId);
result.put("success", true);
result.put("data", response);
Map<String, Object> device = deviceConnector.getDeviceProperties(deviceId);
return Map.of(
"success", true,
"message", "查询属性成功",
"data", device
);
} catch (Exception e) {
result.put("success", false);
result.put("message", "获取设备状态失败: " + e.getMessage());
e.printStackTrace();
return Map.of(
"success", false,
"message", "查询属性失败: " + e.getMessage()
);
}
return result;
}
/**
* 获取设备功能点规格
* 获取设备期望属性
*
* @param deviceId 设备ID
*/
@GetMapping("/specifications")
public Map<String, Object> getDeviceSpecifications() {
Map<String, Object> result = new HashMap<>();
@GetMapping("/getDeviceDesiredProperties")
public Map<String, Object> getDeviceDesiredProperties(@RequestParam String deviceId) {
try {
JsonNode response = tuyaApiUtil.getDeviceSpecifications(deviceId);
result.put("success", true);
result.put("data", response);
Map<String, Object> device = deviceConnector.getDeviceDesiredProperties(deviceId);
return Map.of(
"success", true,
"message", "查询期望成功",
"data", device
);
} catch (Exception e) {
result.put("success", false);
result.put("message", "获取设备规格失败: " + e.getMessage());
e.printStackTrace();
return Map.of(
"success", false,
"message", "查询期望失败: " + e.getMessage()
);
}
return result;
}
/**
* 发送控制指令单个指令
* @param code 功能点code
* @param value 功能点值
* 获取设备期望属性
*
* @param deviceId 设备ID
*/
@PostMapping("/control/single")
public Map<String, Object> sendSingleCommand(
@GetMapping("/getDeviceFunctions")
public Map<String, Object> getDeviceFunctions(@RequestParam String deviceId) {
try {
Map<String, Object> device = deviceConnector.getDeviceFunctions(deviceId);
return Map.of(
"success", true,
"message", "查询设备指令集成功",
"data", device
);
} catch (Exception e) {
return Map.of(
"success", false,
"message", "查询设备指令集失败: " + e.getMessage()
);
}
}
/**
* 发送控制指令单个
*
* @param deviceId 设备ID
* @param code 功能点code
* @param value 功能点值
*/
@PostMapping("/control")
public Map<String, Object> sendCommand(
@RequestParam String deviceId,
@RequestParam String code,
@RequestParam Object value) {
Map<String, Object> result = new HashMap<>();
@RequestParam String value) {
try {
Map<String, Object> command = new HashMap<>();
command.put("code", code);
command.put("value", value);
// 1. 构建 input_params注意是 JSON 字符串
Map<String, Object> params = new HashMap<>();
params.put(code, value);
List<Map<String, Object>> commands = new ArrayList<>();
commands.add(command);
// 👉 转成 JSON 字符串
String inputParams = new com.fasterxml.jackson.databind.ObjectMapper()
.writeValueAsString(params);
// 2. 构建 body
Map<String, Object> body = new HashMap<>();
body.put("code", code); // 动作 code
body.put("input_params", inputParams); // JSON字符串
// 3. 调用
Map<String, Object> result = deviceConnector.sendAction(deviceId, body);
return result;
JsonNode response = tuyaApiUtil.sendControlCommand(deviceId, commands);
result.put("success", true);
result.put("message", "指令发送成功");
result.put("data", response);
} catch (Exception e) {
result.put("success", false);
result.put("message", "指令发送失败: " + e.getMessage());
e.printStackTrace();
return Map.of("success", false, "message", e.getMessage());
}
}
@PostMapping("/switch")
public Map<String, Object> switchAircon(
@RequestParam String deviceId,
@RequestParam Boolean on) {
try {
String code = on ? "PowerOn" : "PowerOff";
Map<String, Object> params = new HashMap<>();
params.put(code, code);
String inputParams = new ObjectMapper().writeValueAsString(params);
Map<String, Object> body = new HashMap<>();
body.put("code", code);
body.put("input_params", inputParams);
Map<String, Object> result = deviceConnector.sendAction(deviceId, body);
return result;
} catch (Exception e) {
return Map.of("success", false, "message", e.getMessage());
}
return result;
}
/**
* 发送批量控制指令多个指令
* @param commands 指令列表格式[{"code":"switch","value":true},{"code":"temp_set","value":26}]
* 空调组合按键下发
* 通过组合命令控制空调
*
* @param infraredId 设备ID (infrared_id)
* @param remoteId 遥控器ID (remote_id)
* @param power 电源0-关闭1-打开必填
* @param mode 模式0-制冷1-制热2-自动3-送风4-除湿可选
* @param temp 温度可选
* @param wind 风速0-自动1-2-3-可选
* @param remoteIndex 遥控器索引ID可选
* @param categoryId 品类ID可选
*/
@PostMapping("/control/batch")
public Map<String, Object> sendBatchCommands(@RequestBody List<Map<String, Object>> commands) {
Map<String, Object> result = new HashMap<>();
@PostMapping("/sendCommand")
public Map<String, Object> sendAirconCommand(
@RequestParam String infraredId,
@RequestParam String remoteId,
@RequestParam Integer power,
@RequestParam(required = false) Integer mode,
@RequestParam(required = false) Integer temp,
@RequestParam(required = false) Integer wind,
@RequestParam(required = false) Integer remoteIndex,
@RequestParam(required = false) Integer categoryId) {
try {
JsonNode response = tuyaApiUtil.sendControlCommand(deviceId, commands);
result.put("success", true);
result.put("message", "批量指令发送成功");
result.put("data", response);
// 构建请求体
Map<String, Object> body = new HashMap<>();
body.put("power", power);
if (mode != null) {
body.put("mode", mode);
}
if (temp != null) {
body.put("temp", temp);
}
if (wind != null) {
body.put("wind", wind);
}
if (remoteIndex != null) {
body.put("remote_index", remoteIndex);
}
if (categoryId != null) {
body.put("category_id", categoryId);
}
// 调用 connector 发送命令
Map<String, Object> result = deviceConnector.sendCommandKT(infraredId, remoteId, body);
// 处理返回结果
if (result != null && Boolean.TRUE.equals(result.get("success"))) {
return Map.of(
"success", true,
"message", "空调控制指令发送成功",
"data", result
);
} else {
String errorMsg = result != null ? result.getOrDefault("message", "未知错误").toString() : "返回结果为空";
return Map.of(
"success", false,
"message", "空调控制指令发送失败: " + errorMsg
);
}
} catch (Exception e) {
result.put("success", false);
result.put("message", "批量指令发送失败: " + e.getMessage());
e.printStackTrace();
return Map.of(
"success", false,
"message", "空调控制指令发送异常: " + e.getMessage()
);
}
}
/**
* 空调组合按键下发使用Body方式传参
*
* @param infraredId 设备ID
* @param remoteId 遥控器ID
* @param body 请求参数体
*/
@PostMapping("/sendCommandWithBody")
public Map<String, Object> sendAirconCommandWithBody(
@RequestParam String infraredId,
@RequestParam String remoteId,
@RequestBody Map<String, Object> body) {
try {
// 验证必填参数 power
if (body == null || !body.containsKey("power")) {
return Map.of(
"success", false,
"message", "缺少必填参数: power"
);
}
Map<String, Object> result = deviceConnector.sendCommandKT(infraredId, remoteId, body);
if (result != null && Boolean.TRUE.equals(result.get("success"))) {
return Map.of(
"success", true,
"message", "空调控制指令发送成功",
"data", result
);
} else {
String errorMsg = result != null ? result.getOrDefault("message", "未知错误").toString() : "返回结果为空";
return Map.of(
"success", false,
"message", "空调控制指令发送失败: " + errorMsg
);
}
} catch (Exception e) {
return Map.of(
"success", false,
"message", "空调控制指令发送异常: " + e.getMessage()
);
}
return result;
}
}

View File

@ -46,7 +46,7 @@
<dependency>
<groupId>com.tuya</groupId>
<artifactId>tuya-spring-boot-starter</artifactId>
<version>1.0.0</version>
<version>1.5.4</version>
</dependency>
</dependencies>

View File

@ -0,0 +1,23 @@
package com.nu.connector;
import com.tuya.connector.api.annotations.GET;
import com.tuya.connector.api.annotations.POST;
import com.tuya.connector.api.annotations.Path;
import com.tuya.connector.api.annotations.Body;
import java.util.List;
import java.util.Map;
public interface AirConditionerConnector {
/**
* 获取设备列表
*/
@GET("/v1.0/devices")
Map<String, Object> getDeviceList();
/**
* 空调控制
*/
@POST("/v1.0/devices/{device_id}/commands")
Map<String, Object> control(@Path("device_id") String deviceId, @Body Map<String, Object> command);
}

View File

@ -0,0 +1,60 @@
package com.nu.connector;
import com.nu.entity.CommandResponse;
import com.tuya.connector.api.annotations.Body;
import com.tuya.connector.api.annotations.GET;
import com.tuya.connector.api.annotations.POST;
import com.tuya.connector.api.annotations.Path;
import java.util.List;
import java.util.Map;
public interface DeviceConnector {
/**
* 获取设备详情
*/
@GET("/v2.0/cloud/thing/{device_id}")
Map<String, Object> getDeviceById(@Path("device_id") String deviceId);
/**
* 查询属性
*/
@GET("/v2.0/cloud/thing/{device_id}/shadow/properties")
Map<String, Object> getDeviceProperties(@Path("device_id") String deviceId);
/**
* 查询期望属性
*/
@GET("/v2.0/cloud/thing/{device_id}/shadow/properties/desired")
Map<String, Object> getDeviceDesiredProperties(@Path("device_id") String deviceId);
/**
* 获取设备支持的指令集
*/
@GET("/v1.0/iot-03/devices/{device_id}/functions")
Map<String, Object> getDeviceFunctions(@Path("device_id") String deviceId);
/**
* 发送控制指令
*/
@POST("/v2.0/cloud/thing/{device_id}/shadow/actions")
Map<String, Object> sendAction(@Path("device_id") String deviceId, @Body Map<String, Object> body);
/**
* 发送控制指令
*/
@POST("/v1.0/iot-03/devices/{device_id}/commands")
Map<String, Object> sendCommands2(@Path("device_id") String deviceId, @Body Map<String, Object> body);
/**
* 发送控制指令
*/
// AirConditionerConnector 中修改为
@POST("/v2.0/infrareds/{infrared_id}/air-conditioners/{remote_id}/scenes/command")
Map<String, Object> sendCommandKT(
@Path("infrared_id") String infraredId,
@Path("remote_id") String remoteId,
@Body Map<String, Object> body
);
}

View File

@ -0,0 +1,9 @@
package com.nu.entity;
import lombok.Data;
@Data
public class CommandParams {
private String code;
private Object value;
}

View File

@ -0,0 +1,11 @@
package com.nu.entity;
import lombok.Data;
@Data
public class CommandResponse {
private Boolean result;
private Long t;
private Boolean success;
private String tid;
}

View File

@ -1,330 +0,0 @@
package com.nu.utils;
import cn.hutool.core.util.HexUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.*;
@Slf4j
@Component
public class TuyaApiUtil {
@Value("${connector.ak}")
private String clientId;
@Value("${connector.sk}")
private String secret;
@Value("${connector.region:CN}")
private String region;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
private volatile String accessToken;
private volatile long tokenExpireTime;
private final Object tokenLock = new Object();
public TuyaApiUtil() {
this.restTemplate = new RestTemplate();
this.objectMapper = new ObjectMapper();
}
private String getApiHost() {
switch (region.toUpperCase()) {
case "CN":
return "https://openapi.tuyacn.com";
case "US":
return "https://openapi.tuyaus.com";
case "EU":
return "https://openapi.tuyaeu.com";
case "IN":
return "https://openapi.tuyain.com";
default:
return "https://openapi.tuyacn.com";
}
}
/**
* 生成签名 - 按照涂鸦官方规范
* 签名字符串格式: METHOD\n CONTENT-SHA256\n HEADERS\n PATH
*/
public String generateSign(String method, String url, Map<String, String> headers, String body) {
try {
// 1. 获取路径和查询参数
String path = getPathAndQuery(url);
// 2. 计算body的SHA256
String contentSha256 = getContentSha256(body);
// 3. 构建header字符串只包含client_id, t, sign_method, nonce等
String headerString = buildHeaderString(headers);
// 4. 构建签名字符串
StringBuilder sb = new StringBuilder();
sb.append(method.toUpperCase()).append("\n");
sb.append(contentSha256).append("\n");
sb.append(headerString).append("\n");
sb.append(path);
String signStr = sb.toString();
log.debug("签名字符串: \n{}", signStr);
// 5. HMAC-SHA256加密
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(secretKeySpec);
byte[] signBytes = mac.doFinal(signStr.getBytes(StandardCharsets.UTF_8));
return HexUtil.encodeHexStr(signBytes).toUpperCase();
} catch (Exception e) {
log.error("签名生成失败", e);
throw new RuntimeException("签名生成失败", e);
}
}
private String getPathAndQuery(String url) {
try {
// 移除协议和域名只保留路径和查询参数
int protocolEnd = url.indexOf("://");
if (protocolEnd != -1) {
int pathStart = url.indexOf("/", protocolEnd + 3);
if (pathStart != -1) {
return url.substring(pathStart);
}
}
return "/";
} catch (Exception e) {
log.error("解析URL失败: {}", url, e);
return url;
}
}
private String getContentSha256(String body) {
try {
if (body == null || body.isEmpty()) {
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
}
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(body.getBytes(StandardCharsets.UTF_8));
return HexUtil.encodeHexStr(hash).toLowerCase();
} catch (NoSuchAlgorithmException e) {
log.error("计算SHA256失败", e);
return "";
}
}
private String buildHeaderString(Map<String, String> headers) {
if (headers == null || headers.isEmpty()) {
return "";
}
// 按key排序
List<String> sortedKeys = new ArrayList<>(headers.keySet());
Collections.sort(sortedKeys);
StringBuilder sb = new StringBuilder();
for (String key : sortedKeys) {
sb.append(key).append(":").append(headers.get(key)).append("\n");
}
// 移除最后一个换行符
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
/**
* 获取Access Token
*/
public String getAccessToken() {
if (accessToken != null && System.currentTimeMillis() < tokenExpireTime) {
return accessToken;
}
synchronized (tokenLock) {
if (accessToken != null && System.currentTimeMillis() < tokenExpireTime) {
return accessToken;
}
try {
long timestamp = Instant.now().toEpochMilli();
String nonce = UUID.randomUUID().toString().replace("-", "");
String url = getApiHost() + "/v1.0/token?grant_type=1";
// 构建签名需要的headers
Map<String, String> signHeaders = new LinkedHashMap<>();
signHeaders.put("client_id", clientId);
signHeaders.put("sign_method", "HMAC-SHA256");
signHeaders.put("t", String.valueOf(timestamp));
signHeaders.put("nonce", nonce);
// 生成签名
String sign = generateSign("GET", url, signHeaders, null);
// 构建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("client_id", clientId);
headers.set("sign", sign);
headers.set("t", String.valueOf(timestamp));
headers.set("sign_method", "HMAC-SHA256");
headers.set("nonce", nonce);
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
log.info("Token响应: {}", response.getBody());
if (response.getStatusCode() == HttpStatus.OK) {
JsonNode jsonNode = objectMapper.readTree(response.getBody());
if (jsonNode.has("success") && jsonNode.get("success").asBoolean()) {
JsonNode result = jsonNode.get("result");
accessToken = result.get("access_token").asText();
long expireTime = result.get("expire_time").asLong();
tokenExpireTime = System.currentTimeMillis() + (expireTime * 1000) - 300000;
log.info("获取Access Token成功过期时间: {}", new Date(tokenExpireTime));
return accessToken;
} else {
String msg = jsonNode.has("msg") ? jsonNode.get("msg").asText() : "未知错误";
throw new RuntimeException("获取Token失败: " + msg);
}
} else {
throw new RuntimeException("获取Token HTTP错误: " + response.getStatusCode());
}
} catch (Exception e) {
log.error("获取Access Token异常", e);
throw new RuntimeException("获取Access Token异常", e);
}
}
}
/**
* 发送设备控制指令
*/
public JsonNode sendControlCommand(String deviceId, List<Map<String, Object>> commands) throws Exception {
Map<String, Object> body = new HashMap<>();
body.put("commands", commands);
String path = "/v1.0/devices/" + deviceId + "/commands";
return post(path, body);
}
/**
* 获取设备状态
*/
public JsonNode getDeviceStatus(String deviceId) throws Exception {
String path = "/v1.0/devices/" + deviceId;
return get(path, null);
}
/**
* 获取设备功能点规格
*/
public JsonNode getDeviceSpecifications(String deviceId) throws Exception {
String path = "/v1.0/devices/" + deviceId + "/specifications";
return get(path, null);
}
/**
* 获取设备列表
*/
public JsonNode getDeviceList(int pageNo, int pageSize) throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("page_no", pageNo);
params.put("page_size", pageSize);
String path = "/v1.0/devices";
return get(path, params);
}
/**
* POST请求
*/
public JsonNode post(String path, Object body) throws Exception {
String bodyJson = body == null ? null : objectMapper.writeValueAsString(body);
String url = getApiHost() + path;
return executeRequest(HttpMethod.POST, url, bodyJson);
}
/**
* GET请求
*/
public JsonNode get(String path, Map<String, Object> params) throws Exception {
String url = buildUrl(path, params);
return executeRequest(HttpMethod.GET, url, null);
}
private String buildUrl(String path, Map<String, Object> params) {
String baseUrl = getApiHost() + path;
if (params == null || params.isEmpty()) {
return baseUrl;
}
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUrl);
for (Map.Entry<String, Object> entry : params.entrySet()) {
builder.queryParam(entry.getKey(), entry.getValue());
}
return builder.build().toUriString();
}
private JsonNode executeRequest(HttpMethod method, String url, String body) throws Exception {
long timestamp = Instant.now().toEpochMilli();
String nonce = UUID.randomUUID().toString().replace("-", "");
// 构建签名需要的headers
Map<String, String> signHeaders = new LinkedHashMap<>();
signHeaders.put("client_id", clientId);
signHeaders.put("sign_method", "HMAC-SHA256");
signHeaders.put("t", String.valueOf(timestamp));
signHeaders.put("nonce", nonce);
// 如果需要access_token
String token = getAccessToken();
signHeaders.put("access_token", token);
// 生成签名
String sign = generateSign(method.name(), url, signHeaders, body);
// 构建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("client_id", clientId);
headers.set("sign", sign);
headers.set("t", String.valueOf(timestamp));
headers.set("sign_method", "HMAC-SHA256");
headers.set("nonce", nonce);
headers.set("access_token", token);
headers.set("Content-Type", "application/json");
HttpEntity<String> entity = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.exchange(url, method, entity, String.class);
log.debug("响应: {}", response.getBody());
if (response.getStatusCode() == HttpStatus.OK) {
JsonNode jsonNode = objectMapper.readTree(response.getBody());
if (jsonNode.has("success") && jsonNode.get("success").asBoolean()) {
return jsonNode;
} else {
String errorMsg = jsonNode.has("msg") ? jsonNode.get("msg").asText() : "未知错误";
throw new RuntimeException("API调用失败: " + errorMsg);
}
} else {
throw new RuntimeException("HTTP请求失败: " + response.getStatusCode());
}
}
}

View File

@ -0,0 +1,45 @@
package com.nu.modules.biz.plan.care.job;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.nu.modules.biz.order.entity.DirectiveOrder;
import com.nu.modules.biz.order.service.IDirectiveOrderService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
/**
* 结束工单定时 每天23:59:50把当天已开始未结束的 结束时间设置为23:59:59
*/
@Slf4j
public class DirectiveEndOrderJob implements Job {
@Autowired
private IDirectiveOrderService directiveOrderService;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
DirectiveOrder upData = new DirectiveOrder();
//设置为当天23:59:59
Calendar calendar = Calendar.getInstance();
upData.setEmpEndTime(new Date(calendar.get(Calendar.YEAR) - 1900,
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH),
23, 59, 59));
//当天任务且已开始的
UpdateWrapper<DirectiveOrder> uw = new UpdateWrapper<>();
uw.apply("DATE(serv_start_time) = CURDATE()");
uw.isNotNull("emp_start_time");
uw.isNull("emp_end_time");
directiveOrderService.update(upData,uw);
}
}

View File

@ -2,6 +2,7 @@ package com.nu.modules.biz.plan.care.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.nu.entity.CareDirectiveEntity;
import com.nu.entity.DirectivePlanDateEntity;
import com.nu.modules.biz.order.entity.DirectiveOrder;
@ -12,13 +13,17 @@ import com.nu.modules.biz.plan.care.service.IDirectivePlanDateService;
import com.nu.modules.care.api.IDirectivePlanDateApi;
import com.nu.websocket.SdWebsocket;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.util.CollectionUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Description: 服务指令编排-日期-快照表
@ -46,40 +51,46 @@ public class DirectivePlanDateServiceImpl extends ServiceImpl<DirectivePlanDateM
//所有要派发的数据查对应时间点数据 所有护理单元所有分类的数据
List<DirectivePlanDate> diretiveList = baseMapper.queryAllTaskByDateTime(queryParam);
//已派发指令不再重复派发逻辑 首先排除即时指令干扰不差cycleTypeId = 2的) 但是已派发出去的工单不知道是不是即时指令因为没有cycleTypeId
//查nuid+directive完全一致的
List<DirectiveOrder> orders = BeanUtil.copyToList(diretiveList, DirectiveOrder.class);
orders.stream().forEach(item -> {
item.setId(null);
item.setEmployeeId(empId);
item.setEmployeeName("王伟东");
item.setOptType("1");//单人执行
item.setServStartTime(item.getStartTime());//服务开始时间
item.setServEndTime(item.getEndTime());//服务结束时间
item.setOrderStartTime(new Date());//工单开始时间
item.setCreateBy("工单定时任务");
});
directiveOrderService.saveBatch(orders);
//查询这个时间点的已派发指令
QueryWrapper<DirectiveOrder> orderQW = new QueryWrapper<>();
orderQW.eq("serv_start_time", queryParam.getStartTime());
List<DirectiveOrder> havingOrderList = directiveOrderService.list(orderQW);
//已派发指令不再重复派发逻辑 不用考虑即时指令 即时指令属于客户主动触发行为
//如何判断重复 查servStartTime+nuid+directive完全一致的 查询时已经是同一年月日时分秒了 所以不用特意再判断
List<DirectiveOrder> genOrders = orders.stream().filter(item -> havingOrderList.stream().noneMatch(existingOrder -> existingOrder.getNuId().equals(item.getNuId()) && existingOrder.getDirectiveId().equals(item.getDirectiveId()))).collect(Collectors.toList());
//发送websocket消息
try {
// 发送数据
JSONObject message = new JSONObject();
message.put("type", "directiveOrder");//消息类型
message.put("timestamp", System.currentTimeMillis());
message.put("from", "system");//发送者
message.put("to", empId);//先固定都发给伟东
message.put("data", orders);//业务数据
String messageJson = message.toJSONString();
//发送给单个用户
sdWebsocket.sendMessage(empId, messageJson);
log.info("【ws消息推送】发送给用户 {}: {}", empId, messageJson);
} catch (Exception e) {
log.error("ws发送消息失败", e);
if (!CollectionUtils.isEmpty(genOrders)) {
genOrders.stream().forEach(item -> {
item.setId(null);
item.setEmployeeId(empId);
item.setEmployeeName("王伟东");
item.setOptType("1");//单人执行
item.setServStartTime(item.getStartTime());//服务开始时间
item.setServEndTime(item.getEndTime());//服务结束时间
item.setOrderStartTime(new Date());//工单开始时间
item.setCreateBy("工单定时任务");
});
directiveOrderService.saveBatch(genOrders);
//发送websocket消息
try {
// 发送数据
JSONObject message = new JSONObject();
message.put("type", "directiveOrder");//消息类型
message.put("timestamp", System.currentTimeMillis());
message.put("from", "system");//发送者
message.put("to", empId);//先固定都发给伟东
message.put("data", genOrders);//业务数据
String messageJson = message.toJSONString();
//发送给单个用户
sdWebsocket.sendMessage(empId, messageJson);
log.info("【ws消息推送】发送给用户 {}: {}", empId, messageJson);
} catch (Exception e) {
log.error("ws发送消息失败", e);
}
}
}
}

View File

@ -1,5 +1,6 @@
package org.jeecg;
import com.tuya.connector.spring.annotations.ConnectorScan;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.oConvertUtils;
import org.mybatis.spring.annotation.MapperScan;
@ -24,6 +25,7 @@ import java.util.Map;
@EnableAsync
@SpringBootApplication
@ComponentScan(basePackages = {"com.nu","org.jeecg"})
@ConnectorScan(basePackages = "com.nu.connector")
@MapperScan({"com.nu.**.mapper","org.jeecg.**.mapper"})
public class NUSystemApplication extends SpringBootServletInitializer {