联动日程设置

parent 77a71622
......@@ -969,8 +969,9 @@ export async function editAcDeviceControl(params: {
setTemp?: number;
fanSpeed?: string;
deviceMode?: string;
lock?: string;
}): Promise<{ success: boolean; message: string }> {
const { deviceId, power, setTemp, fanSpeed, deviceMode } = params;
const { deviceId, power, setTemp, fanSpeed, deviceMode, lock } = params;
// 1. 校验设备是否存在
let device = await selectOneDataByParam(
......
import { selectPaginatedDataWithOrder } from "../data/findData";
import { TABLENAME } from "../config/dbEnum";
/** 故障等级映射 */
const levelMap: Record<number, string> = {
1: '一级',
2: '二级',
3: '三级',
};
/** 处理状态映射 */
const statusMap: Record<number, string> = {
0: '未处理',
1: '处理中',
2: '已解决',
};
export interface AlertWorkOrderQueryParams {
pageNumber?: number;
pageSize?: number;
level?: number; // 故障等级:1/2/3
fault_code?: string; // 故障代码(模糊匹配)
device_id?: string; // 设备ID(模糊匹配)
startTime?: string; // 时间范围起始 'YYYY-MM-DD HH:mm:ss'
endTime?: string; // 时间范围截止 'YYYY-MM-DD HH:mm:ss'
}
/**
* 预警工单列表查询(分页)
*/
export async function getDeviceFaultList(params: AlertWorkOrderQueryParams) {
const pageNumber = params.pageNumber || 1;
const pageSize = params.pageSize || 10;
// 构建查询条件
const queryParam: any = {};
if (params.level !== undefined && params.level !== null) {
queryParam.level = params.level;
}
if (params.fault_code) {
queryParam.fault_code = { "%like%": params.fault_code };
}
if (params.device_id) {
queryParam.device_id = { "%like%": params.device_id };
}
if (params.startTime || params.endTime) {
const start = params.startTime || '1970-01-01 00:00:00';
const end = params.endTime || '2099-12-31 23:59:59';
queryParam.occurred_time = { "%between%": [start, end] };
}
// 分页查询,按创建时间倒序
const result = await selectPaginatedDataWithOrder(
TABLENAME.设备故障表,
{ ...queryParam },
["id", "device_id", "fault_type", "fault_code", "fault_description", "occurred_time", "level", "status", "created_at"],
pageNumber,
pageSize,
[["created_at", "DESC"]],
);
// 映射中文名
const list = (result.data as any[]).map((item: any) => ({
id: item.id,
device_id: item.device_id || '',
fault_type: item.fault_type || '',
fault_code: item.fault_code || '',
fault_description: item.fault_description || '',
occurred_time: item.occurred_time || '',
created_at: item.created_at || '',
level: item.level,
levelName: levelMap[item.level] || '未知',
status: item.status,
statusName: statusMap[item.status] || '未知',
order_address: '', // 预留:工单地址,todo
order_send: 'on', // 预留:工单推送,默认 on,todo
}));
return {
list,
pagination: result.pagination,
};
}
......@@ -304,6 +304,7 @@ export async function getIndoorUnitList(params: any): Promise<any> {
export async function controlIndoorUnit(params: any): Promise<any> {
const path = '/openApi/indoorUnit/control';
if (!params.indoorUnitAddressFull || !Array.isArray(params.indoorUnitAddressFull) || params.indoorUnitAddressFull.length === 0) {
console.error('控制内机: 设备编号不能为空');
throw new BizError(ERRORENUM.参数错误, "设备编号不能为空");
}
const body = { ...params };
......
......@@ -3,6 +3,7 @@ import { addData } from "../data/addData";
import { selectOneDataByParam } from "../data/findData";
import { controlAcByEnvironment } from "./running";
import { systemConfig } from "../config/serverConfig";
import { isInOffPeriod } from "./scheduleStatus";
/**
* 处理接收到的环境设备数据,并存入数据库
......@@ -111,10 +112,10 @@ export async function processDeviceData(message) {
console.log(`数据入库成功: 设备 ${devEUI}`);
writeLog(1, '正常');
// 晚上 23:00 ~ 次日 05:00 不执行空调自控(休眠时段
const currentHour = new Date().getHours();
if (currentHour >= 23 || currentHour < 5) {
console.log(`当前时间 ${currentHour}:00,处于休眠时段(23:00-05:00),跳过空调自控`);
// 动态判断设备是否处于关闭/休眠时段(基于日程开关机时间,默认 23:00-05:00
const regionKey = deviceRow?.data?.region_key || 0;
if (await isInOffPeriod(devEUI, regionKey)) {
console.log(`设备 ${devEUI} 当前处于关闭/休眠时段,跳过空调自控`);
return;
}
// 根据regionKey查询区域表,若包含文创,则禁用空调控制
......
......@@ -473,7 +473,7 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
const sortedFaults: any[] = Array.isArray(ordersRes.data) ? [...ordersRes.data] : [];
let deviceRegionMap = new Map<string, string>();
if (regionType && sortedFaults.length > 0) {
// if (regionType && sortedFaults.length > 0) {
const faultUniqueDeviceIds = [...new Set(sortedFaults.map((f: any) => f.device_id))];
const devicesRes = await selectDataListByParam(TABLENAME.设备表,
{ device_id: { "%in%": faultUniqueDeviceIds } },
......@@ -484,13 +484,15 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
deviceRegionKeyMap.set(d.device_id, d.region_key);
if (d.region_key) regionKeySet.add(d.region_key);
}
let deviceOrderAddressMap = new Map<string, string>();
if (regionKeySet.size > 0) {
const regionsRes = await selectDataListByParam(TABLENAME.区域表,
{ id: { "%in%": [...regionKeySet] }, "%orderAsc%": "sort_order" },
["id", "name"]);
["id", "name", "groups", "type"]);
const regionNameMap = new Map<string, string>();
for (const r of regionsRes.data) {
regionNameMap.set(String(r.id), r.name);
deviceOrderAddressMap.set(String(r.id), `${r.groups || ''}_${r.type || ''}`);
}
for (const [deviceId, regionKey] of deviceRegionKeyMap) {
deviceRegionMap.set(deviceId, regionNameMap.get(regionKey) || '');
......@@ -504,14 +506,18 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
alertAddress: deviceRegionMap.get(f.device_id) || '',
device_id: f.device_id || '',
faultCode: f.fault_code || '',
order_address: deviceOrderAddressMap.get(deviceRegionKeyMap.get(f.device_id)?.toString()) || '',
order_send: 'on', // TODO: 后续需要实现工单推送的实际逻辑
}));
} else {
alertWorkOrders = sortedFaults.map((f: any) => ({
riskContent: f.fault_description || f.fault_type || '',
alertTime: f.occurred_time || '',
status: faultStatusTextMap[f.status] || '未知',
}));
}
// } else {
// alertWorkOrders = sortedFaults.map((f: any) => ({
// riskContent: f.fault_description || f.fault_type || '',
// alertTime: f.occurred_time || '',
// status: faultStatusTextMap[f.status] || '未知',
// order_address: '',
// order_send: 'on', // TODO: 后续需要实现工单推送的实际逻辑
// }));
// }
} catch (err) {
console.error("预警监控数据查询失败,使用空数据降级:", err);
}
......@@ -1050,13 +1056,13 @@ function calcAcTempSet(temperature: number, season: 'summer' | 'winter'): { temp
if (season === 'summer') {
// 夏季制冷:温度越高,设定越低
if (temperature > 24) return { tempSet: 16, fanSpeed: 1, onOff: 1 }; // 未达需求:高风制冷
if (temperature >= 21) return { tempSet: 18, fanSpeed: 2, onOff: 1}; // 舒适区:四舍五入21℃待机 Math.round(temperature) === 21 ? 0 : 1
return { tempSet: 19, fanSpeed: 4, onOff: 1 }; // 已达需求:低风维持
if (temperature >= 21) return { tempSet: 18, fanSpeed: 1, onOff: 1}; // 舒适区:四舍五入21℃待机 Math.round(temperature) === 21 ? 0 : 1
return { tempSet: 19, fanSpeed: 1, onOff: 1 }; // 已达需求:低风维持
} else {
// 冬季制热:温度越低,设定越高
if (temperature < 18) return { tempSet: 21, fanSpeed: 1, onOff: 1 }; // 未达需求:高风制热
if (temperature <= 21) return { tempSet: 20, fanSpeed: 4, onOff: 1}; // 舒适区:四舍五入20℃待机 Math.round(temperature) === 20 ? 0 : 1
return { tempSet: 18, fanSpeed: 4, onOff: 1 }; // 已达需求:低风维持
if (temperature <= 21) return { tempSet: 20, fanSpeed: 1, onOff: 1}; // 舒适区:四舍五入20℃待机 Math.round(temperature) === 20 ? 0 : 1
return { tempSet: 18, fanSpeed: 1, onOff: 1 }; // 已达需求:低风维持
}
}
......@@ -1080,7 +1086,7 @@ export async function controlAcByEnvironment(deviceId: string, data: any, region
const season = getSeason();
const temperature = data.temperature;
const co2 = data.co2;
console.log(`若是环境设备,则解析环境设备数据,判断是否需要联动 ${envDevices.data.length}`);
// 若是环境设备,则解析环境设备数据,判断是否需要联动
if (envDevices.data.length) {
let regionKey = envDevices.data[0].region_key;
......@@ -1096,7 +1102,7 @@ export async function controlAcByEnvironment(deviceId: string, data: any, region
device_type: { "%in%": ["新风", "空调"] },
linkage_start: 'on'
}, ["id", "device_name", "device_id", "device_ad", "device_type"]);
console.log(`若是新风设备,判断联动是否开启 ${fanDevices.data.length}`);
if (fanDevices.data.length > 0) {
// CO2联动:高→送风模式(3)+高风(1),低→不传workMode(不切换模式)+低风(4)
// const fanWorkMode: number | undefined = fanParams.action === 'high' ? 3 : undefined;
......@@ -1145,7 +1151,7 @@ export async function controlAcByEnvironment(deviceId: string, data: any, region
}
}
}
console.log(`若是空调设备,判断温度是否为空 ${temperature}`);
// ===== 空调联动 =====
if (temperature != null) {
let acDevices = await selectDataListByParam(TABLENAME.设备表, {
......@@ -1153,7 +1159,7 @@ export async function controlAcByEnvironment(deviceId: string, data: any, region
device_type: "空调",
linkage_start: 'on'
}, ["id", "device_name", "device_id", "device_ad", "control_params"]);
console.log(`若是空调设备,判断联动是否开启 ${acDevices.data.length}`);
if (acDevices.data.length > 0) {
const { tempSet, fanSpeed, onOff } = calcAcTempSet(temperature, season);
const workMode = season === 'summer' ? 1 : 2; // 1制冷 2制热
......@@ -1179,9 +1185,9 @@ export async function controlAcByEnvironment(deviceId: string, data: any, region
// 去重:若本次计算的控制参数与上次相同,跳过调用
const cacheKey = `ac_${regionKey}`;
const lastState = envControlCache.get(cacheKey);
if (lastState && lastState.tempSet === tempSet && lastState.workMode === workMode && lastState.fanSpeed === fanSpeed && lastState.onOff === onOff && lastState.maxTemp === maxTemp && lastState.minTemp === minTemp) {
console.log(`空调联动跳过(控制参数未变): ${modeName}模式, 设定${tempSet}℃, 风速${fanSpeed === 1 ? '高' : '低'}风, ${onOff ? '开机' : '待机'}, 区域 ${regionKey}`);
} else {
// if (lastState && lastState.tempSet === tempSet && lastState.workMode === workMode && lastState.fanSpeed === fanSpeed && lastState.onOff === onOff && lastState.maxTemp === maxTemp && lastState.minTemp === minTemp) {
// console.log(`空调联动跳过(控制参数未变): ${modeName}模式, 设定${tempSet}℃, 风速${fanSpeed === 1 ? '高' : '低'}风, ${onOff ? '开机' : '待机'}, 区域 ${regionKey}`);
// } else {
let controlRes = await controlAcRunningApi(acDevices, workMode, tempSet, fanSpeed, onOff, maxTemp, minTemp);
// 更新缓存
......@@ -1213,7 +1219,7 @@ export async function controlAcByEnvironment(deviceId: string, data: any, region
} else {
console.error(`空调联动(${modeName})失败: ${controlRes.message}`);
}
}
// }
}
}
}
......
......@@ -7,6 +7,7 @@ import { addData } from "../data/addData";
import { delData } from "../data/delData";
import { updateManyData } from "../data/updateData";
import { TABLENAME } from "../config/dbEnum";
import { clearOffPeriodCache } from "./scheduleStatus";
// ======================= 公共工具 =======================
......@@ -185,19 +186,23 @@ export async function addSchedule(params: {
return { isSuccess: false, msg: "日程创建失败" };
}
// 3. 查这些区域下的所有空调设备
let deviceParams:any = {
device_type: "空调",
};
// 3. 查空调设备(优先 deviceIds,补充 regionKeys 维度)
let devices: any[] = [];
if (deviceIds && deviceIds.length) {
deviceParams.device_id = { "%in%": deviceIds };
}
if (regionKeys && regionKeys.length) {
deviceParams.region_key = { "%in%": regionKeys };
// 有具体设备时,按 deviceIds 查,取出它们的 device_id + region_key
const deviceRes = await selectDataListByParam(TABLENAME.设备表, {
device_type: "空调",
device_id: { "%in%": deviceIds },
}, ["device_id", "region_key"]);
devices = deviceRes.data || [];
} else if (regionKeys && regionKeys.length) {
// 仅传区域时,查该区域下所有空调
const deviceRes = await selectDataListByParam(TABLENAME.设备表, {
device_type: "空调",
region_key: { "%in%": regionKeys },
}, ["device_id", "region_key"]);
devices = deviceRes.data || [];
}
const deviceRes = await selectDataListByParam(TABLENAME.设备表, deviceParams, ["device_id", "region_key"]);
const devices = deviceRes.data || [];
// 4. 批量插入 schedule_device 关联记录
if (devices.length > 0) {
......@@ -208,8 +213,8 @@ export async function addSchedule(params: {
}));
await addData(TABLENAME.日程设备关联表, sdRecords);
console.log(`[新增日程] 关联设备 ${sdRecords.length} 条`);
} else if (!regionKeys && !regionKeys.length) {
// 无设备时也插入一条区域占位记录(允许区域下暂时无设备)
} else if (regionKeys && regionKeys.length) {
// 无设备但传了区域时,插入区域占位记录
for (const rk of regionKeys) {
await addData(TABLENAME.日程设备关联表, {
schedule_id: scheduleId,
......@@ -220,6 +225,7 @@ export async function addSchedule(params: {
console.log(`[新增日程] 区域 ${regionKeys.length} 个,暂无设备`);
}
clearOffPeriodCache(); // 新增日程后清空开关机时段缓存
return { isSuccess: true, scheduleId };
}
......@@ -298,18 +304,21 @@ export async function editSchedule(params: {
// 2. 删除旧的关联记录
await delData(TABLENAME.日程设备关联表, { schedule_id: scheduleId });
// 3. 查区域下的空调设备
let deviceParams:any = {
device_type: "空调",
};
// 3. 查空调设备(优先 deviceIds,补充区域维度)
let devices: any[] = [];
if (deviceIds && deviceIds.length) {
deviceParams.device_id = { "%in%": deviceIds };
}
if (regionKeys && regionKeys.length) {
deviceParams.region_key = { "%in%": regionKeys };
const deviceRes = await selectDataListByParam(TABLENAME.设备表, {
device_type: "空调",
device_id: { "%in%": deviceIds },
}, ["device_id", "region_key"]);
devices = deviceRes.data || [];
} else if (regionKeys && regionKeys.length) {
const deviceRes = await selectDataListByParam(TABLENAME.设备表, {
device_type: "空调",
region_key: { "%in%": regionKeys },
}, ["device_id", "region_key"]);
devices = deviceRes.data || [];
}
const deviceRes = await selectDataListByParam(TABLENAME.设备表, deviceParams, ["device_id", "region_key"]);
const devices = deviceRes.data || [];
// 4. 重建关联
if (devices.length > 0) {
......@@ -320,7 +329,7 @@ export async function editSchedule(params: {
}));
await addData(TABLENAME.日程设备关联表, sdRecords);
console.log(`[编辑日程] 重建关联设备 ${sdRecords.length} 条`);
} else if (!regionKeys && !regionKeys.length) {
} else if (regionKeys && regionKeys.length) {
for (const rk of regionKeys) {
await addData(TABLENAME.日程设备关联表, {
schedule_id: scheduleId,
......@@ -331,6 +340,7 @@ export async function editSchedule(params: {
console.log(`[编辑日程] 区域 ${regionKeys.length} 个,暂无设备`);
}
clearOffPeriodCache(); // 编辑日程后清空开关机时段缓存
return { isSuccess: true };
}
......@@ -348,5 +358,6 @@ export async function deleteSchedule(scheduleId: number): Promise<any> {
// 2. 删除主记录
await delData(TABLENAME.日程表, { id: scheduleId });
clearOffPeriodCache(); // 删除日程后清空开关机时段缓存
return { isSuccess: true };
}
......@@ -179,7 +179,7 @@ async function executeControl(
scheduleId,
time: formatLocalTime(now),
acControl,
params: { ...params, indoorUnitAddressFull: undefined },
params: { ...params, indoorUnitAddressFull: deviceIds.join(",") },
success: result.success,
message: result.message,
},
......
import { TABLENAME } from "../config/dbEnum";
import { selectDataListByParam } from "../data/findData";
/** 默认关机时间 23:00(分钟数) */
const DEFAULT_OFF_MINUTES = 23 * 60;
/** 默认开机时间 05:00(分钟数) */
const DEFAULT_ON_MINUTES = 5 * 60;
/**
* 将 HH:mm:ss 或 HH:mm 格式的时间转为分钟数
*/
function timeToMinutes(timeStr: string): number {
const parts = timeStr.split(':').map(Number);
return parts[0] * 60 + (parts[1] || 0);
}
// ===== 内存缓存 =====
interface CacheEntry {
result: boolean;
expireAt: number; // 过期时间戳 ms
}
const offPeriodCache = new Map<string, CacheEntry>();
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 分钟
function getCacheKey(deviceId: string, regionKey: number | string): string {
return `${deviceId}_${regionKey}`;
}
/** 清空所有缓存 */
export function clearOffPeriodCache(): void {
offPeriodCache.clear();
console.log(`[scheduleStatus] 缓存已清空`);
}
/** 清空指定设备的缓存 */
export function clearOffPeriodCacheForDevice(deviceId: string): void {
const prefix = `${deviceId}_`;
for (const key of offPeriodCache.keys()) {
if (key.startsWith(prefix)) {
offPeriodCache.delete(key);
}
}
}
/**
* 判断设备当前是否处于"关闭/休眠时段"
*
* 逻辑:
* 1. 查 schedule_device 表,获取设备关联的日程 ID(按 device_id 或 region_key 匹配)
* 2. 查 schedule 表,获取 ac_control=on/off 且设置了 schedule_time 的日程
* 3. 若日程中没有 on 时间 → 用默认 05:00;没有 off 时间 → 用默认 23:00
* 4. 找到当前时间点之前最近的一次操作(on/off),若为 off 则处于关闭时段
* 5. 结果缓存 5 分钟
*
* @returns true 表示当前处于关闭时段,应跳过联动
*/
export async function isInOffPeriod(deviceId: string, regionKey: number): Promise<boolean> {
const cacheKey = getCacheKey(deviceId, regionKey);
const cached = offPeriodCache.get(cacheKey);
// 缓存命中且未过期
if (cached && cached.expireAt > Date.now()) {
return cached.result;
}
const now = new Date();
const nowMinutes = now.getHours() * 60 + now.getMinutes();
try {
// 1. 查 schedule_device:匹配 device_id 或 (region_key 且 device_id 为空,即区域级日程)
const sdResult = await selectDataListByParam(
TABLENAME.日程设备关联表,
{
"%or%": [
{ device_id: deviceId },
{ region_key: regionKey, device_id: "" },
],
},
["schedule_id"],
);
const scheduleIds = [...new Set((sdResult.data as any[]).map((d: any) => d.schedule_id))];
// 2. 无关联日程 → 使用默认 23:00-05:00 判断
if (scheduleIds.length === 0) {
const result = nowMinutes >= DEFAULT_OFF_MINUTES || nowMinutes < DEFAULT_ON_MINUTES;
offPeriodCache.set(cacheKey, { result, expireAt: Date.now() + CACHE_TTL_MS });
console.log(
`[scheduleStatus] 设备 ${deviceId} 无关联日程,使用默认时段(23:00-05:00),当前${result ? '关闭' : '开启'}时段`,
);
return result;
}
// 3. 查 schedule 表:有 schedule_time 且 ac_control 为 on/off
const schResult = await selectDataListByParam(
TABLENAME.日程表,
{
id: { "%in%": scheduleIds },
schedule_time: { "%ne%": null },
ac_control: { "%in%": ["on", "off"] },
},
["ac_control", "schedule_time"],
);
const schedules = schResult.data as any[];
let hasOnSchedule = false;
let hasOffSchedule = false;
let latestOffBeforeNow = -1; // 今天当前时间之前最近的 off,不跨天时
let latestOnBeforeNow = -1;
const allOffMinutes: number[] = [];
const allOnMinutes: number[] = [];
for (const s of schedules) {
const minutes = timeToMinutes(s.schedule_time);
if (s.ac_control === "off") {
hasOffSchedule = true;
allOffMinutes.push(minutes);
if (minutes <= nowMinutes && minutes > latestOffBeforeNow) {
latestOffBeforeNow = minutes;
}
} else if (s.ac_control === "on") {
hasOnSchedule = true;
allOnMinutes.push(minutes);
if (minutes <= nowMinutes && minutes > latestOnBeforeNow) {
latestOnBeforeNow = minutes;
}
}
}
// 4. 构建完整的 on/off 时间点列表(含默认值兜底)
const actions: { time: number; type: "on" | "off" }[] = [];
// 加入日程中的时间点
for (const min of allOffMinutes) {
actions.push({ time: min, type: "off" });
}
for (const min of allOnMinutes) {
actions.push({ time: min, type: "on" });
}
// 兜底:日程中没有 on → 默认 05:00 开机
if (!hasOnSchedule) {
actions.push({ time: DEFAULT_ON_MINUTES, type: "on" });
}
// 兜底:日程中没有 off → 默认 23:00 关机
if (!hasOffSchedule) {
actions.push({ time: DEFAULT_OFF_MINUTES, type: "off" });
}
// 按时间排序
actions.sort((a, b) => a.time - b.time);
// 5. 找到当前时间之前最近的一次操作
// 如果今天没有 ≤ nowMinutes 的操作,说明所有操作都在未来,
// 取昨天最后一个操作(即 actions 中时间最大的那个,视为跨天)
let lastAction: { time: number; type: "on" | "off" } | null = null;
for (let i = actions.length - 1; i >= 0; i--) {
if (actions[i].time <= nowMinutes) {
lastAction = actions[i];
break;
}
}
if (lastAction === null) {
// 所有操作都在当前时间之后 → 取昨天的最后一个操作
// 最后一条就是时间最大的,视为昨天执行的操作
const yesterdayAction = actions[actions.length - 1];
const result = yesterdayAction.type === "off";
offPeriodCache.set(cacheKey, { result, expireAt: Date.now() + CACHE_TTL_MS });
const offTime = Math.floor(yesterdayAction.time / 60);
console.log(
`[scheduleStatus] 设备 ${deviceId} 跨天,昨天最后操作为 ${yesterdayAction.type}(${offTime}:00),当前${result ? '关闭' : '开启'}时段`,
);
return result;
}
const result = lastAction.type === "off";
offPeriodCache.set(cacheKey, { result, expireAt: Date.now() + CACHE_TTL_MS });
const actionHour = Math.floor(lastAction.time / 60);
console.log(
`[scheduleStatus] 设备 ${deviceId} 最近操作为 ${lastAction.type}(${actionHour}:00),当前${result ? '关闭' : '开启'}时段`,
);
return result;
} catch (error) {
console.error(`[scheduleStatus] 查询设备 ${deviceId} 日程失败,降级为默认 23-05:`, error);
const result = nowMinutes >= DEFAULT_OFF_MINUTES || nowMinutes < DEFAULT_ON_MINUTES;
return result;
}
}
......@@ -27,7 +27,7 @@ async function lanuch() {
/**启动故障状态更新定时任务(5分钟) */
startFaultStatusSchedule();
/**启动日程执行器 */
// await initScheduleTasks();
await initScheduleTasks();
console.log('This indicates that the server is started successfully.');
......
......@@ -7,6 +7,7 @@ import * as reportBiz from "../biz/report";
import * as regionBiz from "../biz/region";
import * as scheduleBiz from "../biz/schedule";
import * as deviceBiz from "../biz/device";
import * as alertWorkOrderBiz from "../biz/deviceFault";
import { reloadScheduleTasks } from "../biz/scheduleExecutor";
import { eccReqParamater } from "../util/verificationParam";
......@@ -42,6 +43,8 @@ export function setRouter(httpServer) {
/** 设备管理 - 批量编辑设备信息 */
httpServer.post("/api/qdm/admin/device/editDevice_batch", asyncHandler(batchEditLocalDevices));
/** 预警工单列表 */
httpServer.get("/api/qdm/admin/fault/list", asyncHandler(getDeviceFaultList));
}
/**
......@@ -227,14 +230,14 @@ async function getDeviceList(req, res) {
* body: { deviceId, power, setTemp, fanSpeed, deviceMode }
*/
async function editDevice(req, res) {
let reqConf = { deviceId: 'String', power: 'String', setTemp: 'Number', fanSpeed: 'String', deviceMode: 'String' };
const NotMustHaveKeys = ["fanSpeed", "deviceMode"];
let { deviceId, power, setTemp, fanSpeed, deviceMode } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
let reqConf = { deviceId: 'String', power: 'String', setTemp: 'Number', fanSpeed: 'String', deviceMode: 'String', lock: 'String' };
const NotMustHaveKeys = ["fanSpeed", "deviceMode", "lock"];
let { deviceId, power, setTemp, fanSpeed, deviceMode, lock } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
if (!deviceId || !power) {
res.error("缺少必填参数:deviceId, power");
return;
}
const data = await deviceBiz.editAcDeviceControl({ deviceId, power, setTemp, fanSpeed, deviceMode });
const data = await deviceBiz.editAcDeviceControl({ deviceId, power, setTemp, fanSpeed, deviceMode, lock });
res.success(data);
}
......@@ -303,7 +306,7 @@ async function batchEditDevices(req, res) {
/**
* POST /api/qdm/admin/device/editDevice
* 设备管理 - 编辑单台设备表信息
* body: { deviceId, deviceName?, regionKey?, deviceType?, controlParams?, deviceState? }
* body: { deviceId, deviceName?, regionKey?, deviceType?, linkageStart?, deviceState? }
*/
async function editLocalDevice(req, res) {
let reqConf = {
......@@ -324,7 +327,7 @@ async function editLocalDevice(req, res) {
const hasEdit = deviceName !== undefined || regionKey !== undefined
|| deviceType !== undefined || deviceState !== undefined || linkageStart !== undefined;
if (!hasEdit) {
res.error("至少需要提供一个编辑字段(deviceName/regionKey/deviceType/controlParams/deviceState)");
res.error("至少需要提供一个编辑字段(deviceName/regionKey/deviceType/linkageStart/deviceState)");
return;
}
const data = await deviceBiz.editLocalDevice({ deviceId, deviceName, regionKey, deviceType, deviceState, linkageStart });
......@@ -334,7 +337,7 @@ async function editLocalDevice(req, res) {
/**
* POST /api/qdm/admin/device/editDevice_batch
* 设备管理 - 批量编辑设备表信息
* body: { regionKeys?, deviceName?, edits: { deviceName?, regionKey?, deviceType?, controlParams?, deviceState? } }
* body: { regionKeys?, deviceName?, edits: { deviceName?, regionKey?, deviceType?, linkageStart?, deviceState? } }
*/
async function batchEditLocalDevices(req, res) {
let reqConf = {
......@@ -352,12 +355,32 @@ async function batchEditLocalDevices(req, res) {
res.error("至少需要提供 regionKeys 或 deviceName 筛选条件");
return;
}
const validEditKeys = ["deviceName", "regionKey", "deviceType", "controlParams", "deviceState"];
const validEditKeys = ["deviceName", "regionKey", "deviceType", "deviceState", "linkageStart"];
const hasEdit = validEditKeys.some(k => edits[k] !== undefined);
if (!hasEdit) {
res.error("edits 中至少需要一个编辑字段(deviceName/regionKey/deviceType/controlParams/deviceState)");
res.error("edits 中至少需要一个编辑字段(deviceName/regionKey/deviceType/linkageStart/deviceState)");
return;
}
const data = await deviceBiz.batchEditLocalDevices({ regionKeys, deviceName, edits });
res.success(data);
}
// ======================= 预警工单 =======================
/**
* GET /api/qdm/admin/alertWorkOrder/list
* 预警工单列表(分页)
* query: pageNumber, pageSize, level, fault_code, device_id, startTime, endTime
*/
async function getDeviceFaultList(req, res) {
const data = await alertWorkOrderBiz.getDeviceFaultList({
pageNumber: Number(req.query?.pageNumber) || 1,
pageSize: Number(req.query?.pageSize) || 10,
level: req.query?.level ? Number(req.query.level) : undefined,
fault_code: req.query?.fault_code || undefined,
device_id: req.query?.device_id || undefined,
startTime: req.query?.startTime || undefined,
endTime: req.query?.endTime || undefined,
});
res.success(data);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment