mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
Merge pull request #2404 from xinnan-tech/hot-fix
update:该功能易被坏人滥用,违背开发者初心
This commit is contained in:
+1
-60
@@ -93,7 +93,7 @@ public class DeviceController {
|
||||
}
|
||||
|
||||
@PostMapping("/bind/{agentId}")
|
||||
@Operation(summary = "转发POST请求到MQTT网关")
|
||||
@Operation(summary = "设备在线接口")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<String> forwardToMqttGateway(@PathVariable String agentId, @RequestBody String requestBody) {
|
||||
try {
|
||||
@@ -208,63 +208,4 @@ public class DeviceController {
|
||||
deviceService.manualAddDevice(user.getId(), dto);
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
@PostMapping("/commands/{deviceId}")
|
||||
@Operation(summary = "发送设备指令")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<String> sendDeviceCommand(@PathVariable String deviceId, @RequestBody String command) {
|
||||
try {
|
||||
// 从系统参数中获取MQTT网关地址
|
||||
String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
|
||||
if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
|
||||
return new Result<String>().error("MQTT网关地址未配置");
|
||||
}
|
||||
|
||||
// 构建完整的URL
|
||||
// 获取设备信息以构建mqttClientId
|
||||
DeviceEntity deviceById = deviceService.selectById(deviceId);
|
||||
|
||||
if (!deviceById.getUserId().equals(SecurityUser.getUser().getId())) {
|
||||
return new Result<String>().error("设备不存在");
|
||||
}
|
||||
String macAddress = deviceById != null ? deviceById.getMacAddress() : "unknown";
|
||||
String groupId = deviceById != null ? deviceById.getBoard() : null;
|
||||
if (groupId == null) {
|
||||
groupId = "GID_default";
|
||||
}
|
||||
groupId = groupId.replace(":", "_");
|
||||
macAddress = macAddress.replace(":", "_");
|
||||
|
||||
// 拼接为groupId@@@macAddress@@@deviceId格式
|
||||
String mqttClientId = groupId + "@@@" + macAddress + "@@@" + macAddress;
|
||||
|
||||
String url = "http://" + mqttGatewayUrl + "/api/commands/" + mqttClientId;
|
||||
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Content-Type", "application/json");
|
||||
|
||||
// 生成Bearer令牌
|
||||
String dateStr = java.time.LocalDate.now()
|
||||
.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
String signatureKey = sysParamsService.getValue("server.mqtt_signature_key", false);
|
||||
if (StringUtils.isBlank(signatureKey)) {
|
||||
return new Result<String>().error("MQTT签名密钥未配置");
|
||||
}
|
||||
String tokenContent = dateStr + signatureKey;
|
||||
String token = org.apache.commons.codec.digest.DigestUtils.sha256Hex(tokenContent);
|
||||
headers.set("Authorization", "Bearer " + token);
|
||||
|
||||
// 构建请求体
|
||||
HttpEntity<String> requestEntity = new HttpEntity<>(command, headers);
|
||||
|
||||
// 发送POST请求
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
|
||||
|
||||
// 返回响应
|
||||
return new Result<String>().ok(response.getBody());
|
||||
} catch (Exception e) {
|
||||
return new Result<String>().error("发送指令失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,21 +102,4 @@ export default {
|
||||
});
|
||||
}).send();
|
||||
},
|
||||
// 发送设备指令
|
||||
sendDeviceCommand(deviceId, mcpData, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/device/commands/${deviceId}`)
|
||||
.method('POST')
|
||||
.data(mcpData)
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail((err) => {
|
||||
console.error('发送设备指令失败:', err);
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.sendDeviceCommand(deviceId, mcpData, callback);
|
||||
});
|
||||
}).send();
|
||||
},
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -64,9 +64,6 @@
|
||||
<el-button size="mini" type="text" @click="handleUnbind(scope.row.device_id)">
|
||||
{{ $t('device.unbind') }}
|
||||
</el-button>
|
||||
<el-button v-if="scope.row.deviceStatus === 'online'" size="mini" type="text" @click="handleMcpToolCall(scope.row.device_id)">
|
||||
{{ $t('device.toolCall') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -82,9 +79,9 @@
|
||||
<el-button type="success" size="mini" class="add-device-btn" @click="handleManualAddDevice">
|
||||
{{ $t('device.manualAdd') }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteSelected">{{
|
||||
$t('device.unbind')
|
||||
}}</el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteSelected">
|
||||
{{ $t('device.unbind') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="custom-pagination">
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||
@@ -92,20 +89,22 @@
|
||||
:label="$t('dictManagement.itemsPerPage').replace('{items}', item)" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">{{
|
||||
$t('dictManagement.firstPage')
|
||||
}}</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">{{
|
||||
$t('dictManagement.prevPage')
|
||||
}}</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">
|
||||
{{ $t('dictManagement.firstPage') }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">
|
||||
{{ $t('dictManagement.prevPage') }}
|
||||
</button>
|
||||
<button v-for="page in visiblePages" :key="page" class="pagination-btn"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||
{{ page }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">{{
|
||||
$t('dictManagement.nextPage') }}</button>
|
||||
<span class="total-text">{{ $t('dictManagement.totalRecords').replace('{total}', deviceList.length)
|
||||
}}</span>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">
|
||||
{{ $t('dictManagement.nextPage') }}
|
||||
</button>
|
||||
<span class="total-text">
|
||||
{{ $t('dictManagement.totalRecords').replace('{total}', deviceList.length) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -117,7 +116,6 @@
|
||||
@refresh="fetchBindDevices(currentAgentId)" />
|
||||
<ManualAddDeviceDialog :visible.sync="manualAddDeviceDialogVisible" :agent-id="currentAgentId"
|
||||
@refresh="fetchBindDevices(currentAgentId)" />
|
||||
<McpToolCallDialog :visible.sync="mcpToolCallDialogVisible" :device-id="selectedDeviceId" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@@ -127,20 +125,17 @@ import Api from '@/apis/api';
|
||||
import AddDeviceDialog from "@/components/AddDeviceDialog.vue";
|
||||
import HeaderBar from "@/components/HeaderBar.vue";
|
||||
import ManualAddDeviceDialog from "@/components/ManualAddDeviceDialog.vue";
|
||||
import McpToolCallDialog from "@/components/McpToolCallDialog.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HeaderBar,
|
||||
AddDeviceDialog,
|
||||
ManualAddDeviceDialog,
|
||||
McpToolCallDialog
|
||||
ManualAddDeviceDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
addDeviceDialogVisible: false,
|
||||
manualAddDeviceDialogVisible: false,
|
||||
mcpToolCallDialogVisible: false,
|
||||
selectedDeviceId: '',
|
||||
searchKeyword: "",
|
||||
activeSearchKeyword: "",
|
||||
@@ -280,11 +275,6 @@ export default {
|
||||
handleManualAddDevice() {
|
||||
this.manualAddDeviceDialogVisible = true;
|
||||
},
|
||||
|
||||
handleMcpToolCall(deviceId) {
|
||||
this.selectedDeviceId = deviceId;
|
||||
this.mcpToolCallDialogVisible = true;
|
||||
},
|
||||
submitRemark(row) {
|
||||
if (row._submitting) return;
|
||||
|
||||
@@ -383,7 +373,7 @@ export default {
|
||||
.sort((a, b) => a.rawBindTime - b.rawBindTime);
|
||||
this.activeSearchKeyword = "";
|
||||
this.searchKeyword = "";
|
||||
|
||||
|
||||
// 获取设备列表后,立即获取设备状态
|
||||
this.fetchDeviceStatus(agentId);
|
||||
} else {
|
||||
@@ -391,7 +381,7 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
// 获取设备状态
|
||||
fetchDeviceStatus(agentId) {
|
||||
Api.device.getDeviceStatus(agentId, ({ data }) => {
|
||||
@@ -399,7 +389,7 @@ export default {
|
||||
try {
|
||||
// 解析后端返回的设备状态JSON
|
||||
const statusData = JSON.parse(data.data);
|
||||
|
||||
|
||||
// 直接使用解析后的数据作为设备状态映射(不需要devices字段包装)
|
||||
if (statusData && typeof statusData === 'object') {
|
||||
// 更新设备状态
|
||||
@@ -411,7 +401,7 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
// 根据API响应更新设备状态
|
||||
updateDeviceStatusFromResponse(deviceStatusMap) {
|
||||
this.deviceList.forEach(device => {
|
||||
@@ -419,11 +409,11 @@ export default {
|
||||
const macAddress = device.macAddress ? device.macAddress.replace(/:/g, '_') : 'unknown';
|
||||
const groupId = device.model ? device.model.replace(/:/g, '_') : 'GID_default';
|
||||
const mqttClientId = `${groupId}@@@${macAddress}@@@${macAddress}`;
|
||||
|
||||
|
||||
// 从状态映射中获取设备状态
|
||||
if (deviceStatusMap[mqttClientId]) {
|
||||
const statusInfo = deviceStatusMap[mqttClientId];
|
||||
|
||||
|
||||
let isOnline = false;
|
||||
if (statusInfo.isAlive === true) {
|
||||
isOnline = true;
|
||||
@@ -434,7 +424,7 @@ export default {
|
||||
} else {
|
||||
isOnline = false;
|
||||
}
|
||||
|
||||
|
||||
device.deviceStatus = isOnline ? 'online' : 'offline';
|
||||
} else {
|
||||
// 如果没有找到对应的状态信息,默认为离线
|
||||
|
||||
Reference in New Issue
Block a user