From f4d592f3626f94117d8a4eb22176a28290931980 Mon Sep 17 00:00:00 2001
From: doum <doum>
Date: 星期二, 26 五月 2026 18:51:54 +0800
Subject: [PATCH] 新增智能电表、空调管理
---
server/db/conditioner_param.dict.sql | 32
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/ConditionerUtil.java | 553 ++++
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerGatewayCloudController.java | 55
admin/src/views/business/components/YwConditionerLockWindow.vue | 121
server/db/yw_conditioner_module.sql | 165 +
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/RoomInfoResponse.java | 14
admin/src/views/business/ywconditioneractions.vue | 195 +
admin/src/views/system/menu.vue | 1
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerGatewayLog.java | 46
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/ConditionerConfigService.java | 49
admin/src/views/business/ywconditionerreport.vue | 632 ++++
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerReportService.java | 20
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerBilling.java | 48
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/LogQueryRequest.java | 16
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/RoomManageRequest.java | 22
server/db/business.yw_conditioner.menu.root.sql | 68
admin/src/views/business/components/YwConditionerHistoryWindow.vue | 102
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerBillingMapper.java | 7
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/UserManageRequest.java | 31
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerServiceImpl.java | 127
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManyControlItem.java | 25
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerGateway.java | 45
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/ConditionerToolTestUtil.java | 57
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/MeterDbInfoResponse.java | 24
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerGatewayLogMapper.java | 7
server/system_service/src/main/java/com/doumee/core/utils/Constants.java | 5
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerReportCloudController.java | 60
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerService.java | 19
admin/src/api/business/ywconditioneractions.js | 11
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManageRequest.java | 34
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DeviceStatusResponse.java | 40
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerMeter.java | 58
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerActionsService.java | 10
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/AreaManageRequest.java | 19
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/CompanyGsManageRequest.java | 40
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerOperateDTO.java | 20
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerActionsCloudController.java | 49
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ChangeDlSjXsRequest.java | 24
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/WgWithAreaRequest.java | 16
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/FloorInfoResponse.java | 13
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/ConditionerBizService.java | 49
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerMeterService.java | 17
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/MoonDlQueryRequest.java | 16
admin/src/directives/v-permissions.js | 6
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerUsage.java | 39
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerGatewayService.java | 15
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/ConditionerBizServiceImpl.java | 1031 +++++++
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerBillingCloudController.java | 43
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/TimingWithAreaRequest.java | 16
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerMeterCloudController.java | 59
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DlSjXsResponse.java | 22
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/WgManageRequest.java | 19
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/GsWithAreaRequest.java | 16
server/db/business.yw_conditioner.module.permissions.sql | 88
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerGatewayMapper.java | 7
server/db/docs/conditioner_api_index.md | 113
admin/src/api/business/ywconditionerbilling.js | 11
admin/src/views/business/ywconditionermeter.vue | 169 +
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevLockManyControlItem.java | 25
admin/src/api/business/ywconditioner.js | 35
admin/src/views/business/ywconditionerbilling.vue | 127
admin/src/api/business/ywconditionergateway.js | 15
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DeviceArchiveResponse.java | 22
admin/src/api/business/ywconditionermeter.js | 19
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/CompanyGsInfoResponse.java | 23
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ConditionerPageRequest.java | 16
server/system_service/src/main/java/com/doumee/config/cloudfilter/LoginHandlerInterceptor.java | 33
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/UserInfoResponse.java | 23
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerBillingService.java | 12
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerGatewayServiceImpl.java | 66
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/BuildingManageRequest.java | 22
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/BuildingInfoResponse.java | 14
server/db/quartz_job.module.column.sql | 4
admin/src/views/business/components/YwConditionerCard.vue | 293 ++
server/visits/admin_timer/src/main/java/com/doumee/TimerApplication.java | 2
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/UnitInfoResponse.java | 14
server/system_timer/src/main/java/com/doumee/jobs/fegin/VisitServiceFegin.java | 16
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/LoginRequest.java | 16
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DbDaySumQueryRequest.java | 16
server/db/docs/pdf_extract.txt | 771 +++++
admin/src/views/business/ywconditioner.vue | 342 ++
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/TimingManageRequest.java | 46
server/visits/admin_timer/src/main/java/com/doumee/api/YwTimerController.java | 24
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManyControlRequest.java | 24
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/GatewayInfoResponse.java | 16
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerReportQueryDTO.java | 29
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerActions.java | 57
server/db/docs/智精灵API接口文档26-01.pdf | 0
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/FloorManageRequest.java | 19
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/MeterDbManageRequest.java | 42
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/TimingInfoResponse.java | 20
server/db/business.yw_conditioner.admin_role_grant.sql | 60
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerReportServiceImpl.java | 155 +
admin/src/views/business/ywconditionergateway.vue | 139 +
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerLockDTO.java | 22
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/GetDevOneRequest.java | 16
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/UnitManageRequest.java | 22
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/ConditionerConstant.java | 57
admin/src/views/business/components/YwConditionerGatewayLogWindow.vue | 91
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerBillingServiceImpl.java | 45
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerCloudController.java | 65
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ConditionerSessionRequest.java | 39
admin/src/components/base/BasePage.vue | 3
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerActionsServiceImpl.java | 48
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DayDlQueryRequest.java | 22
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevControlRequest.java | 28
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/AreaInfoResponse.java | 13
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerUsageReportPageDTO.java | 16
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevLockControlRequest.java | 19
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerActionsMapper.java | 7
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerMeterServiceImpl.java | 58
admin/src/views/business/components/YwElectricalEdit.vue | 2
server/db/quartz_job.yw_timer.sql | 40
/dev/null | 48
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/ConditionerBaseResponse.java | 28
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/AddMoneyRequest.java | 16
server/db/ELECTRICAL_INTEGRATION.md | 18
admin/src/api/business/ywconditionerreport.js | 19
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerUsageReportVO.java | 27
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerMeterMapper.java | 7
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditioner.java | 54
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerUsageMapper.java | 7
server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/LoginDataResponse.java | 25
123 files changed, 7,968 insertions(+), 67 deletions(-)
diff --git a/admin/src/api/business/ywconditioner.js b/admin/src/api/business/ywconditioner.js
new file mode 100644
index 0000000..6375934
--- /dev/null
+++ b/admin/src/api/business/ywconditioner.js
@@ -0,0 +1,35 @@
+import request from '../../utils/request'
+
+const base = '/visitsAdmin/cloudService/business/ywConditioner'
+
+export function fetchCardPage (data) {
+ return request.post(base + '/cardPage', data, { trim: true })
+}
+
+export function fetchList (data) {
+ return request.post(base + '/page', data, { trim: true })
+}
+
+export function syncAll (data) {
+ return request.post(base + '/syncAll', data || {})
+}
+
+export function syncDevicesAndStatus (data) {
+ return request.post(base + '/syncDevicesAndStatus', data || {})
+}
+
+export function operate (data) {
+ return request.post(base + '/operate', data)
+}
+
+export function lock (data) {
+ return request.post(base + '/lock', data)
+}
+
+export function historyPage (data) {
+ return request.post(base + '/historyPage', data, { trim: true })
+}
+
+export function gatewayOptions () {
+ return request.get(base + '/gatewayOptions')
+}
diff --git a/admin/src/api/business/ywconditioneractions.js b/admin/src/api/business/ywconditioneractions.js
new file mode 100644
index 0000000..a9d2ce5
--- /dev/null
+++ b/admin/src/api/business/ywconditioneractions.js
@@ -0,0 +1,11 @@
+import request from '../../utils/request'
+
+const base = '/visitsAdmin/cloudService/business/ywConditionerActions'
+
+export function fetchList (data) {
+ return request.post(base + '/page', data, { trim: true })
+}
+
+export function exportExcel (data) {
+ return request.post(base + '/exportExcel', data, { trim: true, download: true })
+}
diff --git a/admin/src/api/business/ywconditionerbilling.js b/admin/src/api/business/ywconditionerbilling.js
new file mode 100644
index 0000000..36f14b9
--- /dev/null
+++ b/admin/src/api/business/ywconditionerbilling.js
@@ -0,0 +1,11 @@
+import request from '../../utils/request'
+
+const base = '/visitsAdmin/cloudService/business/ywConditionerBilling'
+
+export function fetchList (data) {
+ return request.post(base + '/page', data, { trim: true })
+}
+
+export function syncAll (data) {
+ return request.post(base + '/syncAll', data || {})
+}
diff --git a/admin/src/api/business/ywconditionergateway.js b/admin/src/api/business/ywconditionergateway.js
new file mode 100644
index 0000000..545f76e
--- /dev/null
+++ b/admin/src/api/business/ywconditionergateway.js
@@ -0,0 +1,15 @@
+import request from '../../utils/request'
+
+const base = '/visitsAdmin/cloudService/business/ywConditionerGateway'
+
+export function fetchList (data) {
+ return request.post(base + '/page', data, { trim: true })
+}
+
+export function syncAll (data) {
+ return request.post(base + '/syncAll', data || {})
+}
+
+export function gatewayLogPage (data) {
+ return request.post(base + '/gatewayLogPage', data, { trim: true })
+}
diff --git a/admin/src/api/business/ywconditionermeter.js b/admin/src/api/business/ywconditionermeter.js
new file mode 100644
index 0000000..da35cca
--- /dev/null
+++ b/admin/src/api/business/ywconditionermeter.js
@@ -0,0 +1,19 @@
+import request from '../../utils/request'
+
+const base = '/visitsAdmin/cloudService/business/ywConditionerMeter'
+
+export function fetchList (data) {
+ return request.post(base + '/page', data, { trim: true })
+}
+
+export function syncAll (data) {
+ return request.post(base + '/syncAll', data || {})
+}
+
+export function queryEnergy (id) {
+ return request.post(base + '/queryEnergy/' + id)
+}
+
+export function queryPower (id) {
+ return request.post(base + '/queryPower/' + id)
+}
diff --git a/admin/src/api/business/ywconditionerreport.js b/admin/src/api/business/ywconditionerreport.js
new file mode 100644
index 0000000..1c286eb
--- /dev/null
+++ b/admin/src/api/business/ywconditionerreport.js
@@ -0,0 +1,19 @@
+import request from '../../utils/request'
+
+const base = '/visitsAdmin/cloudService/business/ywConditionerReport'
+
+export function fetchPage (data) {
+ return request.post(base + '/page', data, { trim: true })
+}
+
+export function syncUsage (data) {
+ return request.post(base + '/syncUsage', data || {})
+}
+
+export function merchantOptions () {
+ return request.get(base + '/merchantOptions')
+}
+
+export function exportExcel (data) {
+ return request.post(base + '/exportExcel', data, { trim: true, download: true })
+}
diff --git a/admin/src/components/base/BasePage.vue b/admin/src/components/base/BasePage.vue
index 38cf164..a0f0bf7 100644
--- a/admin/src/components/base/BasePage.vue
+++ b/admin/src/components/base/BasePage.vue
@@ -39,6 +39,9 @@
if (permissions == null) {
return true
}
+ if (this.isAdmin) {
+ return true
+ }
if (this.userInfo == null) {
return false
}
diff --git a/admin/src/directives/v-permissions.js b/admin/src/directives/v-permissions.js
index c96efda..5d2027d 100644
--- a/admin/src/directives/v-permissions.js
+++ b/admin/src/directives/v-permissions.js
@@ -14,8 +14,12 @@
if (!(configPermissions instanceof Array)) {
throw new Error('v-permissions鐨勫�煎繀椤讳负涓�涓暟缁�')
}
+ // 瓒呯骇绠$悊鍛橈紙瑙掕壊 code=admin锛夋嫢鏈夊叏閮ㄦ寜閽潈闄�
+ if (userInfo.roles && userInfo.roles.findIndex(code => code === 'admin') > -1) {
+ return
+ }
// 楠岃瘉鏉冮檺
- if (configPermissions.findIndex(code => userInfo.permissions.findIndex(p => p === code) > -1) === -1) {
+ if (!userInfo.permissions || configPermissions.findIndex(code => userInfo.permissions.findIndex(p => p === code) > -1) === -1) {
el.parentNode && el.parentNode.removeChild(el)
}
}
diff --git a/admin/src/views/business/components/YwConditionerCard.vue b/admin/src/views/business/components/YwConditionerCard.vue
new file mode 100644
index 0000000..e2aee2d
--- /dev/null
+++ b/admin/src/views/business/components/YwConditionerCard.vue
@@ -0,0 +1,293 @@
+<template>
+ <div class="cond-card" :class="{ offline: !isOnline }">
+ <div class="cond-card__head">
+ <span class="cond-card__name" :title="displayName">{{ displayName }}</span>
+ <i class="cond-card__online" :class="isOnline ? 'el-icon-success online' : 'el-icon-error offline'" :title="device.online || '绂荤嚎'"></i>
+ </div>
+ <div class="cond-card__uptime" v-if="device.uptime">杩愯 {{ device.uptime }}</div>
+ <div class="cond-card__temp">
+ <div class="temp-set">
+ <el-button type="text" class="temp-btn" icon="el-icon-arrow-down" @click="$emit('set-temp', device, -1)" v-permissions="['business:ywconditioner:operate']" />
+ <span class="temp-num">{{ formatTemp(device.tempSet) }}</span>
+ <el-button type="text" class="temp-btn" icon="el-icon-arrow-up" @click="$emit('set-temp', device, 1)" v-permissions="['business:ywconditioner:operate']" />
+ <span class="temp-label">銆愯瀹氭俯搴︺��</span>
+ </div>
+ <div class="temp-indoor">
+ <span class="temp-num sm">{{ formatTemp(device.temp) }}</span>
+ <span class="temp-label">銆愬鍐呮俯搴︺��</span>
+ </div>
+ </div>
+ <div class="cond-card__modes">
+ <span class="cond-card__row-label">妯″紡锛�</span>
+ <span
+ v-for="item in modeOptions"
+ :key="'m' + item.value"
+ class="mode-icon"
+ :class="{ active: device.mode === item.value, 'accent-red': item.accentRed && device.mode === item.value }"
+ :title="item.label"
+ @click="$emit('set-mode', device, item.value)"
+ v-permissions="['business:ywconditioner:operate']"
+ >{{ item.icon }}</span>
+ </div>
+ <div class="cond-card__fans">
+ <span class="cond-card__row-label">椋庨�燂細</span>
+ <span
+ v-for="item in fanOptions"
+ :key="'f' + item.value"
+ class="fan-icon"
+ :class="{ active: currentFan === item.value, 'accent-red': item.accentRed && currentFan === item.value }"
+ :title="item.label"
+ @click="$emit('set-fan', device, item.value)"
+ v-permissions="['business:ywconditioner:operate']"
+ >{{ item.short }}</span>
+ </div>
+ <div class="cond-card__pwr">
+ <span class="pwr-dot" :class="{ on: device.pwr === 1 }"></span>
+ {{ device.pwr === 1 ? '杩愯涓�' : '宸插叧鏈�' }}
+ <span v-if="device.ktLock === 1" class="lock-tag">閿佸畾</span>
+ </div>
+ <div class="cond-card__actions">
+ <el-button size="mini" plain @click="$emit('history', device)">鍘嗗彶</el-button>
+ <el-button
+ v-if="isDeviceLocked"
+ size="mini"
+ type="danger"
+ plain
+ :loading="lockLoading"
+ @click="$emit('unlock', device)"
+ v-permissions="['business:ywconditioner:operate']"
+ >瑙i攣</el-button>
+ <el-button
+ v-else
+ size="mini"
+ plain
+ :loading="lockLoading"
+ @click="$emit('lock', device)"
+ v-permissions="['business:ywconditioner:operate']"
+ >閿佸畾</el-button>
+ <el-button
+ size="mini"
+ :type="device.pwr === 1 ? 'danger' : 'primary'"
+ :loading="powerLoading"
+ @click="$emit('toggle-power', device)"
+ v-permissions="['business:ywconditioner:operate']"
+ >{{ device.pwr === 1 ? '鍏虫満' : '寮�鏈�' }}</el-button>
+ </div>
+ <div class="cond-card__meta" v-if="device.wgMac">
+ <span v-if="device.wgMac" class="wg-block">
+ <span class="wg-mac" :title="device.wgMac">{{ device.wgMac }}</span>
+ <span v-if="device.wgBz" class="wg-bz" :title="device.wgBz">{{ device.wgBz }}</span>
+ </span>
+ </div>
+ </div>
+</template>
+
+<script>
+const MODE_OPTIONS = [
+ { value: 1, label: '鍒剁儹', icon: '鈽�', accentRed: true },
+ { value: 2, label: '鍒跺喎', icon: '鉂�' },
+ { value: 3, label: '閫侀', icon: '椋�' },
+ { value: 4, label: '闄ゆ箍', icon: '婀�' }
+]
+
+const FAN_OPTIONS = [
+ { value: 1, label: '浣庨��', short: 'L' },
+ { value: 2, label: '涓��', short: 'M' },
+ { value: 3, label: '楂橀��', short: 'H' },
+ { value: 4, label: '鑷姩', short: 'A', accentRed: true }
+]
+
+export default {
+ name: 'YwConditionerCard',
+ props: {
+ device: {
+ type: Object,
+ required: true
+ },
+ powerLoading: {
+ type: Boolean,
+ default: false
+ },
+ lockLoading: {
+ type: Boolean,
+ default: false
+ }
+ },
+ data () {
+ return {
+ modeOptions: MODE_OPTIONS,
+ fanOptions: FAN_OPTIONS
+ }
+ },
+ computed: {
+ isOnline () {
+ const o = this.device.online
+ if (o === '鍦ㄧ嚎' || o === '1' || o === 1) return true
+ if (o === 88 || o === 66 || o === '88' || o === '66') return true
+ return false
+ },
+ displayName () {
+ const parts = [this.device.floorName, this.device.roomName, this.device.name]
+ .filter(p => p != null && String(p).trim() !== '')
+ return parts.length ? parts.join('/') : '-'
+ },
+ currentFan () {
+ return this.device.fanSet != null ? this.device.fanSet : this.device.fan
+ },
+ isDeviceLocked () {
+ return this.device.ktLock === 1
+ }
+ },
+ methods: {
+ formatTemp (val) {
+ if (val === null || val === undefined || val === '') return '--'
+ const n = Number(val)
+ if (isNaN(n)) return val
+ const celsius = n > 100 ? n / 10 : n
+ return celsius.toFixed(1) + '鈩�'
+ }
+ }
+}
+</script>
+
+<style scoped>
+.cond-card {
+ background: linear-gradient(145deg, #2c3e50 0%, #1a252f 100%);
+ border-radius: 10px;
+ padding: 14px 16px;
+ color: #e8ecf0;
+ min-height: 220px;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ transition: opacity 0.2s;
+}
+.cond-card.offline { opacity: 0.75; }
+.cond-card__head {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 6px;
+}
+.cond-card__name {
+ font-weight: 600;
+ font-size: 15px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ flex: 1;
+}
+.cond-card__online.online { color: #67c23a; font-size: 18px; }
+.cond-card__online.offline { color: #909399; font-size: 18px; }
+.cond-card__uptime {
+ font-size: 12px;
+ color: #a8b4c0;
+ margin-bottom: 10px;
+}
+.cond-card__temp {
+ display: flex;
+ align-items: flex-end;
+ gap: 20px;
+ margin-bottom: 12px;
+}
+.temp-set .temp-num { font-size: 28px; font-weight: 300; line-height: 1; }
+.temp-indoor .temp-num.sm { font-size: 20px; color: #b0bec5; }
+.temp-label { display: block; font-size: 11px; color: #90a4ae; margin-top: 4px; }
+.cond-card__modes,
+.cond-card__fans {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 8px;
+}
+.cond-card__row-label {
+ font-size: 12px;
+ color: #90a4ae;
+ flex-shrink: 0;
+ min-width: 36px;
+}
+.mode-icon,
+.fan-icon {
+ width: 28px;
+ height: 28px;
+ border-radius: 6px;
+ background: rgba(255, 255, 255, 0.08);
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ color: #78909c;
+ cursor: pointer;
+}
+.mode-icon.active,
+.fan-icon.active {
+ background: rgba(64, 158, 255, 0.35);
+ color: #fff;
+}
+.mode-icon.active.accent-red,
+.fan-icon.active.accent-red {
+ background: rgba(245, 108, 108, 0.5);
+ color: #ffecec;
+ box-shadow: 0 0 0 1px rgba(245, 108, 108, 0.55);
+}
+.temp-btn {
+ color: #b0bec5;
+ padding: 0 4px;
+}
+.cond-card__pwr {
+ font-size: 13px;
+ margin-bottom: 12px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+.pwr-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #909399;
+}
+.pwr-dot.on { background: #67c23a; }
+.lock-tag {
+ font-size: 11px;
+ color: #e6a23c;
+ border: 1px solid #e6a23c;
+ border-radius: 3px;
+ padding: 0 4px;
+}
+.cond-card__actions {
+ display: flex;
+ gap: 6px;
+ flex-wrap: wrap;
+ margin-top: auto;
+}
+.cond-card__actions .el-button { flex: 1; min-width: 56px; }
+.cond-card__meta {
+ margin-top: 8px;
+ font-size: 11px;
+ color: #78909c;
+ display: flex;
+ justify-content: flex-end;
+ gap: 8px;
+}
+.wg-block {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ max-width: 100%;
+ text-align: right;
+ gap: 2px;
+}
+.wg-mac,
+.wg-bz {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ max-width: 100%;
+}
+.wg-bz {
+ color: #90a4ae;
+ font-size: 10px;
+ line-height: 1.2;
+}
+</style>
diff --git a/admin/src/views/business/components/YwConditionerGatewayLogWindow.vue b/admin/src/views/business/components/YwConditionerGatewayLogWindow.vue
new file mode 100644
index 0000000..b90f242
--- /dev/null
+++ b/admin/src/views/business/components/YwConditionerGatewayLogWindow.vue
@@ -0,0 +1,91 @@
+<template>
+ <el-dialog
+ :title="'涓婁笅绾胯褰� - ' + (gateway.wgMac || '')"
+ :visible.sync="visible"
+ width="720px"
+ append-to-body
+ @open="load"
+ >
+ <el-table v-loading="loading" :data="list" stripe size="small" max-height="400">
+ <el-table-column prop="wgMac" label="缃戝叧MAC" min-width="140" align="center" show-overflow-tooltip />
+ <el-table-column prop="oldStatus" label="鍘熺姸鎬�" min-width="90" align="center" />
+ <el-table-column prop="newStatus" label="鏂扮姸鎬�" min-width="90" align="center">
+ <template slot-scope="{ row }">
+ <span :class="row.newStatus === '鍦ㄧ嚎' ? 'green' : 'red'">{{ row.newStatus || '-' }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="logTime" label="璁板綍鏃堕棿" min-width="160" align="center" />
+ <el-table-column prop="source" label="鏉ユ簮" min-width="90" align="center" />
+ </el-table>
+ <pagination
+ class="mt10"
+ @size-change="onSizeChange"
+ @current-change="onPageChange"
+ :pagination="pagination"
+ />
+ </el-dialog>
+</template>
+
+<script>
+import Pagination from '@/components/common/Pagination'
+import { gatewayLogPage } from '@/api/business/ywconditionergateway'
+
+export default {
+ name: 'YwConditionerGatewayLogWindow',
+ components: { Pagination },
+ data () {
+ return {
+ visible: false,
+ loading: false,
+ gateway: {},
+ list: [],
+ pagination: {
+ pageIndex: 1,
+ pageSize: 10,
+ total: 0
+ }
+ }
+ },
+ methods: {
+ open (row) {
+ this.gateway = { ...row }
+ this.pagination.pageIndex = 1
+ this.visible = true
+ },
+ load () {
+ if (!this.gateway.id && !this.gateway.wgMac) return
+ this.loading = true
+ const model = {}
+ if (this.gateway.id) model.gatewayId = this.gateway.id
+ if (this.gateway.wgMac) model.wgMac = this.gateway.wgMac
+ gatewayLogPage({
+ page: this.pagination.pageIndex,
+ capacity: this.pagination.pageSize,
+ model,
+ sorts: []
+ })
+ .then(data => {
+ this.list = data.records || []
+ this.pagination.total = data.total || 0
+ })
+ .catch(() => {})
+ .finally(() => { this.loading = false })
+ },
+ onPageChange (page) {
+ this.pagination.pageIndex = page || 1
+ this.load()
+ },
+ onSizeChange (size) {
+ this.pagination.pageSize = size
+ this.pagination.pageIndex = 1
+ this.load()
+ }
+ }
+}
+</script>
+
+<style scoped>
+.mt10 { margin-top: 10px; }
+.green { color: #67c23a; }
+.red { color: #f56c6c; }
+</style>
diff --git a/admin/src/views/business/components/YwConditionerHistoryWindow.vue b/admin/src/views/business/components/YwConditionerHistoryWindow.vue
new file mode 100644
index 0000000..bf7e2f7
--- /dev/null
+++ b/admin/src/views/business/components/YwConditionerHistoryWindow.vue
@@ -0,0 +1,102 @@
+<template>
+ <GlobalWindow title="鎺у埗鍘嗗彶" :visible.sync="visible" width="900px" :show-confirm="false">
+ <p class="device-info" v-if="device.id">{{ device.name || device.code }} 路 {{ device.wgMac }}</p>
+ <el-table v-loading="loading" :data="list" stripe size="small" max-height="420">
+ <el-table-column prop="id" label="ID" width="70" align="center" />
+ <el-table-column label="鎿嶄綔绫诲瀷" min-width="100" align="center">
+ <template slot-scope="{ row }">{{ formatActionType(row.actionType) }}</template>
+ </el-table-column>
+ <el-table-column prop="actionContent" label="鎿嶄綔鎻忚堪" min-width="180" show-overflow-tooltip align="center" />
+ <el-table-column label="缁撴灉" width="80" align="center">
+ <template slot-scope="{ row }">
+ <span :class="row.resultStatus === 1 ? 'green' : 'red'">{{ row.resultStatus === 1 ? '鎴愬姛' : '澶辫触' }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="resultMsg" label="璇存槑" min-width="120" show-overflow-tooltip align="center" />
+ <el-table-column prop="createDate" label="鏃堕棿" min-width="150" align="center" />
+ </el-table>
+ <pagination
+ class="mt10"
+ @size-change="onSizeChange"
+ @current-change="onPageChange"
+ :pagination="pagination"
+ />
+ </GlobalWindow>
+</template>
+
+<script>
+import GlobalWindow from '@/components/common/GlobalWindow'
+import Pagination from '@/components/common/Pagination'
+import { historyPage } from '@/api/business/ywconditioner'
+
+const ACTION_TYPE_MAP = {
+ 1: '寮�鍏�',
+ 2: '妯″紡',
+ 3: '椋庨��',
+ 4: '娓╁害',
+ 5: '閿佸畾',
+ 6: '鏌ョ數閲�',
+ 7: '鏌ュ姛鐜�'
+}
+
+export default {
+ name: 'YwConditionerHistoryWindow',
+ components: { GlobalWindow, Pagination },
+ data () {
+ return {
+ visible: false,
+ loading: false,
+ device: {},
+ list: [],
+ pagination: {
+ pageIndex: 1,
+ pageSize: 10,
+ total: 0
+ }
+ }
+ },
+ methods: {
+ open (row) {
+ this.device = { ...row }
+ this.pagination.pageIndex = 1
+ this.visible = true
+ this.load()
+ },
+ load () {
+ if (!this.device.id) return
+ this.loading = true
+ historyPage({
+ page: this.pagination.pageIndex,
+ capacity: this.pagination.pageSize,
+ model: { conditionerId: this.device.id },
+ sorts: []
+ })
+ .then(data => {
+ this.list = data.records || []
+ this.pagination.total = data.total || 0
+ })
+ .catch(() => {})
+ .finally(() => { this.loading = false })
+ },
+ onPageChange (page) {
+ this.pagination.pageIndex = page || 1
+ this.load()
+ },
+ onSizeChange (size) {
+ this.pagination.pageSize = size
+ this.pagination.pageIndex = 1
+ this.load()
+ },
+ formatActionType (val) {
+ return ACTION_TYPE_MAP[val] || val || '-'
+ }
+ }
+}
+</script>
+
+<style scoped>
+.device-info { margin-bottom: 12px; color: #606266; }
+.mt10 { margin-top: 10px; }
+.green { color: #67c23a; }
+.red { color: #f56c6c; }
+</style>
diff --git a/admin/src/views/business/components/YwConditionerLockWindow.vue b/admin/src/views/business/components/YwConditionerLockWindow.vue
new file mode 100644
index 0000000..283ea8a
--- /dev/null
+++ b/admin/src/views/business/components/YwConditionerLockWindow.vue
@@ -0,0 +1,121 @@
+<template>
+ <GlobalWindow title="璁惧閿佸畾璁剧疆" :visible.sync="visible" width="520px" @confirm="save" :confirm-working="isSaving">
+ <div class="lock-form" v-if="device.id">
+ <p class="device-info">{{ device.name || device.code }} 路 {{ device.wgMac }}</p>
+ <div class="lock-section">
+ <div class="lock-label">寮�鍏崇姸鎬�</div>
+ <el-radio-group v-model="form.pwr" size="small">
+ <el-radio-button :label="-1">涓嶉檺</el-radio-button>
+ <el-radio-button :label="0">鍏�</el-radio-button>
+ <el-radio-button :label="1">寮�</el-radio-button>
+ </el-radio-group>
+ </div>
+ <div class="lock-section">
+ <div class="lock-label">妯″紡閿佸畾</div>
+ <el-radio-group v-model="form.mode" size="small">
+ <el-radio-button :label="-1">涓嶉檺</el-radio-button>
+ <el-radio-button v-for="item in modeOptions" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button>
+ </el-radio-group>
+ </div>
+ <div class="lock-section">
+ <div class="lock-label">椋庨�熼攣瀹�</div>
+ <el-radio-group v-model="form.fan" size="small">
+ <el-radio-button :label="-1">涓嶉檺</el-radio-button>
+ <el-radio-button v-for="item in fanOptions" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button>
+ </el-radio-group>
+ </div>
+ <div class="lock-section">
+ <div class="lock-label">娓╁害鑼冨洿 (掳C)</div>
+ <div class="temp-range">
+ <el-input-number v-model="form.minTemp" :min="-1" :max="35" :step="1" size="small" controls-position="right" />
+ <span class="range-sep">~</span>
+ <el-input-number v-model="form.maxTemp" :min="-1" :max="35" :step="1" size="small" controls-position="right" />
+ </div>
+ <p class="hint">璁句负 -1 琛ㄧず涓嶉檺鍒讹紙淇濆瓨鏃舵湭濉垯浼� -1锛�</p>
+ </div>
+ </div>
+ </GlobalWindow>
+</template>
+
+<script>
+import GlobalWindow from '@/components/common/GlobalWindow'
+import { lock } from '@/api/business/ywconditioner'
+
+const MODE_OPTIONS = [
+ { value: 0, label: '鑷姩' },
+ { value: 1, label: '鍒跺喎' },
+ { value: 2, label: '鍒剁儹' },
+ { value: 3, label: '閫侀' },
+ { value: 4, label: '闄ゆ箍' }
+]
+
+const FAN_OPTIONS = [
+ { value: 0, label: '鑷姩' },
+ { value: 1, label: '浣庨��' },
+ { value: 2, label: '涓��' },
+ { value: 3, label: '楂橀��' }
+]
+
+export default {
+ name: 'YwConditionerLockWindow',
+ components: { GlobalWindow },
+ data () {
+ return {
+ visible: false,
+ isSaving: false,
+ device: {},
+ form: {
+ pwr: -1,
+ mode: -1,
+ fan: -1,
+ minTemp: 17,
+ maxTemp: 32
+ },
+ modeOptions: MODE_OPTIONS,
+ fanOptions: FAN_OPTIONS
+ }
+ },
+ methods: {
+ open (row) {
+ this.device = { ...row }
+ this.form = {
+ pwr: 1,
+ mode: 2,
+ fan: 0,
+ minTemp: 17,
+ maxTemp: 32
+ }
+ this.visible = true
+ },
+ save () {
+ this.isSaving = true
+ lock({
+ id: this.device.id,
+ lockPwr: 1,
+ pwr: this.form.pwr,
+ mode: this.form.mode,
+ fan: this.form.fan,
+ minTemp: this.form.minTemp != null ? this.form.minTemp : -1,
+ maxTemp: this.form.maxTemp != null ? this.form.maxTemp : -1,
+ source: 'admin'
+ })
+ .then(res => {
+ this.$tip.apiSuccess(res || '閿佸畾鎴愬姛')
+ this.visible = false
+ this.$emit('success')
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isSaving = false })
+ }
+ }
+}
+</script>
+
+<style scoped>
+.device-info { margin-bottom: 16px; color: #606266; }
+.lock-section { margin-bottom: 18px; }
+.lock-label { font-size: 13px; color: #303133; margin-bottom: 8px; font-weight: 500; }
+.temp-range { display: flex; align-items: center; gap: 8px; }
+.range-sep { color: #909399; }
+.hint { font-size: 12px; color: #909399; margin-top: 6px; }
+</style>
diff --git a/admin/src/views/business/components/YwElectricalEdit.vue b/admin/src/views/business/components/YwElectricalEdit.vue
index 8522326..a354342 100644
--- a/admin/src/views/business/components/YwElectricalEdit.vue
+++ b/admin/src/views/business/components/YwElectricalEdit.vue
@@ -1,5 +1,5 @@
<template>
- <GlobalWindow title="缂栬緫鐢佃〃" :visible.sync="visible" width="920px" :confirm-working="isWorking" @confirm="confirm">
+ <GlobalWindow title="缂栬緫鐢佃〃" :visible.sync="visible" width="720px" :confirm-working="isWorking" @confirm="confirm">
<el-form ref="form" :model="form" :rules="rules" label-width="120px" class="electrical-edit-form">
<el-form-item label="閲囬泦鍣ㄥ彿">
<span>{{ form.collectorId }}</span>
diff --git a/admin/src/views/business/ywconditioner.vue b/admin/src/views/business/ywconditioner.vue
new file mode 100644
index 0000000..598a89e
--- /dev/null
+++ b/admin/src/views/business/ywconditioner.vue
@@ -0,0 +1,342 @@
+<template>
+ <TableLayout :permissions="['business:ywconditioner:query']">
+ <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
+ <el-form-item label="璁惧淇℃伅" prop="devKeyword">
+ <el-input v-model="searchForm.devKeyword" placeholder="鍚嶇О/缂栧彿/鎴块棿" clearable @keypress.enter.native="search" />
+ </el-form-item>
+ <el-form-item label="鍦ㄧ嚎鐘舵��" prop="onlineFilter">
+ <el-select v-model="searchForm.onlineFilter" clearable placeholder="鍏ㄩ儴" style="min-width: 120px">
+ <el-option label="鍦ㄧ嚎" value="鍦ㄧ嚎" />
+ <el-option label="绂荤嚎" value="绂荤嚎" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="寮�鍏崇姸鎬�" prop="pwrFilter">
+ <el-select v-model="searchForm.pwrFilter" clearable placeholder="鍏ㄩ儴" style="min-width: 120px">
+ <el-option label="寮�鏈�" :value="1" />
+ <el-option label="鍏虫満" :value="0" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="缃戝叧MAC" prop="wgMacFilter">
+ <el-select v-model="searchForm.wgMacFilter" clearable filterable placeholder="鍏ㄩ儴" style="min-width: 180px">
+ <el-option v-for="item in gatewayOptions" :key="item.wgMac" :label="item.wgMac" :value="item.wgMac" />
+ </el-select>
+ </el-form-item>
+ <section>
+ <el-button type="primary" icon="el-icon-search" @click="search">鏌ヨ</el-button>
+ <el-button icon="el-icon-refresh" @click="reset">閲嶇疆</el-button>
+ </section>
+ </el-form>
+ <template v-slot:table-wrap>
+ <ul class="toolbar">
+ <li>
+ <el-button
+ type="primary"
+ :loading="isSyncingAll"
+ v-permissions="['business:ywconditioner:sync']"
+ @click="handleSyncAll"
+ >涓�閿悓姝ョ┖璋冨鑱旀満</el-button>
+ </li>
+ <li>
+ <el-button
+ type="primary"
+ :loading="isSyncingDevices"
+ v-permissions="['business:ywconditioner:sync']"
+ @click="handleSyncDevices"
+ >鍚屾璁惧鍜岀姸鎬�</el-button>
+ </li>
+ </ul>
+ <div v-loading="isWorking.search" class="card-grid-wrap">
+ <div v-if="!tableData.list.length && !isWorking.search" class="empty-tip">鏆傛棤璁惧鏁版嵁锛岃鍏堝悓姝ュ唴鏈�</div>
+ <div class="card-grid">
+ <YwConditionerCard
+ v-for="item in tableData.list"
+ :key="item.id"
+ :device="item"
+ :power-loading="powerLoadingId === item.id"
+ :lock-loading="lockLoadingId === item.id"
+ @history="openHistory"
+ @lock="openLock"
+ @unlock="unlockDevice"
+ @toggle-power="togglePower"
+ @set-mode="setMode"
+ @set-fan="setFan"
+ @set-temp="setTemp"
+ />
+ </div>
+ </div>
+ <pagination
+ @size-change="handleSizeChange"
+ @current-change="handlePageChange"
+ :pagination="paginationConfig"
+ />
+ </template>
+ <YwConditionerLockWindow ref="lockWindow" @success="refreshList" />
+ <YwConditionerHistoryWindow ref="historyWindow" />
+ </TableLayout>
+</template>
+
+<script>
+import BaseTable from '@/components/base/BaseTable'
+import TableLayout from '@/layouts/TableLayout'
+import Pagination from '@/components/common/Pagination'
+import * as conditionerApi from '@/api/business/ywconditioner'
+import YwConditionerCard from './components/YwConditionerCard'
+import YwConditionerLockWindow from './components/YwConditionerLockWindow'
+import YwConditionerHistoryWindow from './components/YwConditionerHistoryWindow'
+
+const ADJUST_OPERATE_INTERVAL_MS = 2000
+const ADJUST_ACTION_TYPES = [2, 3, 4]
+const OFFLINE_TIP = '璁惧绂荤嚎浜�,璇锋鏌ュ搴旇澶囩綉缁�'
+
+export default {
+ name: 'YwConditioner',
+ extends: BaseTable,
+ components: {
+ TableLayout,
+ Pagination,
+ YwConditionerCard,
+ YwConditionerLockWindow,
+ YwConditionerHistoryWindow
+ },
+ data () {
+ return {
+ searchForm: {
+ devKeyword: '',
+ onlineFilter: '',
+ pwrFilter: null,
+ wgMacFilter: ''
+ },
+ gatewayOptions: [],
+ isSyncingAll: false,
+ isSyncingDevices: false,
+ powerLoadingId: null,
+ lockLoadingId: null,
+ deviceAdjustOperateAt: {}
+ }
+ },
+ computed: {
+ paginationConfig () {
+ return {
+ ...this.tableData.pagination,
+ pageSizes: [15, 30, 45, 60]
+ }
+ }
+ },
+ created () {
+ this.api = conditionerApi
+ this.module = '绌鸿皟鍐呮満'
+ this.configData['field.id'] = 'id'
+ this.configData['field.main'] = 'name'
+ this.tableData.pagination.pageSize = 15
+ this.loadGatewayOptions()
+ this.search()
+ },
+ methods: {
+ loadGatewayOptions () {
+ conditionerApi.gatewayOptions()
+ .then(list => { this.gatewayOptions = list || [] })
+ .catch(() => {})
+ },
+ buildSearchModel () {
+ const model = {}
+ if (this.searchForm.devKeyword) model.devKeyword = this.searchForm.devKeyword
+ if (this.searchForm.onlineFilter) model.onlineFilter = this.searchForm.onlineFilter
+ if (this.searchForm.pwrFilter !== null && this.searchForm.pwrFilter !== '') {
+ model.pwrFilter = this.searchForm.pwrFilter
+ }
+ if (this.searchForm.wgMacFilter) model.wgMacFilter = this.searchForm.wgMacFilter
+ return model
+ },
+ handlePageChange (pageIndex) {
+ const page = Number(pageIndex)
+ if (page > 0) {
+ this.tableData.pagination.pageIndex = page
+ }
+ this.isWorking.search = true
+ conditionerApi.fetchCardPage({
+ page: this.tableData.pagination.pageIndex,
+ capacity: this.tableData.pagination.pageSize,
+ model: this.buildSearchModel(),
+ sorts: this.tableData.sorts
+ })
+ .then(data => {
+ this.tableData.list = data.records || []
+ this.tableData.pagination.total = data.total || 0
+ })
+ .catch(() => {})
+ .finally(() => { this.isWorking.search = false })
+ },
+ refreshList () {
+ this.handlePageChange(this.tableData.pagination.pageIndex)
+ },
+ patchDeviceInList (id, patch) {
+ const idx = this.tableData.list.findIndex(item => item.id === id)
+ if (idx === -1) return
+ this.$set(this.tableData.list, idx, { ...this.tableData.list[idx], ...patch })
+ },
+ isDeviceOnline (row) {
+ const o = row && row.online
+ if (o === '鍦ㄧ嚎' || o === '1' || o === 1) return true
+ if (o === 88 || o === 66 || o === '88' || o === '66') return true
+ if (typeof o === 'string' && o.toLowerCase() === 'online') return true
+ return false
+ },
+ ensureDeviceOnline (row) {
+ if (!this.isDeviceOnline(row)) {
+ this.$tip.warning(OFFLINE_TIP)
+ return false
+ }
+ return true
+ },
+ reserveAdjustOperate (deviceId) {
+ const last = this.deviceAdjustOperateAt[deviceId]
+ const now = Date.now()
+ if (last && now - last < ADJUST_OPERATE_INTERVAL_MS) {
+ const waitSec = ((ADJUST_OPERATE_INTERVAL_MS - (now - last)) / 1000).toFixed(1)
+ this.$tip.warning(`鎿嶄綔杩囦簬棰戠箒锛岃 ${waitSec} 绉掑悗鍐嶈瘯`)
+ return false
+ }
+ this.$set(this.deviceAdjustOperateAt, deviceId, now)
+ return true
+ },
+ reset () {
+ this.searchForm = { devKeyword: '', onlineFilter: '', pwrFilter: null, wgMacFilter: '' }
+ this.search()
+ },
+ handleSyncAll () {
+ this.$dialog.actionConfirm('纭鍚屾椂鍏ㄩ噺鍚屾鏇存柊 缃戝叧銆佺數琛ㄣ�佽璐圭郴鏁般�佸唴鏈烘暟鎹俊鎭悧锛�', '涓�閿悓姝ョ┖璋冨鑱旀満')
+ .then(() => {
+ this.isSyncingAll = true
+ conditionerApi.syncAll()
+ .then(res => {
+ this.$tip.apiSuccess(res || '鍚屾鎴愬姛')
+ this.loadGatewayOptions()
+ this.search()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isSyncingAll = false })
+ })
+ .catch(() => {})
+ },
+ handleSyncDevices () {
+ this.$dialog.actionConfirm('纭浠庢櫤绮剧伒骞冲彴鍚屾璁惧淇℃伅鍜屾渶鏂拌澶囩姸鎬佸悧锛�', '鍚屾璁惧鍜岀姸鎬�')
+ .then(() => {
+ this.isSyncingDevices = true
+ conditionerApi.syncDevicesAndStatus()
+ .then(res => {
+ this.$tip.apiSuccess(res || '鍚屾鎴愬姛')
+ this.search()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isSyncingDevices = false })
+ })
+ .catch(() => {})
+ },
+ openHistory (row) {
+ this.$refs.historyWindow.open(row)
+ },
+ openLock (row) {
+ if (!this.ensureDeviceOnline(row)) return
+ this.$refs.lockWindow.open(row)
+ },
+ unlockDevice (row) {
+ if (!this.ensureDeviceOnline(row)) return
+ this.$dialog.actionConfirm('纭瑙i攣璇ヨ澶囩殑閿佸畾璁剧疆鍚楋紵', '璁惧瑙i攣')
+ .then(() => {
+ this.lockLoadingId = row.id
+ conditionerApi.lock({
+ id: row.id,
+ lockPwr: 0,
+ pwr: -1,
+ mode: -1,
+ fan: -1,
+ minTemp: -1,
+ maxTemp: -1,
+ source: 'admin'
+ })
+ .then(res => {
+ this.$tip.apiSuccess(res || '瑙i攣鎴愬姛')
+ this.patchDeviceInList(row.id, { ktLock: 0, lockPwr: 0 })
+ this.refreshList()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.lockLoadingId = null })
+ })
+ .catch(() => {})
+ },
+ togglePower (row) {
+ if (!this.ensureDeviceOnline(row)) return
+ const next = row.pwr === 1 ? 0 : 1
+ const msg = next === 1 ? '纭寮�鏈哄悧锛�' : '纭鍏虫満鍚楋紵'
+ this.$dialog.actionConfirm(msg, '璁惧鎺у埗')
+ .then(() => {
+ this.powerLoadingId = row.id
+ conditionerApi.operate({
+ id: row.id,
+ actionType: 1,
+ setVal: next,
+ source: 'admin'
+ })
+ .then(res => {
+ this.$tip.apiSuccess(res || '鎿嶄綔鎴愬姛')
+ this.patchDeviceInList(row.id, { pwr: next })
+ this.refreshList()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.powerLoadingId = null })
+ })
+ .catch(() => {})
+ },
+ doOperate (row, actionType, setVal, label, patch) {
+ if (!this.ensureDeviceOnline(row)) return
+ if (ADJUST_ACTION_TYPES.includes(actionType) && !this.reserveAdjustOperate(row.id)) {
+ return
+ }
+ conditionerApi.operate({
+ id: row.id,
+ actionType,
+ setVal,
+ source: 'admin'
+ })
+ .then(res => {
+ this.$tip.apiSuccess(res || label + '鎴愬姛')
+ if (patch) {
+ this.patchDeviceInList(row.id, patch)
+ }
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ },
+ setMode (row, mode) {
+ if (row.mode === mode) return
+ this.doOperate(row, 2, mode, '妯″紡璁剧疆', { mode })
+ },
+ setFan (row, fan) {
+ const cur = row.fanSet != null ? row.fanSet : row.fan
+ if (cur === fan) return
+ this.doOperate(row, 3, fan, '椋庨�熻缃�', { fanSet: fan, fan })
+ },
+ setTemp (row, delta) {
+ let cur = Number(row.tempSet)
+ if (isNaN(cur)) cur = 260
+ if (cur > 100) cur = cur / 10
+ const next = Math.max(16, Math.min(32, cur + delta))
+ this.doOperate(row, 4, next, '娓╁害璁剧疆', { tempSet: next * 10 })
+ }
+ }
+}
+</script>
+
+<style scoped>
+.card-grid-wrap { min-height: 120px; }
+.card-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
+ gap: 16px;
+ padding: 8px 0 16px;
+}
+.empty-tip {
+ text-align: center;
+ color: #909399;
+ padding: 48px 0;
+}
+</style>
diff --git a/admin/src/views/business/ywconditioneractions.vue b/admin/src/views/business/ywconditioneractions.vue
new file mode 100644
index 0000000..d6aad2a
--- /dev/null
+++ b/admin/src/views/business/ywconditioneractions.vue
@@ -0,0 +1,195 @@
+<template>
+ <TableLayout :permissions="['business:ywconditioneractions:query']">
+ <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
+ <el-form-item label="鎿嶄綔绫诲瀷" prop="actionType">
+ <el-select v-model="searchForm.actionType" clearable placeholder="鍏ㄩ儴" style="min-width: 160px">
+ <el-option v-for="item in actionTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="璁惧淇℃伅" prop="devKeyword">
+ <el-input v-model="searchForm.devKeyword" placeholder="璁惧鍚嶇О" clearable @keypress.enter.native="search" />
+ </el-form-item>
+ <el-form-item label="缃戝叧MAC" prop="wgMac">
+ <el-input v-model="searchForm.wgMac" placeholder="缃戝叧MAC" clearable @keypress.enter.native="search" />
+ </el-form-item>
+ <el-form-item label="鎿嶄綔鏃堕棿" prop="operateTimeRange">
+ <el-date-picker
+ v-model="searchForm.operateTimeRange"
+ type="datetimerange"
+ value-format="yyyy-MM-dd HH:mm:ss"
+ range-separator="-"
+ start-placeholder="寮�濮嬫椂闂�"
+ end-placeholder="缁撴潫鏃堕棿"
+ style="width: 360px"
+ />
+ </el-form-item>
+ <section>
+ <el-button type="primary" icon="el-icon-search" @click="search">鏌ヨ</el-button>
+ <el-button icon="el-icon-refresh" @click="reset">閲嶇疆</el-button>
+ </section>
+ </el-form>
+ <template v-slot:table-wrap>
+ <ul class="toolbar">
+ <li>
+ <el-button
+ @click="exportExcel"
+ :loading="isWorking.export"
+ v-permissions="['business:ywconditioneractions:exportExcel']"
+ >瀵煎嚭</el-button>
+ </li>
+ </ul>
+ <el-table v-loading="isWorking.search" :data="tableData.list" stripe>
+ <el-table-column prop="id" label="ID" min-width="70" align="center" />
+ <el-table-column prop="devName" label="璁惧鍚嶇О" min-width="140" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ row.devName || '-' }}</template>
+ </el-table-column>
+ <el-table-column prop="wgMac" label="缃戝叧MAC" min-width="140" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ row.wgMac || '-' }}</template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔绫诲瀷" min-width="100" align="center">
+ <template slot-scope="{ row }">{{ formatActionType(row.actionType) }}</template>
+ </el-table-column>
+ <el-table-column prop="actionContent" label="鎿嶄綔鎻忚堪" min-width="180" align="center" show-overflow-tooltip />
+ <el-table-column label="缁撴灉" min-width="80" align="center">
+ <template slot-scope="{ row }">
+ <span :class="row.resultStatus === 1 ? 'green' : 'red'">{{ formatResult(row.resultStatus) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="resultMsg" label="璇存槑" min-width="140" align="center" show-overflow-tooltip />
+ <el-table-column prop="createDate" label="鎿嶄綔鏃堕棿" min-width="160" align="center" />
+ <el-table-column label="璇锋眰鎶ユ枃" min-width="90" align="center">
+ <template slot-scope="{ row }">
+ <el-button type="text" :disabled="!row.requestBody" @click="openJson('璇锋眰鎶ユ枃', row.requestBody)">鏌ョ湅</el-button>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍝嶅簲鎶ユ枃" min-width="90" align="center">
+ <template slot-scope="{ row }">
+ <el-button type="text" :disabled="!row.responseBody" @click="openJson('鍝嶅簲鎶ユ枃', row.responseBody)">鏌ョ湅</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination
+ @size-change="handleSizeChange"
+ @current-change="handlePageChange"
+ :pagination="tableData.pagination"
+ />
+ </template>
+ <OperaInterfaceLogWindow ref="jsonWindow" />
+ </TableLayout>
+</template>
+
+<script>
+import BaseTable from '@/components/base/BaseTable'
+import TableLayout from '@/layouts/TableLayout'
+import Pagination from '@/components/common/Pagination'
+import OperaInterfaceLogWindow from '@/components/business/OperaInterfaceLogWindow'
+import * as actionsApi from '@/api/business/ywconditioneractions'
+
+const ACTION_TYPE_MAP = {
+ 1: '寮�鍏�',
+ 2: '妯″紡',
+ 3: '椋庨��',
+ 4: '娓╁害',
+ 5: '閿佸畾',
+ 6: '鏌ョ數閲�',
+ 7: '鏌ュ姛鐜�'
+}
+
+export default {
+ name: 'YwConditionerActions',
+ extends: BaseTable,
+ components: { TableLayout, Pagination, OperaInterfaceLogWindow },
+ data () {
+ return {
+ searchForm: {
+ actionType: null,
+ devKeyword: '',
+ wgMac: '',
+ operateTimeRange: []
+ },
+ actionTypeOptions: Object.keys(ACTION_TYPE_MAP).map(key => ({
+ value: Number(key),
+ label: ACTION_TYPE_MAP[key]
+ }))
+ }
+ },
+ created () {
+ this.api = actionsApi
+ this.module = '绌鸿皟鎺у埗璁板綍'
+ this.configData['field.id'] = 'id'
+ this.configData['field.main'] = 'id'
+ this.search()
+ },
+ methods: {
+ buildSearchModel () {
+ const model = {}
+ if (this.searchForm.actionType !== null && this.searchForm.actionType !== '' && this.searchForm.actionType !== undefined) {
+ model.actionType = this.searchForm.actionType
+ }
+ if (this.searchForm.devKeyword) model.devKeyword = this.searchForm.devKeyword
+ if (this.searchForm.wgMac) model.wgMac = this.searchForm.wgMac
+ if (this.searchForm.operateTimeRange && this.searchForm.operateTimeRange.length === 2) {
+ model.operateTimeBegin = this.searchForm.operateTimeRange[0]
+ model.operateTimeEnd = this.searchForm.operateTimeRange[1]
+ }
+ return model
+ },
+ handlePageChange (pageIndex) {
+ this.tableData.pagination.pageIndex = pageIndex || this.tableData.pagination.pageIndex
+ this.isWorking.search = true
+ actionsApi.fetchList({
+ page: this.tableData.pagination.pageIndex,
+ capacity: this.tableData.pagination.pageSize,
+ model: this.buildSearchModel(),
+ sorts: this.tableData.sorts
+ })
+ .then(data => {
+ this.tableData.list = data.records || []
+ this.tableData.pagination.total = data.total || 0
+ })
+ .catch(() => {})
+ .finally(() => { this.isWorking.search = false })
+ },
+ reset () {
+ this.searchForm = { actionType: null, devKeyword: '', wgMac: '', operateTimeRange: [] }
+ if (this.$refs.searchForm) {
+ this.$refs.searchForm.resetFields()
+ }
+ this.search()
+ },
+ formatActionType (val) {
+ return ACTION_TYPE_MAP[val] || val || '-'
+ },
+ formatResult (val) {
+ if (val === 1 || val === '1') return '鎴愬姛'
+ if (val === 0 || val === '0') return '澶辫触'
+ return val == null ? '-' : String(val)
+ },
+ openJson (title, content) {
+ if (!content) return
+ this.$refs.jsonWindow.open(title, { content })
+ },
+ exportExcel () {
+ this.$dialog.exportConfirm('纭瀵煎嚭鍚楋紵')
+ .then(() => {
+ this.isWorking.export = true
+ actionsApi.exportExcel({
+ page: 1,
+ capacity: 1000000,
+ model: this.buildSearchModel(),
+ sorts: this.tableData.sorts
+ })
+ .then(response => { this.download(response) })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isWorking.export = false })
+ })
+ .catch(() => {})
+ }
+ }
+}
+</script>
+
+<style scoped>
+.green { color: #67c23a; }
+.red { color: #f56c6c; }
+</style>
diff --git a/admin/src/views/business/ywconditionerbilling.vue b/admin/src/views/business/ywconditionerbilling.vue
new file mode 100644
index 0000000..5c31ff6
--- /dev/null
+++ b/admin/src/views/business/ywconditionerbilling.vue
@@ -0,0 +1,127 @@
+<template>
+ <TableLayout :permissions="['business:ywconditionerbilling:query']">
+ <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
+ <el-form-item label="璁惧淇℃伅" prop="devKeyword">
+ <el-input v-model="searchForm.devKeyword" placeholder="璁惧鍚嶇О/ID" clearable @keypress.enter.native="search" />
+ </el-form-item>
+ <el-form-item label="缃戝叧MAC" prop="wgMac">
+ <el-input v-model="searchForm.wgMac" placeholder="缃戝叧MAC" clearable @keypress.enter.native="search" />
+ </el-form-item>
+ <section>
+ <el-button type="primary" icon="el-icon-search" @click="search">鏌ヨ</el-button>
+ <el-button icon="el-icon-refresh" @click="reset">閲嶇疆</el-button>
+ </section>
+ </el-form>
+ <template v-slot:table-wrap>
+ <ul class="toolbar">
+ <li>
+ <el-button
+ type="primary"
+ :loading="isSyncing"
+ v-permissions="['business:ywconditionerbilling:sync']"
+ @click="handleSync"
+ >鍚屾璁¤垂绯绘暟</el-button>
+ </li>
+ </ul>
+ <el-table v-loading="isWorking.search" :data="tableData.list" stripe>
+ <el-table-column prop="platformDevId" label="骞冲彴璁惧ID" min-width="110" align="center" />
+ <el-table-column prop="devName" label="璁惧鍚嶇О" min-width="140" align="center" show-overflow-tooltip />
+ <el-table-column prop="wgMac" label="缃戝叧MAC" min-width="140" align="center" show-overflow-tooltip />
+ <el-table-column label="璁¤垂绫诲瀷" min-width="100" align="center">
+ <template slot-scope="{ row }">{{ formatKwType(row.kwType) }}</template>
+ </el-table-column>
+ <el-table-column prop="fanArg" label="椋庢満绯绘暟" min-width="100" align="center" />
+ <el-table-column prop="fanKw" label="椋庢満鍔熺巼" min-width="100" align="center" />
+ <el-table-column prop="higKw" label="楂樻。鍔熺巼" min-width="100" align="center" />
+ <el-table-column prop="midKw" label="涓。鍔熺巼" min-width="100" align="center" />
+ <el-table-column prop="lowKw" label="浣庢。鍔熺巼" min-width="100" align="center" />
+ <el-table-column prop="lastSyncDate" label="鏈�鍚庡悓姝�" min-width="160" align="center" />
+ </el-table>
+ <pagination
+ @size-change="handleSizeChange"
+ @current-change="handlePageChange"
+ :pagination="tableData.pagination"
+ />
+ </template>
+ </TableLayout>
+</template>
+
+<script>
+import BaseTable from '@/components/base/BaseTable'
+import TableLayout from '@/layouts/TableLayout'
+import Pagination from '@/components/common/Pagination'
+import * as billingApi from '@/api/business/ywconditionerbilling'
+
+const KW_TYPE_MAP = {
+ 0: '鏃堕暱',
+ 1: '鑳借��',
+ 2: '鐢佃〃'
+}
+
+export default {
+ name: 'YwConditionerBilling',
+ extends: BaseTable,
+ components: { TableLayout, Pagination },
+ data () {
+ return {
+ searchForm: {
+ devKeyword: '',
+ wgMac: ''
+ },
+ isSyncing: false
+ }
+ },
+ created () {
+ this.api = billingApi
+ this.module = '绌鸿皟璁¤垂绯绘暟'
+ this.configData['field.id'] = 'id'
+ this.configData['field.main'] = 'devName'
+ this.search()
+ },
+ methods: {
+ buildSearchModel () {
+ const model = {}
+ if (this.searchForm.devKeyword) model.devKeyword = this.searchForm.devKeyword
+ if (this.searchForm.wgMac) model.wgMac = this.searchForm.wgMac
+ return model
+ },
+ handlePageChange (pageIndex) {
+ this.tableData.pagination.pageIndex = pageIndex || this.tableData.pagination.pageIndex
+ this.isWorking.search = true
+ billingApi.fetchList({
+ page: this.tableData.pagination.pageIndex,
+ capacity: this.tableData.pagination.pageSize,
+ model: this.buildSearchModel(),
+ sorts: this.tableData.sorts
+ })
+ .then(data => {
+ this.tableData.list = data.records || []
+ this.tableData.pagination.total = data.total || 0
+ })
+ .catch(() => {})
+ .finally(() => { this.isWorking.search = false })
+ },
+ reset () {
+ this.searchForm = { devKeyword: '', wgMac: '' }
+ this.search()
+ },
+ formatKwType (val) {
+ return KW_TYPE_MAP[val] != null ? KW_TYPE_MAP[val] : (val == null ? '-' : val)
+ },
+ handleSync () {
+ this.$dialog.actionConfirm('纭浠庢櫤绮剧伒骞冲彴鍚屾鍏ㄩ儴璁¤垂绯绘暟鍚楋紵', '鍚屾璁¤垂绯绘暟')
+ .then(() => {
+ this.isSyncing = true
+ billingApi.syncAll()
+ .then(res => {
+ this.$tip.apiSuccess(res || '鍚屾鎴愬姛')
+ this.search()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isSyncing = false })
+ })
+ .catch(() => {})
+ }
+ }
+}
+</script>
diff --git a/admin/src/views/business/ywconditionergateway.vue b/admin/src/views/business/ywconditionergateway.vue
new file mode 100644
index 0000000..1877eee
--- /dev/null
+++ b/admin/src/views/business/ywconditionergateway.vue
@@ -0,0 +1,139 @@
+<template>
+ <TableLayout :permissions="['business:ywconditionergateway:query']">
+ <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
+ <el-form-item label="缃戝叧MAC" prop="wgMac">
+ <el-input v-model="searchForm.wgMac" placeholder="MAC鍦板潃" clearable @keypress.enter.native="search" />
+ </el-form-item>
+ <el-form-item label="鍦ㄧ嚎鐘舵��" prop="onlineStatus">
+ <el-select v-model="searchForm.onlineStatus" clearable placeholder="鍏ㄩ儴" style="min-width: 120px">
+ <el-option label="鍦ㄧ嚎" value="鍦ㄧ嚎" />
+ <el-option label="绂荤嚎" value="绂荤嚎" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍏抽敭瀛�" prop="keyword">
+ <el-input v-model="searchForm.keyword" placeholder="MAC/澶囨敞" clearable @keypress.enter.native="search" />
+ </el-form-item>
+ <section>
+ <el-button type="primary" icon="el-icon-search" @click="search">鏌ヨ</el-button>
+ <el-button icon="el-icon-refresh" @click="reset">閲嶇疆</el-button>
+ </section>
+ </el-form>
+ <template v-slot:table-wrap>
+ <ul class="toolbar">
+ <li>
+ <el-button
+ type="primary"
+ :loading="isSyncing"
+ v-permissions="['business:ywconditionergateway:sync']"
+ @click="handleSync"
+ >鍚屾缃戝叧</el-button>
+ </li>
+ </ul>
+ <el-table v-loading="isWorking.search" :data="tableData.list" stripe>
+ <el-table-column prop="wgMac" label="缃戝叧MAC" min-width="150" align="center" show-overflow-tooltip />
+ <el-table-column prop="wgBz" label="澶囨敞" min-width="140" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ row.wgBz || '-' }}</template>
+ </el-table-column>
+ <el-table-column label="鍦ㄧ嚎鐘舵��" min-width="100" align="center">
+ <template slot-scope="{ row }">
+ <span :class="row.onlineStatus === '鍦ㄧ嚎' ? 'green' : 'red'">{{ row.onlineStatus || '-' }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="lastSyncDate" label="鏈�鍚庡悓姝�" min-width="160" align="center" />
+ <el-table-column label="鎿嶄綔" min-width="120" align="center" fixed="right">
+ <template slot-scope="{ row }">
+ <el-button type="text" @click="openLog(row)">涓婄嚎/绂荤嚎璁板綍</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination
+ @size-change="handleSizeChange"
+ @current-change="handlePageChange"
+ :pagination="tableData.pagination"
+ />
+ </template>
+ <YwConditionerGatewayLogWindow ref="logWindow" />
+ </TableLayout>
+</template>
+
+<script>
+import BaseTable from '@/components/base/BaseTable'
+import TableLayout from '@/layouts/TableLayout'
+import Pagination from '@/components/common/Pagination'
+import * as gatewayApi from '@/api/business/ywconditionergateway'
+import YwConditionerGatewayLogWindow from './components/YwConditionerGatewayLogWindow'
+
+export default {
+ name: 'YwConditionerGateway',
+ extends: BaseTable,
+ components: { TableLayout, Pagination, YwConditionerGatewayLogWindow },
+ data () {
+ return {
+ searchForm: {
+ wgMac: '',
+ onlineStatus: '',
+ keyword: ''
+ },
+ isSyncing: false
+ }
+ },
+ created () {
+ this.api = gatewayApi
+ this.module = '绌鸿皟缃戝叧'
+ this.configData['field.id'] = 'id'
+ this.configData['field.main'] = 'wgMac'
+ this.search()
+ },
+ methods: {
+ buildSearchModel () {
+ const model = {}
+ if (this.searchForm.wgMac) model.wgMac = this.searchForm.wgMac
+ if (this.searchForm.onlineStatus) model.onlineStatus = this.searchForm.onlineStatus
+ if (this.searchForm.keyword) model.keyword = this.searchForm.keyword
+ return model
+ },
+ handlePageChange (pageIndex) {
+ this.tableData.pagination.pageIndex = pageIndex || this.tableData.pagination.pageIndex
+ this.isWorking.search = true
+ gatewayApi.fetchList({
+ page: this.tableData.pagination.pageIndex,
+ capacity: this.tableData.pagination.pageSize,
+ model: this.buildSearchModel(),
+ sorts: this.tableData.sorts
+ })
+ .then(data => {
+ this.tableData.list = data.records || []
+ this.tableData.pagination.total = data.total || 0
+ })
+ .catch(() => {})
+ .finally(() => { this.isWorking.search = false })
+ },
+ reset () {
+ this.searchForm = { wgMac: '', onlineStatus: '', keyword: '' }
+ this.search()
+ },
+ handleSync () {
+ this.$dialog.actionConfirm('纭浠庢櫤绮剧伒骞冲彴鍚屾鍏ㄩ儴缃戝叧鍚楋紵', '鍚屾缃戝叧')
+ .then(() => {
+ this.isSyncing = true
+ gatewayApi.syncAll()
+ .then(res => {
+ this.$tip.apiSuccess(res || '鍚屾鎴愬姛')
+ this.search()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isSyncing = false })
+ })
+ .catch(() => {})
+ },
+ openLog (row) {
+ this.$refs.logWindow.open(row)
+ }
+ }
+}
+</script>
+
+<style scoped>
+.green { color: #67c23a; }
+.red { color: #f56c6c; }
+</style>
diff --git a/admin/src/views/business/ywconditionermeter.vue b/admin/src/views/business/ywconditionermeter.vue
new file mode 100644
index 0000000..392aa24
--- /dev/null
+++ b/admin/src/views/business/ywconditionermeter.vue
@@ -0,0 +1,169 @@
+<template>
+ <TableLayout :permissions="['business:ywconditionermeter:query']">
+ <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
+ <el-form-item label="璁惧淇℃伅" prop="keyword">
+ <el-input v-model="searchForm.keyword" placeholder="鐢佃〃鍚嶇О/鍦板潃" clearable @keypress.enter.native="search" />
+ </el-form-item>
+ <el-form-item label="缃戝叧MAC" prop="wgMacFilter">
+ <el-input v-model="searchForm.wgMacFilter" placeholder="缃戝叧MAC" clearable @keypress.enter.native="search" />
+ </el-form-item>
+ <section>
+ <el-button type="primary" icon="el-icon-search" @click="search">鏌ヨ</el-button>
+ <el-button icon="el-icon-refresh" @click="reset">閲嶇疆</el-button>
+ </section>
+ </el-form>
+ <template v-slot:table-wrap>
+ <ul class="toolbar">
+ <li>
+ <el-button
+ type="primary"
+ :loading="isSyncing"
+ v-permissions="['business:ywconditionermeter:sync']"
+ @click="handleSync"
+ >鍚屾鐢佃〃</el-button>
+ </li>
+ </ul>
+ <el-table v-loading="isWorking.search" :data="tableData.list" stripe>
+ <el-table-column prop="dbName" label="鐢佃〃鍚嶇О" min-width="130" align="center" show-overflow-tooltip />
+ <el-table-column prop="dbAdr" label="琛ㄥ湴鍧�" min-width="120" align="center" show-overflow-tooltip />
+ <el-table-column prop="wgMac" label="缃戝叧MAC" min-width="140" align="center" show-overflow-tooltip />
+ <el-table-column prop="dbBb" label="鍙樻瘮" min-width="80" align="center" />
+ <el-table-column prop="xyName" label="鍗忚" min-width="100" align="center" show-overflow-tooltip />
+ <el-table-column prop="standbyShare" label="寰呮満鍒嗘憡" min-width="100" align="center" show-overflow-tooltip />
+ <el-table-column prop="outdoorLoop" label="澶栨満鍥炶矾" min-width="90" align="center" />
+ <el-table-column label="鍔熺巼(kW)" min-width="100" align="center">
+ <template slot-scope="{ row }">{{ formatNum(row.powerKw) }}</template>
+ </el-table-column>
+ <el-table-column label="绱鐢甸噺" min-width="110" align="center">
+ <template slot-scope="{ row }">{{ formatNum(row.totalDl) }}</template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" min-width="160" align="center" fixed="right">
+ <template slot-scope="{ row }">
+ <el-button
+ type="text"
+ :loading="operatingId === row.id && operateType === 'energy'"
+ v-permissions="['business:ywconditionermeter:operate']"
+ @click="queryEnergy(row)"
+ >鏌ョ數閲�</el-button>
+ <el-button
+ type="text"
+ :loading="operatingId === row.id && operateType === 'power'"
+ v-permissions="['business:ywconditionermeter:operate']"
+ @click="queryPower(row)"
+ >鏌ュ姛鐜�</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination
+ @size-change="handleSizeChange"
+ @current-change="handlePageChange"
+ :pagination="tableData.pagination"
+ />
+ </template>
+ </TableLayout>
+</template>
+
+<script>
+import BaseTable from '@/components/base/BaseTable'
+import TableLayout from '@/layouts/TableLayout'
+import Pagination from '@/components/common/Pagination'
+import * as meterApi from '@/api/business/ywconditionermeter'
+
+export default {
+ name: 'YwConditionerMeter',
+ extends: BaseTable,
+ components: { TableLayout, Pagination },
+ data () {
+ return {
+ searchForm: {
+ keyword: '',
+ wgMacFilter: ''
+ },
+ isSyncing: false,
+ operatingId: null,
+ operateType: ''
+ }
+ },
+ created () {
+ this.api = meterApi
+ this.module = '绌鸿皟鐢佃〃'
+ this.configData['field.id'] = 'id'
+ this.configData['field.main'] = 'dbName'
+ this.search()
+ },
+ methods: {
+ buildSearchModel () {
+ const model = {}
+ if (this.searchForm.keyword) model.keyword = this.searchForm.keyword
+ if (this.searchForm.wgMacFilter) model.wgMacFilter = this.searchForm.wgMacFilter
+ return model
+ },
+ handlePageChange (pageIndex) {
+ this.tableData.pagination.pageIndex = pageIndex || this.tableData.pagination.pageIndex
+ this.isWorking.search = true
+ meterApi.fetchList({
+ page: this.tableData.pagination.pageIndex,
+ capacity: this.tableData.pagination.pageSize,
+ model: this.buildSearchModel(),
+ sorts: this.tableData.sorts
+ })
+ .then(data => {
+ this.tableData.list = data.records || []
+ this.tableData.pagination.total = data.total || 0
+ })
+ .catch(() => {})
+ .finally(() => { this.isWorking.search = false })
+ },
+ reset () {
+ this.searchForm = { keyword: '', wgMacFilter: '' }
+ this.search()
+ },
+ formatNum (val) {
+ if (val === null || val === undefined || val === '') return '-'
+ return val
+ },
+ handleSync () {
+ this.$dialog.actionConfirm('纭浠庢櫤绮剧伒骞冲彴鍚屾鍏ㄩ儴鐢佃〃鍚楋紵', '鍚屾鐢佃〃')
+ .then(() => {
+ this.isSyncing = true
+ meterApi.syncAll()
+ .then(res => {
+ this.$tip.apiSuccess(res || '鍚屾鎴愬姛')
+ this.search()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isSyncing = false })
+ })
+ .catch(() => {})
+ },
+ queryEnergy (row) {
+ this.operatingId = row.id
+ this.operateType = 'energy'
+ meterApi.queryEnergy(row.id)
+ .then(res => {
+ this.$tip.apiSuccess(res || '鏌ョ數閲忚姹傚凡鎻愪氦')
+ this.handlePageChange()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => {
+ this.operatingId = null
+ this.operateType = ''
+ })
+ },
+ queryPower (row) {
+ this.operatingId = row.id
+ this.operateType = 'power'
+ meterApi.queryPower(row.id)
+ .then(res => {
+ this.$tip.apiSuccess(res || '鏌ュ姛鐜囪姹傚凡鎻愪氦')
+ this.handlePageChange()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => {
+ this.operatingId = null
+ this.operateType = ''
+ })
+ }
+ }
+}
+</script>
diff --git a/admin/src/views/business/ywconditionerreport.vue b/admin/src/views/business/ywconditionerreport.vue
new file mode 100644
index 0000000..d01c8dc
--- /dev/null
+++ b/admin/src/views/business/ywconditionerreport.vue
@@ -0,0 +1,632 @@
+<template>
+
+ <TableLayout :permissions="['business:ywconditionerreport:query']">
+
+ <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
+
+ <el-form-item label="鎶ヨ〃绫诲瀷" prop="reportType">
+ <el-select v-model="searchForm.reportType" style="min-width: 140px" @change="onReportTypeChange">
+ <el-option label="鏃ユ姤琛�" value="day" />
+ <el-option label="鏈堟姤琛�" value="month" />
+ </el-select>
+ </el-form-item>
+ <el-form-item v-if="searchForm.reportType === 'month'" label="鏈堜唤" prop="month">
+
+ <el-date-picker
+
+ v-model="searchForm.month"
+
+ type="month"
+
+ value-format="yyyy-MM"
+
+ placeholder="閫夋嫨鏈堜唤"
+
+ style="width: 160px"
+
+ />
+
+ </el-form-item>
+
+ <el-form-item v-if="searchForm.reportType === 'day'" label="鏃堕棿娈�" prop="dateRange">
+
+ <el-date-picker
+
+ v-model="searchForm.dateRange"
+
+ type="daterange"
+
+ value-format="yyyy-MM-dd"
+
+ range-separator="鑷�"
+
+ start-placeholder="寮�濮嬫棩鏈�"
+
+ end-placeholder="缁撴潫鏃ユ湡"
+
+ :picker-options="datePickerOptions"
+
+ style="width: 280px"
+
+ @change="onDateRangeChange"
+
+ />
+
+ </el-form-item>
+
+ <el-form-item label="鍟嗘埛" prop="gsId">
+
+ <el-select v-model="searchForm.gsId" clearable filterable placeholder="鍏ㄩ儴" style="min-width: 200px">
+
+ <el-option v-for="item in merchantOptions" :key="item.gsId" :label="item.gsName" :value="item.gsId" />
+
+ </el-select>
+
+ </el-form-item>
+
+ <el-form-item label="璁惧淇℃伅" prop="devKeyword">
+
+ <el-input v-model="searchForm.devKeyword" placeholder="璁惧鍚嶇О/ID" clearable @keypress.enter.native="search" />
+
+ </el-form-item>
+
+ <section>
+
+ <el-button type="primary" icon="el-icon-search" @click="search">鏌ヨ</el-button>
+
+ <el-button icon="el-icon-refresh" @click="reset">閲嶇疆</el-button>
+
+ </section>
+
+ </el-form>
+
+ <template v-slot:table-wrap>
+
+ <ul class="toolbar">
+
+ <li>
+
+ <el-button
+
+ type="primary"
+
+ :loading="isSyncing"
+
+ v-permissions="['business:ywconditionerreport:sync']"
+
+ @click="handleSyncUsage"
+
+ >鍚屾鐢ㄩ噺</el-button>
+
+ </li>
+
+ <li>
+
+ <el-button
+
+ @click="handleExport"
+
+ :loading="isWorking.export"
+
+ v-permissions="['business:ywconditionerreport:exportExcel']"
+
+ >瀵煎嚭</el-button>
+
+ </li>
+
+ </ul>
+
+ <div class="report-scroll-wrap">
+
+ <el-table v-loading="isWorking.search" :data="tableData.list" stripe border size="small">
+
+ <el-table-column label="璁惧鍚嶇О" min-width="160" fixed align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ formatDevName(row) }}</template>
+ </el-table-column>
+
+ <el-table-column prop="devId" label="璁惧ID" width="90" fixed align="center" />
+
+ <el-table-column label="鎬绘椂闀�(h)" min-width="100" align="center">
+ <template slot-scope="{ row }">{{ formatNum(row.totalTime) }}</template>
+ </el-table-column>
+ <el-table-column label="鎬荤數閲�(kWh)" min-width="110" align="center">
+ <template slot-scope="{ row }">{{ formatNum(row.totalDl) }}</template>
+ </el-table-column>
+ <el-table-column label="鎬荤數璐�(鍏�)" min-width="100" align="center">
+ <template slot-scope="{ row }">{{ formatNum(row.totalDf) }}</template>
+ </el-table-column>
+ <el-table-column
+ v-for="col in dateColumns"
+ :key="col"
+ :label="col"
+ align="center"
+ >
+ <el-table-column label="鐢甸噺(kWh)" min-width="88" align="center">
+ <template slot-scope="{ row }">{{ dailyVal(row, col, 'dl') }}</template>
+ </el-table-column>
+ <el-table-column label="鏃堕暱(h)" min-width="80" align="center">
+ <template slot-scope="{ row }">{{ dailyVal(row, col, 'time') }}</template>
+ </el-table-column>
+ <el-table-column label="鐢佃垂(鍏�)" min-width="88" align="center">
+ <template slot-scope="{ row }">{{ dailyVal(row, col, 'df') }}</template>
+ </el-table-column>
+ </el-table-column>
+
+ </el-table>
+
+ </div>
+
+ <pagination
+
+ @size-change="handleSizeChange"
+
+ @current-change="handlePageChange"
+
+ :pagination="tableData.pagination"
+
+ />
+
+ </template>
+
+ </TableLayout>
+
+</template>
+
+
+
+<script>
+
+import BaseTable from '@/components/base/BaseTable'
+
+import TableLayout from '@/layouts/TableLayout'
+
+import Pagination from '@/components/common/Pagination'
+
+import * as reportApi from '@/api/business/ywconditionerreport'
+
+import dayjs from 'dayjs'
+
+
+
+function lastMonthStr () {
+
+ return dayjs().subtract(1, 'month').format('YYYY-MM')
+
+}
+
+
+
+function defaultDayRange () {
+
+ const end = dayjs().subtract(1, 'day')
+
+ const start = end.subtract(6, 'day')
+
+ return [start.format('YYYY-MM-DD'), end.format('YYYY-MM-DD')]
+
+}
+
+
+
+export default {
+
+ name: 'YwConditionerReport',
+
+ extends: BaseTable,
+
+ components: { TableLayout, Pagination },
+
+ data () {
+
+ return {
+
+ searchForm: {
+
+ reportType: 'day',
+
+ month: lastMonthStr(),
+
+ dateRange: defaultDayRange(),
+
+ gsId: null,
+
+ devKeyword: ''
+
+ },
+
+ dateColumns: [],
+
+ merchantOptions: [],
+
+ isSyncing: false,
+
+ rangePickAnchor: null,
+
+ datePickerOptions: {
+
+ onPick: ({ minDate, maxDate }) => {
+
+ this.rangePickAnchor = maxDate ? null : minDate
+
+ },
+
+ disabledDate: (time) => {
+
+ if (!this.rangePickAnchor) return false
+
+ const anchor = dayjs(this.rangePickAnchor)
+
+ const t = dayjs(time)
+
+ return t.isBefore(anchor.subtract(30, 'day'), 'day') || t.isAfter(anchor.add(30, 'day'), 'day')
+
+ }
+
+ }
+
+ }
+
+ },
+
+ created () {
+
+ this.api = reportApi
+
+ this.module = '绌鸿皟鐢ㄩ噺鎶ヨ〃'
+
+ this.configData['field.id'] = 'devId'
+
+ this.configData['field.main'] = 'devName'
+
+ this.loadMerchants()
+
+ this.search()
+
+ },
+
+ methods: {
+
+ loadMerchants () {
+
+ reportApi.merchantOptions()
+
+ .then(list => { this.merchantOptions = list || [] })
+
+ .catch(() => {})
+
+ },
+
+ onReportTypeChange (val) {
+ if (val === 'month') {
+ if (!this.searchForm.month) {
+ this.searchForm.month = lastMonthStr()
+ }
+ } else if (val === 'day') {
+
+ if (!this.searchForm.dateRange || this.searchForm.dateRange.length !== 2) {
+
+ this.searchForm.dateRange = defaultDayRange()
+
+ }
+
+ }
+
+ },
+
+ onDateRangeChange (val) {
+
+ this.rangePickAnchor = null
+
+ if (!val || val.length !== 2) return
+
+ const days = dayjs(val[1]).diff(dayjs(val[0]), 'day') + 1
+
+ if (days > 31) {
+
+ this.$tip.error('鏃堕棿娈垫渶澶氫笉瓒呰繃31澶�')
+
+ this.searchForm.dateRange = [
+
+ dayjs(val[1]).subtract(30, 'day').format('YYYY-MM-DD'),
+
+ val[1]
+
+ ]
+
+ }
+
+ },
+
+ validateDayRange () {
+
+ if (this.searchForm.reportType !== 'day') return null
+
+ const range = this.searchForm.dateRange
+
+ if (!range || range.length !== 2) {
+
+ return '璇烽�夋嫨鏃堕棿娈�'
+
+ }
+
+ const days = dayjs(range[1]).diff(dayjs(range[0]), 'day') + 1
+
+ if (days > 31) {
+
+ return '鏃堕棿娈垫渶澶氫笉瓒呰繃31澶�'
+
+ }
+
+ return null
+
+ },
+
+ search () {
+
+ const err = this.validateDayRange()
+
+ if (err) {
+
+ this.$tip.error(err)
+
+ return
+
+ }
+
+ this.handlePageChange(1)
+
+ },
+
+ buildQuery () {
+
+ const q = {
+
+ reportType: this.searchForm.reportType,
+
+ gsId: this.searchForm.gsId || undefined,
+
+ devKeyword: this.searchForm.devKeyword || undefined,
+
+ page: this.tableData.pagination.pageIndex,
+
+ capacity: this.tableData.pagination.pageSize
+
+ }
+
+ if (this.searchForm.reportType === 'day') {
+
+ const range = this.searchForm.dateRange || []
+
+ q.startTime = range[0]
+
+ q.endTime = range[1]
+
+ } else {
+
+ q.month = this.searchForm.month || lastMonthStr()
+
+ }
+
+ return q
+
+ },
+
+ handlePageChange (pageIndex) {
+
+ const err = this.validateDayRange()
+
+ if (err) {
+
+ this.$tip.error(err)
+
+ return
+
+ }
+
+ this.tableData.pagination.pageIndex = pageIndex || this.tableData.pagination.pageIndex
+
+ this.isWorking.search = true
+
+ reportApi.fetchPage(this.buildQuery())
+
+ .then(data => {
+
+ this.tableData.list = data.records || []
+
+ this.tableData.pagination.total = data.total || 0
+
+ this.dateColumns = data.dateColumns || []
+
+ if (data.page) this.tableData.pagination.pageIndex = data.page
+
+ if (data.capacity) this.tableData.pagination.pageSize = data.capacity
+
+ })
+
+ .catch(() => {})
+
+ .finally(() => { this.isWorking.search = false })
+
+ },
+
+ reset () {
+
+ this.searchForm = {
+
+ reportType: 'day',
+
+ month: lastMonthStr(),
+
+ dateRange: defaultDayRange(),
+
+ gsId: null,
+
+ devKeyword: ''
+
+ }
+
+ this.search()
+
+ },
+
+ columnToDateKey (col) {
+
+ if (this.searchForm.reportType === 'day') {
+
+ return col
+
+ }
+
+ const month = this.searchForm.month || lastMonthStr()
+
+ const parts = String(col).split('.')
+
+ if (parts.length !== 2) return null
+
+ const m = parseInt(parts[0], 10)
+
+ const d = parseInt(parts[1], 10)
+
+ if (isNaN(m) || isNaN(d)) return null
+
+ const year = parseInt(month.split('-')[0], 10)
+
+ const pad = n => (n < 10 ? '0' + n : '' + n)
+
+ return `${year}-${pad(m)}-${pad(d)}`
+
+ },
+
+ dailyVal (row, col, field) {
+
+ if (!row.daily) return '-'
+
+ const key = this.columnToDateKey(col)
+
+ if (!key || !row.daily[key]) return '-'
+
+ const v = row.daily[key][field]
+
+ return v != null && v !== '' ? v : '-'
+
+ },
+
+ formatNum (val) {
+
+ if (val === null || val === undefined || val === '') return '-'
+
+ return val
+
+ },
+
+ formatDevName (row) {
+ const parts = [row.floorName, row.roomName, row.devName]
+ .filter(p => p != null && String(p).trim() !== '')
+ return parts.length ? parts.join('/') : '-'
+ },
+
+ handleSyncUsage () {
+
+ const err = this.validateDayRange()
+
+ if (err) {
+
+ this.$tip.error(err)
+
+ return
+
+ }
+
+ const q = this.buildQuery()
+
+ const msg = q.reportType === 'day'
+
+ ? `纭鍚屾 ${q.startTime} 鑷� ${q.endTime} 鐢ㄩ噺鏁版嵁鍚楋紵`
+
+ : `纭鍚屾 ${q.month} 鐢ㄩ噺鏁版嵁鍚楋紵`
+
+ this.$dialog.actionConfirm(msg, '鍚屾鐢ㄩ噺')
+
+ .then(() => {
+
+ this.isSyncing = true
+
+ reportApi.syncUsage(q)
+
+ .then(res => {
+
+ this.$tip.apiSuccess(res || '鍚屾鎴愬姛')
+
+ this.search()
+
+ })
+
+ .catch(e => this.$tip.apiFailed(e))
+
+ .finally(() => { this.isSyncing = false })
+
+ })
+
+ .catch(() => {})
+
+ },
+
+ handleExport () {
+
+ const err = this.validateDayRange()
+
+ if (err) {
+
+ this.$tip.error(err)
+
+ return
+
+ }
+
+ this.$dialog.exportConfirm('纭瀵煎嚭鍚楋紵')
+
+ .then(() => {
+
+ this.isWorking.export = true
+
+ reportApi.exportExcel({
+
+ ...this.buildQuery(),
+
+ page: 1,
+
+ capacity: 1000000
+
+ })
+
+ .then(response => {
+
+ this.download(response)
+
+ })
+
+ .catch(e => this.$tip.apiFailed(e))
+
+ .finally(() => { this.isWorking.export = false })
+
+ })
+
+ .catch(() => {})
+
+ }
+
+ }
+
+}
+
+</script>
+
+
+
+<style scoped>
+.report-scroll-wrap {
+ width: 100%;
+ overflow-x: auto;
+}
+</style>
+
diff --git a/admin/src/views/system/menu.vue b/admin/src/views/system/menu.vue
index 27b05ad..dd130a9 100644
--- a/admin/src/views/system/menu.vue
+++ b/admin/src/views/system/menu.vue
@@ -15,7 +15,6 @@
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
row-key="id"
stripe
- default-expand-all
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" fixed="left"></el-table-column>
diff --git a/server/db/ELECTRICAL_INTEGRATION.md b/server/db/ELECTRICAL_INTEGRATION.md
index d206bdf..7c85612 100644
--- a/server/db/ELECTRICAL_INTEGRATION.md
+++ b/server/db/ELECTRICAL_INTEGRATION.md
@@ -163,10 +163,16 @@
---
-## 浜斻�佸畾鏃朵换鍔★紙鍙�� HTTP 瑙﹀彂锛�
+## 浜斻�佸畾鏃朵换鍔★紙system_timer + quartz_job锛�
-| 浠诲姟 | Cron | HTTP锛坅dmin_timer锛� |
-|------|------|---------------------|
-| 閲囬泦鍣ㄧ姸鎬� | `0 */5 * * * ?` | GET `/timer/yw/getElectricalStatus` |
-| 鎵归噺鎶勮〃 | `30 0 * * * ?` | GET `/timer/yw/syncElectricalMeterData` |
-| 鏃ュ織娓呯悊 | `0 30 2 * * ?` | GET `/timer/yw/cleanElectricalLog` |
+鐢� `system_timer` 璇诲彇 `quartz_job` 琛紝閫氳繃 `visitServiceJob` Bean 鍙嶅皠璋冪敤 `VisitServiceFegin` 瀵瑰簲鏂规硶锛孒TTP 钀藉埌 `admin_timer` 鐨� `YwTimerController`銆�
+
+閰嶇疆鑴氭湰锛歚server/db/quartz_job.yw_timer.sql`锛堟墽琛屽悗闇�鍦ㄤ綔涓氳皟搴﹀钩鍙伴噸鍚�/鎭㈠浠诲姟浠ュ姞杞� Cron锛夈��
+
+| 浠诲姟 | Cron | module锛團eign 鏂规硶锛� | HTTP锛坅dmin_timer锛� |
+|------|------|----------------------|---------------------|
+| 閲囬泦鍣ㄧ姸鎬� | `0 */5 * * * ?` | `getElectricalStatus` | GET `/timer/yw/getElectricalStatus` |
+| 鎵归噺鎶勮〃 | `30 0 * * * ?` | `syncElectricalMeterData` | GET `/timer/yw/syncElectricalMeterData` |
+| 鏃ュ織娓呯悊 | `0 30 2 * * ?` | `cleanElectricalLog` | GET `/timer/yw/cleanElectricalLog` |
+| 绌鸿皟缃戝叧鐘舵�� | `0 */5 * * * ?` | `syncConditionerGatewayStatus` | GET `/timer/yw/syncConditionerGatewayStatus` |
+| 绌鸿皟鍐呮満鐘舵�� | `0 */10 * * * ?` | `syncConditionerIndoorUnits` | GET `/timer/yw/syncConditionerIndoorUnits` |
diff --git a/server/db/business.yw_conditioner.admin_role_grant.sql b/server/db/business.yw_conditioner.admin_role_grant.sql
new file mode 100644
index 0000000..0768656
--- /dev/null
+++ b/server/db/business.yw_conditioner.admin_role_grant.sql
@@ -0,0 +1,60 @@
+-- 绌鸿皟澶氳仈鏈猴細涓鸿秴绾х鐞嗗憳瑙掕壊琛ュ叏鏉冮檺锛堝彲閲嶅鎵ц锛�
+-- 鎵ц鍚庤閲嶆柊鐧诲綍浠ュ埛鏂� Redis 涓殑鏉冮檺缂撳瓨
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditioner:sync', '鍚屾绌鸿皟鍐呮満', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditioner:sync' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditioner:operate', '绌鸿皟鍐呮満鎺у埗', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditioner:operate' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_ROLE_PERMISSION` (`ROLE_ID`, `PERMISSION_ID`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`)
+SELECT r.`ID`, p.`ID`, CURRENT_TIMESTAMP, NULL, 1, NULL, 0
+FROM `SYSTEM_ROLE` r
+INNER JOIN `SYSTEM_PERMISSION` p ON p.`CODE` IN (
+ 'business:ywconditioner:query',
+ 'business:ywconditioner:create',
+ 'business:ywconditioner:update',
+ 'business:ywconditioner:delete',
+ 'business:ywconditioner:exportExcel',
+ 'business:ywconditioner:sync',
+ 'business:ywconditioner:operate',
+ 'business:ywconditionergateway:query',
+ 'business:ywconditionergateway:sync',
+ 'business:ywconditionermeter:query',
+ 'business:ywconditionermeter:sync',
+ 'business:ywconditionermeter:operate',
+ 'business:ywconditionerbilling:query',
+ 'business:ywconditionerbilling:sync',
+ 'business:ywconditioneractions:query',
+ 'business:ywconditioneractions:exportExcel',
+ 'business:ywconditionerreport:query',
+ 'business:ywconditionerreport:sync',
+ 'business:ywconditionerreport:exportExcel'
+) AND p.`DELETED` = 0
+WHERE r.`DELETED` = 0 AND (r.`CODE` = 'admin' OR r.`NAME` IN ('瓒呯骇绠$悊鍛�', '绠$悊鍛�'))
+ AND NOT EXISTS (
+ SELECT 1 FROM `SYSTEM_ROLE_PERMISSION` rp
+ WHERE rp.`ROLE_ID` = r.`ID` AND rp.`PERMISSION_ID` = p.`ID` AND rp.`DELETED` = 0
+ );
+
+INSERT INTO `SYSTEM_ROLE_MENU` (`ROLE_ID`, `MENU_ID`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`)
+SELECT r.`ID`, menu.`ID`, CURRENT_TIMESTAMP, NULL, 1, NULL, 0
+FROM `SYSTEM_ROLE` r
+INNER JOIN `SYSTEM_MENU` menu ON menu.`DELETED` = 0 AND (
+ (menu.`NAME` = '绌鸿皟澶氳仈鏈�' AND (menu.`PATH` IS NULL OR menu.`PATH` = ''))
+ OR menu.`PATH` IN (
+ '/business/ywconditioner',
+ '/business/ywconditionergateway',
+ '/business/ywconditionermeter',
+ '/business/ywconditionerbilling',
+ '/business/ywconditioneractions',
+ '/business/ywconditionerreport'
+ )
+)
+WHERE r.`DELETED` = 0 AND (r.`CODE` = 'admin' OR r.`NAME` IN ('瓒呯骇绠$悊鍛�', '绠$悊鍛�'))
+ AND NOT EXISTS (
+ SELECT 1 FROM `SYSTEM_ROLE_MENU` rm
+ WHERE rm.`ROLE_ID` = r.`ID` AND rm.`MENU_ID` = menu.`ID` AND rm.`DELETED` = 0
+ );
diff --git a/server/db/business.yw_conditioner.menu.root.sql b/server/db/business.yw_conditioner.menu.root.sql
new file mode 100644
index 0000000..3b240d9
--- /dev/null
+++ b/server/db/business.yw_conditioner.menu.root.sql
@@ -0,0 +1,68 @@
+-- 绌鸿皟澶氳仈鏈猴細涓�绾ц彍鍗� + 6 瀛愯彍鍗� + 瓒呯骇绠$悊鍛樻巿鏉�
+
+-- 涓�绾х洰褰曪紙鏃� PATH 鎴栫┖ PATH锛屼粎浣滃垎缁勶級
+INSERT INTO `SYSTEM_MENU` (`PARENT_ID`, `NAME`, `PATH`, `REMARK`, `ICON`, `DISABLED`, `SORT`, `FIXED`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`, `PARAMS`)
+SELECT 0, '绌鸿皟澶氳仈鏈�', '', '鏅虹簿鐏电┖璋冨鑱旀満绠$悊', NULL, 0,
+ IFNULL((SELECT MAX(sm.`SORT`) FROM `SYSTEM_MENU` sm WHERE sm.`PARENT_ID` = 0 AND sm.`DELETED` = 0), 0) + 1,
+ 0, CURRENT_TIMESTAMP, NULL, 1, NULL, 0, NULL
+WHERE NOT EXISTS (
+ SELECT 1 FROM `SYSTEM_MENU` x WHERE x.`DELETED` = 0 AND x.`NAME` = '绌鸿皟澶氳仈鏈�' AND (x.`PATH` IS NULL OR x.`PATH` = '')
+);
+
+-- 瀛愯彍鍗�
+INSERT INTO `SYSTEM_MENU` (`PARENT_ID`, `NAME`, `PATH`, `REMARK`, `ICON`, `DISABLED`, `SORT`, `FIXED`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`, `PARAMS`)
+SELECT p.`ID`, '绌鸿皟鍐呮満', '/business/ywconditioner', '绌鸿皟鍐呮満鍗$墖鎺у埗', NULL, 0, 1, 0, CURRENT_TIMESTAMP, NULL, 1, NULL, 0, NULL
+FROM `SYSTEM_MENU` p WHERE p.`DELETED` = 0 AND p.`NAME` = '绌鸿皟澶氳仈鏈�' AND (p.`PATH` IS NULL OR p.`PATH` = '')
+ AND NOT EXISTS (SELECT 1 FROM `SYSTEM_MENU` x WHERE x.`DELETED` = 0 AND x.`PATH` = '/business/ywconditioner')
+LIMIT 1;
+
+INSERT INTO `SYSTEM_MENU` (`PARENT_ID`, `NAME`, `PATH`, `REMARK`, `ICON`, `DISABLED`, `SORT`, `FIXED`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`, `PARAMS`)
+SELECT p.`ID`, '缃戝叧绠$悊', '/business/ywconditionergateway', '缃戝叧鍚屾涓庝笂涓嬬嚎璁板綍', NULL, 0, 2, 0, CURRENT_TIMESTAMP, NULL, 1, NULL, 0, NULL
+FROM `SYSTEM_MENU` p WHERE p.`DELETED` = 0 AND p.`NAME` = '绌鸿皟澶氳仈鏈�' AND (p.`PATH` IS NULL OR p.`PATH` = '')
+ AND NOT EXISTS (SELECT 1 FROM `SYSTEM_MENU` x WHERE x.`DELETED` = 0 AND x.`PATH` = '/business/ywconditionergateway')
+LIMIT 1;
+
+INSERT INTO `SYSTEM_MENU` (`PARENT_ID`, `NAME`, `PATH`, `REMARK`, `ICON`, `DISABLED`, `SORT`, `FIXED`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`, `PARAMS`)
+SELECT p.`ID`, '鐢佃〃绠$悊', '/business/ywconditionermeter', '鐢佃〃鍚屾涓庢煡閲�', NULL, 0, 3, 0, CURRENT_TIMESTAMP, NULL, 1, NULL, 0, NULL
+FROM `SYSTEM_MENU` p WHERE p.`DELETED` = 0 AND p.`NAME` = '绌鸿皟澶氳仈鏈�' AND (p.`PATH` IS NULL OR p.`PATH` = '')
+ AND NOT EXISTS (SELECT 1 FROM `SYSTEM_MENU` x WHERE x.`DELETED` = 0 AND x.`PATH` = '/business/ywconditionermeter')
+LIMIT 1;
+
+INSERT INTO `SYSTEM_MENU` (`PARENT_ID`, `NAME`, `PATH`, `REMARK`, `ICON`, `DISABLED`, `SORT`, `FIXED`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`, `PARAMS`)
+SELECT p.`ID`, '璁¤垂绯绘暟', '/business/ywconditionerbilling', '璁¤垂绯绘暟鍚屾', NULL, 0, 4, 0, CURRENT_TIMESTAMP, NULL, 1, NULL, 0, NULL
+FROM `SYSTEM_MENU` p WHERE p.`DELETED` = 0 AND p.`NAME` = '绌鸿皟澶氳仈鏈�' AND (p.`PATH` IS NULL OR p.`PATH` = '')
+ AND NOT EXISTS (SELECT 1 FROM `SYSTEM_MENU` x WHERE x.`DELETED` = 0 AND x.`PATH` = '/business/ywconditionerbilling')
+LIMIT 1;
+
+INSERT INTO `SYSTEM_MENU` (`PARENT_ID`, `NAME`, `PATH`, `REMARK`, `ICON`, `DISABLED`, `SORT`, `FIXED`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`, `PARAMS`)
+SELECT p.`ID`, '璁惧鎺у埗璁板綍', '/business/ywconditioneractions', '绌鸿皟鎺у埗鎿嶄綔鍘嗗彶', NULL, 0, 5, 0, CURRENT_TIMESTAMP, NULL, 1, NULL, 0, NULL
+FROM `SYSTEM_MENU` p WHERE p.`DELETED` = 0 AND p.`NAME` = '绌鸿皟澶氳仈鏈�' AND (p.`PATH` IS NULL OR p.`PATH` = '')
+ AND NOT EXISTS (SELECT 1 FROM `SYSTEM_MENU` x WHERE x.`DELETED` = 0 AND x.`PATH` = '/business/ywconditioneractions')
+LIMIT 1;
+
+INSERT INTO `SYSTEM_MENU` (`PARENT_ID`, `NAME`, `PATH`, `REMARK`, `ICON`, `DISABLED`, `SORT`, `FIXED`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`, `PARAMS`)
+SELECT p.`ID`, '鐢ㄩ噺鏁版嵁鎶ヨ〃', '/business/ywconditionerreport', '璁惧鐢ㄩ噺閫忚鎶ヨ〃', NULL, 0, 6, 0, CURRENT_TIMESTAMP, NULL, 1, NULL, 0, NULL
+FROM `SYSTEM_MENU` p WHERE p.`DELETED` = 0 AND p.`NAME` = '绌鸿皟澶氳仈鏈�' AND (p.`PATH` IS NULL OR p.`PATH` = '')
+ AND NOT EXISTS (SELECT 1 FROM `SYSTEM_MENU` x WHERE x.`DELETED` = 0 AND x.`PATH` = '/business/ywconditionerreport')
+LIMIT 1;
+
+-- 瓒呯骇绠$悊鍛橈細鑿滃崟
+INSERT INTO `SYSTEM_ROLE_MENU` (`ROLE_ID`, `MENU_ID`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`)
+SELECT r.`ID`, menu.`ID`, CURRENT_TIMESTAMP, NULL, 1, NULL, 0
+FROM `SYSTEM_ROLE` r
+INNER JOIN `SYSTEM_MENU` menu ON menu.`DELETED` = 0 AND (
+ (menu.`NAME` = '绌鸿皟澶氳仈鏈�' AND (menu.`PATH` IS NULL OR menu.`PATH` = ''))
+ OR menu.`PATH` IN (
+ '/business/ywconditioner',
+ '/business/ywconditionergateway',
+ '/business/ywconditionermeter',
+ '/business/ywconditionerbilling',
+ '/business/ywconditioneractions',
+ '/business/ywconditionerreport'
+ )
+)
+WHERE r.`DELETED` = 0 AND (r.`CODE` = 'admin' OR r.`NAME` IN ('瓒呯骇绠$悊鍛�', '绠$悊鍛�'))
+ AND NOT EXISTS (
+ SELECT 1 FROM `SYSTEM_ROLE_MENU` rm
+ WHERE rm.`ROLE_ID` = r.`ID` AND rm.`MENU_ID` = menu.`ID` AND rm.`DELETED` = 0
+ );
diff --git a/server/db/business.yw_conditioner.module.permissions.sql b/server/db/business.yw_conditioner.module.permissions.sql
new file mode 100644
index 0000000..fa074f4
--- /dev/null
+++ b/server/db/business.yw_conditioner.module.permissions.sql
@@ -0,0 +1,88 @@
+-- 绌鸿皟澶氳仈鏈烘ā鍧楁潈闄愶紙闃查噸澶� INSERT锛�
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditioner:sync', '鍚屾绌鸿皟鍐呮満', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditioner:sync' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditioner:operate', '绌鸿皟鍐呮満鎺у埗', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditioner:operate' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionergateway:query', '鏌ヨ缃戝叧', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionergateway:query' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionergateway:sync', '鍚屾缃戝叧', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionergateway:sync' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionermeter:query', '鏌ヨ鐢佃〃', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionermeter:query' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionermeter:sync', '鍚屾鐢佃〃', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionermeter:sync' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionermeter:operate', '鐢佃〃鏌ラ噺鏌ュ姛鐜�', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionermeter:operate' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionerbilling:query', '鏌ヨ璁¤垂绯绘暟', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionerbilling:query' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionerbilling:sync', '鍚屾璁¤垂绯绘暟', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionerbilling:sync' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditioneractions:query', '鏌ヨ鎺у埗璁板綍', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditioneractions:query' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditioneractions:exportExcel', '瀵煎嚭鎺у埗璁板綍', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditioneractions:exportExcel' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionerreport:query', '鏌ヨ鐢ㄩ噺鎶ヨ〃', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionerreport:query' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionerreport:sync', '鍚屾鐢ㄩ噺鏁版嵁', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionerreport:sync' AND `DELETED` = 0);
+
+INSERT INTO `SYSTEM_PERMISSION`(`CODE`, `NAME`, `REMARK`, `FIXED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'business:ywconditionerreport:exportExcel', '瀵煎嚭鐢ㄩ噺鎶ヨ〃', '', 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (SELECT 1 FROM `SYSTEM_PERMISSION` WHERE `CODE` = 'business:ywconditionerreport:exportExcel' AND `DELETED` = 0);
+
+-- 瓒呯骇绠$悊鍛橈細鍏ㄩ儴鏉冮檺
+INSERT INTO `SYSTEM_ROLE_PERMISSION` (`ROLE_ID`, `PERMISSION_ID`, `CREATE_TIME`, `UPDATE_TIME`, `CREATE_USER`, `UPDATE_USER`, `DELETED`)
+SELECT r.`ID`, p.`ID`, CURRENT_TIMESTAMP, NULL, 1, NULL, 0
+FROM `SYSTEM_ROLE` r
+INNER JOIN `SYSTEM_PERMISSION` p ON p.`CODE` IN (
+ 'business:ywconditioner:query',
+ 'business:ywconditioner:create',
+ 'business:ywconditioner:update',
+ 'business:ywconditioner:delete',
+ 'business:ywconditioner:exportExcel',
+ 'business:ywconditioner:sync',
+ 'business:ywconditioner:operate',
+ 'business:ywconditionergateway:query',
+ 'business:ywconditionergateway:sync',
+ 'business:ywconditionermeter:query',
+ 'business:ywconditionermeter:sync',
+ 'business:ywconditionermeter:operate',
+ 'business:ywconditionerbilling:query',
+ 'business:ywconditionerbilling:sync',
+ 'business:ywconditioneractions:query',
+ 'business:ywconditioneractions:exportExcel',
+ 'business:ywconditionerreport:query',
+ 'business:ywconditionerreport:sync',
+ 'business:ywconditionerreport:exportExcel'
+) AND p.`DELETED` = 0
+WHERE r.`DELETED` = 0 AND (r.`CODE` = 'admin' OR r.`NAME` IN ('瓒呯骇绠$悊鍛�', '绠$悊鍛�'))
+ AND NOT EXISTS (
+ SELECT 1 FROM `SYSTEM_ROLE_PERMISSION` rp
+ WHERE rp.`ROLE_ID` = r.`ID` AND rp.`PERMISSION_ID` = p.`ID` AND rp.`DELETED` = 0
+ );
diff --git a/server/db/conditioner_param.dict.sql b/server/db/conditioner_param.dict.sql
new file mode 100644
index 0000000..94f9fe9
--- /dev/null
+++ b/server/db/conditioner_param.dict.sql
@@ -0,0 +1,32 @@
+-- 鏅虹簿鐏电┖璋冨钩鍙板弬鏁帮紙SYSTEM_DICT / SYSTEM_DICT_DATA锛�
+-- 瀛楀吀绫诲瀷锛欳ONDITIONER_PARAM
+
+INSERT INTO `SYSTEM_DICT` (`CODE`, `NAME`, `REMARK`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT 'CONDITIONER_PARAM', '鏅虹簿鐏电┖璋冨钩鍙板弬鏁�', 'base_url銆乽sername銆乸assword', 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+WHERE NOT EXISTS (
+ SELECT 1 FROM `SYSTEM_DICT` WHERE `CODE` = 'CONDITIONER_PARAM' AND `DELETED` = 0
+);
+
+INSERT INTO `SYSTEM_DICT_DATA` (`DICT_ID`, `CODE`, `LABEL`, `REMARK`, `SORT`, `DISABLED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT d.`ID`, 'http://119.45.163.5:1125/zjl/API', 'base_url', 'API 鏍瑰湴鍧�锛堜笉鍚湯灏炬枩鏉狅級', 1, 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+FROM `SYSTEM_DICT` d
+WHERE d.`CODE` = 'CONDITIONER_PARAM' AND d.`DELETED` = 0
+ AND NOT EXISTS (
+ SELECT 1 FROM `SYSTEM_DICT_DATA` x WHERE x.`DICT_ID` = d.`ID` AND x.`LABEL` = 'base_url' AND x.`DELETED` = 0
+ );
+
+INSERT INTO `SYSTEM_DICT_DATA` (`DICT_ID`, `CODE`, `LABEL`, `REMARK`, `SORT`, `DISABLED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT d.`ID`, 'admin', 'username', '鐧诲綍鐢ㄦ埛鍚�', 2, 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+FROM `SYSTEM_DICT` d
+WHERE d.`CODE` = 'CONDITIONER_PARAM' AND d.`DELETED` = 0
+ AND NOT EXISTS (
+ SELECT 1 FROM `SYSTEM_DICT_DATA` x WHERE x.`DICT_ID` = d.`ID` AND x.`LABEL` = 'username' AND x.`DELETED` = 0
+ );
+
+INSERT INTO `SYSTEM_DICT_DATA` (`DICT_ID`, `CODE`, `LABEL`, `REMARK`, `SORT`, `DISABLED`, `CREATE_USER`, `CREATE_TIME`, `UPDATE_USER`, `UPDATE_TIME`, `DELETED`)
+SELECT d.`ID`, '12345678', 'password', '鐧诲綍瀵嗙爜', 3, 0, 1, CURRENT_TIMESTAMP, NULL, NULL, 0
+FROM `SYSTEM_DICT` d
+WHERE d.`CODE` = 'CONDITIONER_PARAM' AND d.`DELETED` = 0
+ AND NOT EXISTS (
+ SELECT 1 FROM `SYSTEM_DICT_DATA` x WHERE x.`DICT_ID` = d.`ID` AND x.`LABEL` = 'password' AND x.`DELETED` = 0
+ );
diff --git a/server/db/docs/conditioner_api_index.md b/server/db/docs/conditioner_api_index.md
new file mode 100644
index 0000000..c631b1a
--- /dev/null
+++ b/server/db/docs/conditioner_api_index.md
@@ -0,0 +1,113 @@
+# 鏅虹簿鐏� API 鎺ュ彛绱㈠紩锛堟潵婧愶細鏅虹簿鐏礎PI鎺ュ彛鏂囨。26-01.pdf锛�
+
+## 鍗忚璇存槑
+
+| 绫诲瀷 | 鏂瑰紡 | 璇存槑 |
+|------|------|------|
+| 璇诲彇 | GET | Query 鍙傛暟锛歚kt_token`銆乣kt_dwid`銆乣kt_sonid` 绛� |
+| 鎺у埗/鍐欏叆 | POST | Body 涓� JSON锛宍Content-Type: application/json` |
+| 鎴愬姛鏍囪瘑 | `code=200` | 澶辫触鏃� `code=500` 绛� |
+
+Base URL 绀轰緥锛歚http://119.45.163.5:1125/zjl/API/`锛堥厤缃」 `base_url`锛�
+
+鐧诲綍鍚庡叕鍏卞弬鏁帮細`kt_token`銆乣kt_dwid`锛堝叕鍙窱D锛夈�乣kt_sonid`锛堝瓙璐﹀彿锛岄粯璁� `0`锛�
+
+## 浜屻�佸熀纭�鎺у埗
+
+| Util 鏂规硶 | Path | Method | 璇存槑 |
+|-----------|------|--------|------|
+| `login` | `/login` | POST | 鐢ㄦ埛鐧诲綍锛岃幏鍙� token |
+| `getDevList` | `/getDevList` | GET | 鍏ㄩ儴璁惧鐘舵�� |
+| `getDevOne` | `/getDevOne` | GET | 鍗曞彴璁惧鐘舵�侊紙wg_mac, wg_qid锛� |
+| `devCtr` | `/devCtr` | POST | 鍗曡澶囨帶鍒� / 缁勫悎鎺у埗 / 鐢佃〃鏌ヨ |
+| `devManyCtr` | `/devManyCtr` | POST | 澶氳澶囨帶鍒� / 閿佸畾鎺у埗 |
+
+## 涓夈�佽澶囩鐞�
+
+| Util 鏂规硶 | Path | Method | 璇存槑 |
+|-----------|------|--------|------|
+| `getWg` | `/getWg` | GET | 鑾峰彇缃戝叧鍒楄〃锛堟枃妗f湭鍐� URL锛屾寜鍛藉悕绾﹀畾锛� |
+| `addWg` | `/addWg` | POST | 娣诲姞缃戝叧 |
+| `changeWg` | `/changeWg` | POST | 淇敼缃戝叧 |
+| `delWg` | `/delWg` | POST | 鍒犻櫎缃戝叧 |
+| `wgWithArea` | `/wgWithArea` | POST | 缃戝叧鍏宠仈鍖哄煙 |
+| `getDev` | `/getDev` | GET | 鑾峰彇璁惧妗f鍒楄〃 |
+| `addDev` | `/addDev` | POST | 娣诲姞璁惧 |
+| `changeDev` | `/changeDev` | POST | 淇敼璁惧 |
+| `delDev` | `/delDev` | POST | 鍒犻櫎璁惧 |
+
+## 鍥涖�佸尯鍩熺鐞�
+
+| Util 鏂规硶 | Path | Method |
+|-----------|------|--------|
+| `getRoom` | `/getRoom` | GET |
+| `addRoom` | `/addRoom` | POST |
+| `changeRoom` | `/changeRoom` | POST |
+| `delRoom` | `/delRoom` | POST |
+| `getFloor` | `/getFloor` | GET |
+| `addFloor` | `/addFloor` | POST |
+| `changeFloor` | `/changeFloor` | POST |
+| `delFloor` | `/delFloor` | POST |
+| `getUnit` | `/getUnit` | GET |
+| `addUnit` | `/addUnit` | POST |
+| `changeUnit` | `/changeUnit` | POST |
+| `delUnit` | `/delUnit` | POST |
+| `getBuilding` | `/getBuilding` | GET |
+| `addBuilding` | `/addBuilding` | POST |
+| `changeBuilding` | `/changeBuilding` | POST |
+| `delBuilding` | `/delBuilding` | POST |
+| `getArea` | `/getArea` | GET |
+| `addArea` | `/addArea` | POST |
+| `changeArea` | `/changeArea` | POST |
+| `delArea` | `/delArea` | POST |
+
+## 浜斻�佸畾鏃剁鐞�
+
+| Util 鏂规硶 | Path | Method |
+|-----------|------|--------|
+| `getTiming` | `/getTiming` | GET |
+| `addTiming` | `/addTiming` | POST |
+| `changeTiming` | `/changeTiming` | POST |
+| `delTiming` | `/delTiming` | POST |
+| `timingWithArea` | `/timingWithArea` | POST |
+
+## 鍏�佹暟鎹煡璇�
+
+| Util 鏂规硶 | Path | Method |
+|-----------|------|--------|
+| `getLogWg` | `/getLogWg` | GET |
+| `getLogDev` | `/getLogDev` | GET |
+
+## 涓冦�佽閲忕鐞�
+
+| Util 鏂规硶 | Path | Method |
+|-----------|------|--------|
+| `getDlSjXs` | `/getDlSjXs` | GET |
+| `changeDlSjXs` | `/changeDlSjXs` | POST |
+| `getDb` | `/glDb/getDb` | GET |
+| `addDb` | `/glDb/addDb` | POST |
+| `changeDb` | `/glDb/changeDb` | POST |
+| `delDb` | `/glDb/delDb` | POST |
+| `getDbDaySum` | `/getDbDaySum` | GET |
+| `getDayDl` | `/getDayDl` | GET |
+| `getMoonDl` | `/getMoonDl` | GET |
+| `getGs` | `/getGs` | GET |
+| `addGs` | `/addGs` | POST |
+| `changeGs` | `/changeGs` | POST |
+| `delGs` | `/delGs` | POST |
+| `gsWithArea` | `/gsWithArea` | POST |
+| `addMoney` | `/addMoney` | POST |
+| `cleanMoney` | `/cleanMoney` | POST |
+| `getCzLog` | `/getCzLog` | GET |
+
+## 鍏�佽处鍙风鐞�
+
+| Util 鏂规硶 | Path | Method |
+|-----------|------|--------|
+| `getUser` | `/getUser` | GET |
+| `addUser` | `/addUser` | POST |
+| `changeUser` | `/changeUser` | POST |
+| `changeUserPwd` | `/changeUserPwd` | POST |
+| `delUser` | `/delUser` | POST |
+
+> 鏍囨敞銆屾寜鍛藉悕绾﹀畾銆嶇殑鎺ュ彛鍦� PDF 涓湭缁欏嚭瀹屾暣 URL锛屽疄鐜颁笌鍚岀被鎺ュ彛淇濇寔涓�鑷达紱鑻ヨ仈璋� 404 璇蜂互骞冲彴瀹為檯璺緞涓哄噯銆�
diff --git a/server/db/docs/pdf_extract.txt b/server/db/docs/pdf_extract.txt
new file mode 100644
index 0000000..20a620e
--- /dev/null
+++ b/server/db/docs/pdf_extract.txt
@@ -0,0 +1,771 @@
+API鎺ュ彛鏂囨。
+涓�銆佸崗璁鏄�
+1銆佸崗璁被鍨嬭鏄�
+璇诲彇鎺ュ彛浣跨敤GET
+鎺у埗鎺ュ彛浣跨敤POST锛屽弬鏁癲ata:JSON
+浜屻�佸熀纭�鎺у埗
+1銆佺敤鎴风櫥褰�
+http://119.45.163.5:1125/zjl/API/login?
+{"username":"admin","password":"12345678"}
+
+2銆佽幏鍙栧叏閮ㄨ澶囩姸鎬�
+http://119.45.163.5:1125/zjl/API/getDevList?
+瀛楁 瀛楁璇存槑 澶囨敞
+kt_token 鐧诲綍鍥炲弬
+kt_dwid 鍏徃ID 鐧诲綍鍥炲弬
+鍥炲弬锛�
+瀛楁 瀛楁璇存槑 澶囨敞
+dev_id 璁惧ID
+online 88鍜�66鍦ㄧ嚎锛屽叾浠栫绾� 璁惧鐘舵��
+xh 淇″彿寮哄害
+kt_lock 0鏈攣锛�1閿佸畾 閿佸畾鐘舵��
+pwr 0鍏抽棴锛�1寮�鍚� 寮�鍏�
+mode 1鍒剁儹锛�2鍒跺喎锛�3閫侀锛�4闄ゆ箍 妯″紡
+fan 1浣庨��2涓��3楂橀��4鑷姩 椋庨�熺姸鎬�
+fan_set 1浣庨��2涓��3楂橀��4鑷姩 璁惧畾椋庨��
+sf_pwr 0鍏抽棴锛�1寮�鍚� 姘撮榾鐘舵��
+temp 鏄剧ず鐨勮闄や互10 鐜娓╁害
+temp_set 鏄剧ず鐨勮闄や互10 璁惧畾娓╁害
+stop_logo 0鍋滄満锛�1鏈仠 鍋滄満鐘舵��
+sum_runtime 鎬昏繍琛屾椂闀�
+hig_runtime 楂橀�熻繍琛屾椂闀�
+mid_runtime 涓�熻繍琛屾椂闀�
+low_runtime 浣庨�熻繍琛屾椂闀�
+wg_id 缃戝叧id
+wg_mac 缃戝叧mac鍦板潃
+wg_qid 璁惧鍦板潃
+dev_name 璁惧鍚嶇О
+floor_id 妤煎眰id
+floor_name 妤煎眰鍚嶇О
+room_id 鎴块棿id
+room_name 鎴块棿鍚嶇О
+dev_type_id 璁惧绫诲瀷id
+dev_type_name 璁惧绫诲瀷鍚嶇О
+uptime 涓婃姤鏃堕棿
+2.1鑾峰彇鍗曞彴璁惧鐘舵��
+http://119.45.163.5:1125/zjl/API/getDevOne?kt_token=172588271126741&kt_dwid=0&kt_sonid=0&wg_
+mac=010000000001&wg_qid=1
+wg_mac骞冲彴娣诲姞鐨勫搴旂綉鍏砿ac鍦板潃
+wg_qid缃戝叧涓嬬殑璁惧鍦板潃锛堣澶囩鐞嗕腑鐨勮澶嘔D锛�
+3銆佸崟涓澶囨帶鍒�
+http://119.45.163.5:1125/zjl/API/devCtr?
+璇锋眰鏂瑰紡POST
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"wg_mac":"9abde73056c353e3",
+"wg_qid":"1",
+"pid":"sj",
+"set_type":"pwr",
+"set_val":0,
+"ctr_type":"set_one",
+"kt_sonid":"0"
+}
+http://119.45.163.5:1125/zjl/API/devCtr?
+瀛楁 瀛楁璇存槑 澶囨敞
+kt_token 鐧诲綍鍥炲弬
+kt_dwid 鍏徃ID 鐧诲綍鍥炲弬
+pid 姘存満sj銆佸鑱旀満dlj锛堣幏鍙栫姸鎬佹椂杩斿洖锛� 璁惧绫诲瀷
+wg_mac 缃戝叧mac鍦板潃
+wg_qid 璁惧鍦板潃
+ctr_type set_one鍗曟潯鎺у埗 鎺у埗绫诲瀷
+set_typepwr寮�鍏筹紝mode妯″紡
+fan椋庨�燂紝temp娓╁害璁剧疆绫诲瀷
+set_val 0鍏抽棴锛�1寮�鍚� 寮�鍏宠缃��
+set_val 1鍒剁儹锛�2鍒跺喎锛�3閫侀锛�4闄ゆ箍 妯″紡璁剧疆鍊�
+set_val 1浣庨锛�2涓锛�3楂橀锛�4鑷姩 椋庨�熻缃��
+set_val 娓╁害鍊�*10 娓╁害璁剧疆鍊�
+鍥炲弬锛�
+瀛楁 瀛楁璇存槑 澶囨敞
+code 200鎴愬姛锛�500鎶ラ敊
+4銆佸涓澶囧悓鏃舵帶鍒�
+http://119.45.163.5:1125/zjl/API/devManyCtr?
+{
+"ctr_type":"set_many",
+"li_data":[
+{
+"wg_mac":"9abde73056c353e3",
+"wg_qid":"91f64e6991f64e69",
+"pid":"sj",
+"set_type":"pwr",
+"set_val":1
+},
+{
+"wg_mac":"9abde73056c353e3",
+"wg_qid":"931a964e931a964e",
+"pid":"sj",
+"set_type":"pwr",
+"set_val":1
+}
+],
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0"
+}
+5銆佸鍔熻兘缁勫悎鎺у埗
+http://119.45.163.5:1125/zjl/API/devCtr?
+瀛楁 瀛楁璇存槑 澶囨敞
+ctr_type set_one鍗曡澶囷紝set_many澶氳澶� 鎺у埗绫诲瀷
+pid 姘存満sj銆佸鑱旀満dlj锛堣幏鍙栫姸鎬佹椂杩斿洖锛� 璁惧绫诲瀷
+set_type many_ctr 璁剧疆绫诲瀷
+set_val {"pwr":1,"mode":1} 鍊煎拰鍗曞姛鑳戒竴鑷�
+6銆侀攣瀹氭帶鍒�
+http://119.45.163.5:1125/zjl/API/devManyCtr?
+瀛楁 瀛楁璇存槑 澶囨敞
+pid 姘存満sj銆佸鑱旀満dlj锛堣幏鍙栫姸鎬佹椂杩斿洖锛� 璁惧绫诲瀷
+set_type many_ctr 璁剧疆绫诲瀷
+set_val 璁剧疆鍊�
+lock_pwr 0瑙i攣锛�1閿佸畾 閿佸畾璁剧疆
+pwr 0閿佸畾鍏虫満锛�1閿佸畾寮�鏈猴紝-1涓嶉攣瀹� 寮�鍏抽攣瀹�
+mode 1閿佸畾鍒剁儹锛�2鍒跺喎锛�3閫侀锛�4闄ゆ箍锛�-1涓嶉攣 妯″紡閿佸畾
+fan 1閿佸畾浣庨锛�2涓锛�3楂橀锛�4鑷姩锛�-1涓嶉攣 椋庨�熼攣瀹�
+min_temp 娓╁害鍊�*10锛�-1涓嶉攣 娓╁害涓嬮檺
+max_temp 娓╁害鍊�*10锛�-1涓嶉攣 娓╁害涓婇檺
+涓夈�佽澶囩鐞�
+1銆佺綉鍏崇鐞�
+1锛夎幏鍙栫綉鍏�
+鍥炲弬锛�
+{
+"code":200,
+"message":"ok",
+"data":[{
+"wg_id":1,
+"wg_mac":"000000000416",
+"wg_bz":"",
+"wg_status":"鍦ㄧ嚎"锛�
+"area":{}锛�//鍖哄煙
+}]
+}
+2锛夋坊鍔犵綉鍏�
+3锛変慨鏀圭綉鍏�
+4锛夊垹闄ょ綉鍏�
+
+5锛夌綉鍏冲叧鑱斿尯鍩�
+http://119.45.163.5:1125/zjl/API/wgWithArea?
+璇锋眰鏂瑰紡锛歅OST
+{
+"id":1852,
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":1,
+"room_id":6786
+}
+2銆佽澶囩鐞�
+1锛夎幏鍙栬澶�
+鍥炲弬锛�
+{
+"code":200,
+"message":"ok",
+"data":[{
+"wg_mac":"000000000416",
+"floor_name":"1",
+"room_name":"22",
+"dev_type_name":"姘存満",
+"dev_id":1,
+"floor_id":1,
+"room_id":1,
+"wg_id":1,
+"wg_qid":3,
+"dev_name":"22-03",
+"dev_bz":"",
+"dev_type_id":1
+}]
+}
+瀛楁 瀛楁璇存槑 澶囨敞
+wg_id 缃戝叧id
+wg_mac 缃戝叧mac鍦板潃
+wg_qid 璁惧鍦板潃
+dev_id 璁惧id
+dev_name 璁惧鍚嶇О
+floor_id 妤煎眰id
+floor_name 妤煎眰鍚嶇О
+room_id 鎴块棿id
+room_name 鎴块棿鍚嶇О
+dev_type_id 璁惧绫诲瀷id
+dev_type_name璁惧绫诲瀷鍚嶇О
+dev_bz 璁惧澶囨敞
+2锛夋坊鍔犺澶�
+http://119.45.163.5:1125/zjl/API/addDev?
+瀛楁 瀛楁璇存槑 澶囨敞
+wg_id 缃戝叧id
+wg_qid 璁惧鍦板潃
+floor_id 妤煎眰id
+room_id 鎴块棿id
+dev_name 璁惧鍚嶇О
+dev_type_id 璁惧绫诲瀷id
+dev_bz 澶囨敞
+3锛変慨鏀硅澶�
+4锛夊垹闄よ澶�
+
+鍥涖�佸尯鍩熺鐞�
+1銆佹埧闂寸鐞�
+1锛夎幏鍙栨埧闂�
+鍥炲弬锛�
+{
+"code":200,
+"data":[{
+"room_id":49,
+"floor_id":1,
+"room_name":"111",
+"room_bz":""
+}],
+"message":""
+}
+瀛楁 瀛楁璇存槑 澶囨敞
+floor_id 妤煎眰id
+room_id 鎴块棿id
+room_name 鎴块棿鍚嶇О
+room_bz 鎴块棿澶囨敞
+2锛夋坊鍔犳埧闂�
+3锛変慨鏀规埧闂�
+
+4锛夊垹闄ゆ埧闂�
+2銆佹ゼ灞傜鐞�
+1锛夎幏鍙栨ゼ灞�
+http://119.45.163.5:1125/zjl/API/getFloor?kt_token=172588271126741
+&kt_dwid=0&kt_unit=1&kt_sonid=0&page=1&pageSize=100
+瀛楁 瀛楁璇存槑 澶囨敞
+kt_token
+kt_dwid 鍏徃id
+kt_unit 鍗曞厓id
+kt_sonid 璐﹀彿id
+page 椤�
+pageSize 涓�椤垫潯鏁�
+鍥炲弬锛�
+{
+"code":200,
+"message":"ok",
+"data":[{
+"floor_id":1,
+"floor_name":"1",
+"floor_bz":""
+}]
+}
+瀛楁 瀛楁璇存槑 澶囨敞
+floor_id 妤煎眰id
+floor_name 妤煎眰鍚嶇О
+floor_bz 妤煎眰澶囨敞
+2锛夋坊鍔犳ゼ灞�
+http://119.45.163.5:1125/zjl/API/addFloor?
+瀛楁 瀛楁璇存槑 澶囨敞
+kt_token
+kt_dwid 鍏徃id
+kt_unit 鍗曞厓id
+kt_sonid 璐﹀彿id
+floor_name 妤煎眰鍚嶇О
+floor_bz 妤煎眰澶囨敞
+3锛変慨鏀规ゼ灞�
+http://119.45.163.5:1125/zjl/API/changeFloor?
+瀛楁 瀛楁璇存槑 澶囨敞
+kt_token
+kt_dwid 鍏徃id
+kt_unit 鍗曞厓id
+kt_sonid 璐﹀彿id
+floor_id 妤煎眰id
+floor_name 妤煎眰鍚嶇О
+floor_bz 妤煎眰澶囨敞
+4锛夊垹闄ゆゼ灞�
+3銆佸崟鍏冪鐞�
+1锛夎幏鍙栧崟鍏�
+http://119.45.163.5:1125/zjl/API/getUnit?kt_token=v14YwV2SKsq477W9sw4z5m3O667g1&kt_d
+wid=20&kt_sonid=0&page=1&pageSize=100000
+璇锋眰鏂瑰紡GET
+2锛夋坊鍔犲崟鍏�
+http://119.45.163.5:1125/zjl/API/addUnit?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"building_id":21,
+"unit_name":"1",
+"unit_bz":""
+}
+3锛変慨鏀瑰崟鍏�
+http://119.45.163.5:1125/zjl/API/changeUnit?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"unit_id":527,
+"unit_name":"12",
+"unit_bz":""
+}
+4锛夊垹闄ゅ崟鍏�
+http://119.45.163.5:1125/zjl/API/delUnit?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"unit_id":527
+}
+4銆佹ゼ鏍嬬鐞�
+1锛夎幏鍙栨ゼ鏍�
+http://119.45.163.5:1125/zjl/API/getBuilding?kt_token=v14YwV2SKsq477W9sw4z5m3O667g1&k
+t_dwid=20&kt_sonid=0&page=1&pageSize=100000
+璇锋眰鏂瑰紡GET
+2锛夋坊鍔犳ゼ鏍�
+http://119.45.163.5:1125/zjl/API/addBuilding?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"area_id":21,
+"building_name":"1F",
+"building_bz":""
+}
+3锛変慨鏀规ゼ鏍�
+http://119.45.163.5:1125/zjl/API/changeBuilding?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"building_id":335,
+"building_name":"2F",
+"building_bz":""
+}
+4锛夊垹闄ゆゼ鏍�
+http://119.45.163.5:1125/zjl/API/delBuilding?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"building_id":294
+}
+5銆佸皬鍖虹鐞�
+1锛夎幏鍙栧皬鍖�
+http://119.45.163.5:1125/zjl/API/getArea?kt_token=v14YwV2SKsq477W9sw4z5m3O667g1&kt_d
+wid=20&kt_sonid=0&page=1&pageSize=100000
+璇锋眰鏂瑰紡GET
+2锛夋坊鍔犲皬鍖�
+http://119.45.163.5:1125/zjl/API/addArea?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"area_name":"1area",
+"area_bz":""
+}
+3锛変慨鏀瑰皬鍖�
+http://119.45.163.5:1125/zjl/API/changeArea?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"area_id":298,
+"area_name":"2area",
+"area_bz":""
+}
+4锛夊垹闄ゅ皬鍖�
+http://119.45.163.5:1125/zjl/API/delArea?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"area_id":298
+}
+浜斻�佸畾鏃剁鐞�
+1銆佽幏鍙栧畾鏃�
+鍥炲弬锛�
+{
+"code":200,
+"message":"ok",
+"data":[{
+"id":9,
+"time_pwr":1,
+"time_week":"0,1,2,3,4,5,6,",
+"time_type":"lock",
+"uptime":"15:05:00",
+"li_devid":[1099],
+"d_scene":{
+"li_data":[{
+"set_type":"lock_many",
+"set_val":{
+"lock_pwr":1,
+"pwr":0,
+"mode":2,
+"fan":3,
+"min_temp":160,
+"max_temp":320
+},
+"wg_mac":"010000000001",
+"wg_qid":1
+}]
+},
+"time_bz":"瀹氭椂1"
+}]
+}
+瀛楁 瀛楁璇存槑 澶囨敞
+id 瀹氭椂id
+time_pwr 瀹氭椂寮�鍏�
+time_week 0鍛ㄤ竴锛�6鍛ㄦ棩
+time_type pwr寮�鍏筹紝lock閿佸畾锛宮any缁勫悎鎺у埗 瀹氭椂绫诲瀷
+uptime 瀹氭椂鏃堕棿
+li_devid 璁惧id闆嗗悎
+li_data 鍦烘櫙闆嗗悎
+set_type many_ctr鎺у埗锛宭ock_many閿佸畾 璁剧疆绫诲瀷
+set_val 鍜岃澶囨帶鍒朵竴鑷� 璁剧疆鍊�
+wg_mac 缃戝叧mac
+wg_qid 缃戝叧qid
+2銆佹坊鍔犲畾鏃�
+http://119.45.163.5:1125/zjl/API/addTiming?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"uptime":"18:00",
+"li_devid":[
+43868
+],
+"time_pwr":1,
+"time_type":"pwr",
+"d_scene":{
+"li_data":[
+{
+"set_type":"pwr",
+"set_val":0,
+"wg_mac":"000000001403",
+"wg_qid":"3",
+"pid":"sj"
+}
+]
+},
+"date_type":0,
+"time_date":"",
+"time_week":"0,1,2,3,4,",
+"time_bz":"瀹氭椂1",
+"sceneId":0,
+"sceneName":""
+}
+time_pwr瀹氭椂寮�鍏� 0鍏抽棴銆�1寮�鍚�
+time_type瀹氭椂绫诲瀷 寮�鍏硃wr銆佸満鏅痵cene銆侀攣瀹氭帶鍒秎ock
+缁勫悎鎺у埗many
+d_scene{li_data:[]}鎺у埗鍐呭 鍜屾壒閲忔帶鍒朵竴鑷�
+date_type鏃堕棿绫诲瀷 0鍛ㄦ湡鎺у埗銆�1鏃ユ湡鎺у埗
+time_week鍛ㄦ湡 0鍛ㄤ竴銆�6鍛ㄦ棩
+time_date瀹氭椂鏃ユ湡2026-01-07
+sceneId鍦烘櫙ID 鍜屽満鏅叧鑱旀墽琛�
+sceneName鍦烘櫙鍚嶇О 鍜屽満鏅叧鑱旀墽琛�
+3銆佷慨鏀瑰畾鏃�
+4銆佸垹闄ゅ畾鏃�
+6銆佸叧鑱斿尯鍩�
+http://119.45.163.5:1125/zjl/API/timingWithArea?
+{
+"id":593,
+"kt_dwid":"20",
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"room_id":6783
+}
+鍏�佹暟鎹煡璇�
+1銆佺綉鍏充笂绾�/绂荤嚎璁板綍
+2銆佽澶囨帶鍒惰褰�
+http://119.45.163.5:1125/zjl/API/getLogDev?kt_token=172594860456857&kt_dwid=20&kt_
+sonid=0&start_time=2025-07-21&end_time=2025-07-22&page=1&pageSize=10
+
+涓冦�佽閲忕鐞�
+1銆佽閲忕郴鏁�
+1锛夎幏鍙栬閲忕郴鏁�
+http://119.45.163.5:1125/zjl/API/getDlSjXs?kt_token=172588271126741&kt_dwid=0&kt_sonid=0&kt_uni
+t=1
+鍥炲弬锛�
+瀛楁 瀛楁璇存槑 澶囨敞
+dev_id 璁惧ID
+kt_dj 璁¤垂鐢典环
+kw_type 0鏃堕暱璁¤垂1鑳借�楄璐�2鐢佃〃璁¤垂 璁¤垂绫诲瀷
+hig_kw 楂樻。椋庨�熺郴鏁�
+mid_kw 涓。椋庨�熺郴鏁�
+low_kw 浣庢。椋庨�熺郴鏁�
+fan_arg 鐩樼绯绘暟
+fan_kw 椋庢満鑰楃數
+cold_kw 鍒跺喎鍗犳瘮 鏆傛湭鐢�
+heat_kw 鍒剁儹鍗犳瘮 鏆傛湭鐢�
+day_kw 澶栨満鏃ヨ�楃數 鏆傛湭鐢�
+2锛変慨鏀硅閲忕郴鏁�
+http://119.45.163.5:1125/zjl/API/changeDlSjXs?
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"dev_id":43868,
+"kt_sonid":"0",
+"kw_type":1,
+"fan_arg":1,
+"fan_kw":1,
+"hig_kw":1.3,
+"mid_kw":1.1,
+"low_kw":1
+}
+2銆佺數琛ㄧ鐞�
+1锛夎幏鍙栫數琛ㄥ垪琛�
+http://119.45.163.5:1125/zjl/API/glDb/getDb?kt_token=172588271126741&kt_dwid=0&kt_sonid=0&kt_
+unit=1&page=1&pageSize=100000
+绛涢�夊崟鍙扮數琛ㄥ姞&db_mac=990000011e01
+鍥炲弬锛�
+瀛楁 瀛楁璇存槑 澶囨敞
+db_id 鐢佃〃id
+wg_mac 鐢佃〃缃戝叧MAC
+wg_id 鐢佃〃缃戝叧id
+xy_name 鍗忚鍚嶇О
+xy_id 鍗忚id
+db_adr 鐢佃〃鍦板潃
+db_name 鐢佃〃鍚嶇О
+db_bb 鍙樻瘮
+db_rhd 澶栨満鏃ヨ�楃數
+li_dev [1099,1100,1102] 璁惧id鍒楄〃
+db_bz 澶囨敞
+db_data 鏈�鏂颁竴鏉℃暟鎹�
+db_uptime 鏈�鏂颁竴鏉℃暟鎹椂闂�
+2锛夋坊鍔犵數琛�
+http://119.45.163.5:1125/zjl/API/glDb/addDb?
+{
+"kt_token":"172588271126741",
+"kt_dwid":"0",
+"kt_sonid":"0",
+"kt_unit":"1",
+"wg_id":147,
+"xy_id":2,
+"db_adr":"1",
+"db_name":"1",
+"db_bb":1,
+"db_rhd":0,
+"db_bz":"1",
+"li_dev":[1,3]
+}
+3锛変慨鏀圭數琛ㄤ俊鎭�
+http://119.45.163.5:1125/zjl/API/glDb/changeDb?
+{
+"kt_token":"172588271126741",
+"kt_dwid":"0",
+"kt_sonid":"0",
+"kt_unit":"1",
+"db_id":1,
+"wg_id":147,
+"xy_id":2,
+"db_adr":"1",
+"db_name":"鐢佃〃1",
+"db_bb":30,
+"db_rhd":0,
+"db_bz":"",
+"li_dev":[
+1099,
+1100,
+1102
+]
+}
+4锛夊垹闄ょ數琛�
+http://119.45.163.5:1125/zjl/API/glDb/delDb?
+5锛夎Е鍙戠數琛ㄦ煡璇㈢數閲�
+http://119.45.163.5:1125/zjl/API/devCtr?
+璇锋眰绫诲瀷POST
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"wg_mac":"990000011e01",
+"wg_qid":"1",
+"ctr_type":"set_one",
+"set_type":"find_dn",
+"set_val":{
+"db_bb":1,
+"xy_name":"645鍗忚"
+}
+}
+6锛夎Е鍙戠數琛ㄦ煡璇㈠姛鐜�
+http://119.45.163.5:1125/zjl/API/devCtr?
+璇锋眰绫诲瀷POST
+{
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"kt_dwid":"20",
+"kt_sonid":"0",
+"wg_mac":"990000011e01",
+"wg_qid":"1",
+"ctr_type":"set_one",
+"set_type":"find_power",
+"set_val":{
+"db_bb":1,
+"xy_name":"645鍗忚"
+}
+}
+7锛夋煡璇㈢數琛ㄦ暟鎹�
+http://119.45.163.5:1125/zjl/API/getDbDaySum?kt_token=172588271126741&kt_dwid=0&kt_unit=1&kt_
+sonid=0&start_time=2024-12-03&end_time=2024-12-04
+鍥炲弬锛�
+瀛楁 瀛楁璇存槑 澶囨敞
+uptime 涓婃姤鏃堕棿
+wg_mac 鐢佃〃缃戝叧mac
+db_adr 鐢佃〃鍦板潃
+dl 鐢甸噺
+3銆佺數閲忔暟鎹�
+1锛夋棩鐢甸噺鎶ヨ〃
+http://119.45.163.5:1125/zjl/API/getDayDl?kt_token=v14YwV2SKsq477W9sw4z5m3O667g1&kt_dwid=20
+&kt_sonid=0&page=1&pageSize=1000000&start_time=2026-01-06&end_time=2026-01-07
+绛涢�夊叕鍙稿姞&gs_id=1
+绛涢�夎澶囧姞&dev_id=10
+鍥炲弬锛�
+瀛楁 瀛楁璇存槑 澶囨敞
+dev_id 璁惧ID
+uptime 璁¤垂鏃堕棿
+sum_df 褰撴棩鎬荤數璐�
+sum_dl 褰撴棩鎬荤數閲�
+sum_time 褰撴棩鎬昏繍琛屾椂闂�
+hig_time 褰撴棩楂樻。杩愯鏃堕棿
+mid_time 褰撴棩涓。杩愯鏃堕棿
+low_time 褰撴棩浣庢。杩愯鏃堕棿
+2锛夋湀鐢甸噺鎶ヨ〃
+http://119.45.163.5:1125/zjl/API/getMoonDl?kt_token=v14YwV2SKsq477W9sw4z5m3O667g1&k
+t_dwid=20&kt_sonid=0&page=1&pageSize=1000000&date=2025-12
+绛涢�夊叕鍙稿姞&gs_id=1
+4銆佽璐瑰叕鍙哥鐞�
+1锛夎幏鍙栧叕鍙稿垪琛�
+http://119.45.163.5:1125/zjl/API/getGs?kt_token=v14YwV2SKsq477W9sw4z5m3O667g1&kt_dwi
+d=20&kt_sonid=0
+绛涢�夊叕鍙稿姞&id=1
+鍥炲弬锛�
+瀛楁 瀛楁璇存槑 澶囨敞
+id 鍏徃id
+uptime 涓婃姤鏃堕棿
+is_pwr 鍏呭�煎紑鍏�
+is_rest_stop 澶滈棿涓嶅仠鏈�
+gs_name 鍏徃鍚嶇О
+left_money 鍓╀綑閲戦
+left_money_y 鏄ㄦ棩鍓╀綑閲戦
+is_stop 鏄惁鍋滄満
+li_dev 璁惧鍒楄〃
+d_dev 鍒嗘憡姣斾緥 鏆傛湭鍚敤
+gs_bz 鍏徃澶囨敞
+2锛夋坊鍔犲叕鍙�
+http://119.45.163.5:1125/zjl/API/addGs?
+{
+"kt_token":"172588271126741",
+"kt_dwid":"0",
+"kt_unit":"1",
+"kt_sonid":"0",
+"is_pwr":1,
+"li_dev":[
+1,
+2
+],
+"d_dev":{
+"1":100,
+"2":100
+},
+"gs_name":"鍏徃1",
+"is_rest_stop":1,
+"gs_bz":""
+}
+3锛変慨鏀瑰叕鍙镐俊鎭�
+http://119.45.163.5:1125/zjl/API/changeGs?
+{
+"id":52,
+"is_pwr":1,
+"is_rest_stop":1,
+"gs_name":"鍏徃1",
+"left_money":0,
+"is_stop":0,
+"li_dev":[
+1,
+2
+],
+"d_dev":{
+"1":100,
+"2":100
+},
+"gs_bz":"",
+"is_pwr_xs":"寮�鍚�",
+"is_stop_xs":"鏈仠鏈�"
+}
+4锛夊垹闄ゅ叕鍙�
+http://119.45.163.5:1125/zjl/API/delGs?
+{
+"kt_token":"172588271126741",
+"kt_dwid":"0",
+"kt_sonid":"0",
+"kt_unit":"1",
+"id":52
+}
+5锛夊叧鑱斿尯鍩�
+http://119.45.163.5:1125/zjl/API/gsWithArea?
+{
+"id":2431,
+"kt_dwid":"20",
+"kt_token":"v14YwV2SKsq477W9sw4z5m3O667g1",
+"room_id":6783
+}
+6锛夊厖鍊艰垂鐢�
+http://119.45.163.5:1125/zjl/API/addMoney?
+{
+"kt_token":"172588271126741",
+"kt_dwid":"0",
+"kt_unit":"1",
+"kt_sonid":"0",
+"id":54,
+"cz_money":"100"
+}
+7锛夋竻绌洪噾棰�
+http://119.45.163.5:1125/zjl/API/cleanMoney?
+{
+"kt_token":"172588271126741",
+"kt_dwid":"0",
+"kt_unit":"1",
+"kt_sonid":"0",
+"id":54
+}
+8锛夋煡璇㈠厖鍊艰褰�
+http://119.45.163.5:1125/zjl/API/getCzLog?kt_token=172588271126741&kt_dwid=0&kt_unit=1&kt_soni
+d=0&start_time=2024-12-03&end_time=2024-12-05&page=1&pageSize=100000
+鍥炲弬锛�
+瀛楁 瀛楁璇存槑 澶囨敞
+gs_name 鍏徃鍚嶇О
+uptime 鍏呭�兼椂闂�
+cz_user 鍏呭�肩敤鎴�
+cz_tip 鍏呭�兼彁绀�
+cz_money 鍏呭�奸噾棰�
+left_money 鍓╀綑閲戦
+鍏�佽处鍙风鐞�
+1銆佽幏鍙栬处鍙�
+鍥炲弬锛�
+{
+"code":200,
+"message":"ok",
+"data":[{
+"user_id":1,
+"kt_token":"172588271126741",
+"kt_dwid":0,
+"username":"admin",
+"kt_qx":0,
+"kt_level":0,
+"kt_sonid":0,
+"kt_area":"0",
+"cz_pwr":0,
+"gs_name":"鏅虹簿鐏�",
+"kt_bz":"",
+"end_time":"2228-08-0808:08:08",
+"kt_unit":"1"
+}]
+}
+瀛楁 瀛楁璇存槑 澶囨敞
+user_id 鐢ㄦ埛id
+username 鐢ㄦ埛鍚嶇О
+kt_qx 鐢ㄦ埛鏉冮檺
+kt_level 0锛氭墍鏈夊姛鑳藉彲鎺� 鎺у埗鏉冮檺
+kt_area 0锛氳秴绾х鐞嗗憳 鏉冮檺绛夌骇
+kt_unit "0"锛氭墍鏈夊尯鍩燂紝鍜宬t_level鍏宠仈 鍙帶鍒跺尯鍩�
+2銆佹坊鍔犲瓙璐﹀彿
+http://119.45.163.5:1125/zjl/API/addUser?
+瀛楁 瀛楁璇存槑 澶囨敞
+kt_qx0鎵�鏈夊姛鑳藉彲鎺�
+1鍙兘鎺у埗锛屼笉鑳戒慨鏀硅澶囦俊鎭�
+2鍙兘鏌ョ湅锛屼笉鑳芥帶鍒朵慨鏀�
+kt_level0瓒呯骇绠$悊鍛�
+1璁惧绠$悊鍛�
+2鎴块棿绠$悊鍛�
+3妤煎眰绠$悊鍛�
+4鍗曞厓绠$悊鍛�
+5妤兼爧绠$悊鍛�
+6鍖哄煙绠$悊鍛�
+kt_area鏍规嵁kt_level鏀瑰彉
+濡俴t_level涓�1锛屽~鍏ラ�夋嫨鐨勮澶噄d
+瀛楃涓�"0,1,2,3,35,38,49,"
+涓�2鍒欏~鍏ユ埧闂寸殑id
+3銆佷慨鏀硅处鍙蜂俊鎭�
+4銆佷慨鏀硅处鍙峰瘑鐮�
+
+5锛夊垹闄よ处鍙�
+
diff --git "a/server/db/docs/\346\231\272\347\262\276\347\201\265API\346\216\245\345\217\243\346\226\207\346\241\24326-01.pdf" "b/server/db/docs/\346\231\272\347\262\276\347\201\265API\346\216\245\345\217\243\346\226\207\346\241\24326-01.pdf"
new file mode 100644
index 0000000..ab7e85d
--- /dev/null
+++ "b/server/db/docs/\346\231\272\347\262\276\347\201\265API\346\216\245\345\217\243\346\226\207\346\241\24326-01.pdf"
Binary files differ
diff --git a/server/db/quartz_job.module.column.sql b/server/db/quartz_job.module.column.sql
new file mode 100644
index 0000000..56c5af3
--- /dev/null
+++ b/server/db/quartz_job.module.column.sql
@@ -0,0 +1,4 @@
+-- quartz_job 澧炲姞 module 瀛楁锛圴isitServiceJobBiz 閫氳繃 module 鍙嶅皠璋冪敤 Feign 鏂规硶锛�
+-- 鑻ュ垪宸插瓨鍦紝鎵ц鎶ラ敊鍙拷鐣�
+
+ALTER TABLE `quartz_job` ADD COLUMN `module` varchar(200) DEFAULT NULL COMMENT 'Feign鏂规硶鍚�' AFTER `params`;
diff --git a/server/db/quartz_job.yw_timer.sql b/server/db/quartz_job.yw_timer.sql
new file mode 100644
index 0000000..449636b
--- /dev/null
+++ b/server/db/quartz_job.yw_timer.sql
@@ -0,0 +1,40 @@
+-- 闃滃畞杩愮淮瀹氭椂浠诲姟锛氱敱 system_timer 寰湇鍔¢�氳繃 quartz_job 璋冨害
+-- 鍓嶇疆锛氬厛鎵ц quartz_job.module.column.sql锛堣嫢灏氭棤 module 鍒楋級
+-- bean_name 鍥哄畾涓� visitServiceJob锛圴isitServiceJobBiz锛夛紝module 涓� VisitServiceFegin 鏂规硶鍚�
+-- state锛�1=杩愯 2=鏆傚仠锛涙柊澧炲悗璇峰湪銆屼綔涓氳皟搴︺�嶄腑鎭㈠浠诲姟鎴栭噸鍚� system_timer 浠ュ姞杞� Cron
+
+INSERT INTO `quartz_job` (`bean_name`, `params`, `module`, `cron_expres`, `state`, `remark`, `create_time`)
+SELECT 'visitServiceJob', '', 'getElectricalStatus', '0 */5 * * * ?', 1, '鏅鸿兘鐢佃〃-閲囬泦鍣ㄥ湪绾跨姸鎬�', CURRENT_TIMESTAMP
+WHERE NOT EXISTS (
+ SELECT 1 FROM `quartz_job` WHERE `module` = 'getElectricalStatus' AND `bean_name` = 'visitServiceJob'
+);
+
+INSERT INTO `quartz_job` (`bean_name`, `params`, `module`, `cron_expres`, `state`, `remark`, `create_time`)
+SELECT 'visitServiceJob', '', 'syncElectricalMeterData', '30 0 * * * ?', 1, '鏅鸿兘鐢佃〃-姣忓皬鏃舵壒閲忔妱琛�', CURRENT_TIMESTAMP
+WHERE NOT EXISTS (
+ SELECT 1 FROM `quartz_job` WHERE `module` = 'syncElectricalMeterData' AND `bean_name` = 'visitServiceJob'
+);
+
+INSERT INTO `quartz_job` (`bean_name`, `params`, `module`, `cron_expres`, `state`, `remark`, `create_time`)
+SELECT 'visitServiceJob', '', 'cleanElectricalLog', '0 30 2 * * ?', 1, '鏅鸿兘鐢佃〃-娓呯悊涓変釜鏈堝墠鎺ュ彛鏃ュ織', CURRENT_TIMESTAMP
+WHERE NOT EXISTS (
+ SELECT 1 FROM `quartz_job` WHERE `module` = 'cleanElectricalLog' AND `bean_name` = 'visitServiceJob'
+);
+
+INSERT INTO `quartz_job` (`bean_name`, `params`, `module`, `cron_expres`, `state`, `remark`, `create_time`)
+SELECT 'visitServiceJob', '', 'syncConditionerGatewayStatus', '0 */5 * * * ?', 1, '绌鸿皟澶氳仈鏈�-缃戝叧鍦ㄧ嚎鐘舵�佸悓姝�', CURRENT_TIMESTAMP
+WHERE NOT EXISTS (
+ SELECT 1 FROM `quartz_job` WHERE `module` = 'syncConditionerGatewayStatus' AND `bean_name` = 'visitServiceJob'
+);
+
+INSERT INTO `quartz_job` (`bean_name`, `params`, `module`, `cron_expres`, `state`, `remark`, `create_time`)
+SELECT 'visitServiceJob', '', 'syncConditionerIndoorUnits', '0 */10 * * * ?', 1, '绌鸿皟澶氳仈鏈�-鍐呮満杩愯鎬佸悓姝�', CURRENT_TIMESTAMP
+WHERE NOT EXISTS (
+ SELECT 1 FROM `quartz_job` WHERE `module` = 'syncConditionerIndoorUnits' AND `bean_name` = 'visitServiceJob'
+);
+
+INSERT INTO `quartz_job` (`bean_name`, `params`, `module`, `cron_expres`, `state`, `remark`, `create_time`)
+SELECT 'visitServiceJob', '', 'syncConditionerUsagePreviousDay', '0 1 0 * * ?', 1, '绌鸿皟澶氳仈鏈�-鍚屾鍓嶄竴鏃ョ敤閲忔姤琛�', CURRENT_TIMESTAMP
+WHERE NOT EXISTS (
+ SELECT 1 FROM `quartz_job` WHERE `module` = 'syncConditionerUsagePreviousDay' AND `bean_name` = 'visitServiceJob'
+);
diff --git a/server/db/yw_conditioner_module.sql b/server/db/yw_conditioner_module.sql
new file mode 100644
index 0000000..759a21c
--- /dev/null
+++ b/server/db/yw_conditioner_module.sql
@@ -0,0 +1,165 @@
+-- 绌鸿皟澶氳仈鏈烘ā鍧楋細缃戝叧/鐢佃〃/璁¤垂/鎿嶄綔璁板綍/鐢ㄩ噺 + 鍐呮満琛ㄦ墿灞�
+
+CREATE TABLE IF NOT EXISTS `yw_conditioner_gateway` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `creator` int DEFAULT NULL,
+ `create_date` datetime DEFAULT NULL,
+ `editor` int DEFAULT NULL,
+ `edit_date` datetime DEFAULT NULL,
+ `isdeleted` int DEFAULT 0,
+ `remark` varchar(500) DEFAULT NULL,
+ `platform_wg_id` int DEFAULT NULL COMMENT '骞冲彴缃戝叧ID',
+ `wg_mac` varchar(64) NOT NULL COMMENT '缃戝叧MAC',
+ `wg_bz` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+ `online_status` varchar(20) DEFAULT NULL COMMENT '鍦ㄧ嚎/绂荤嚎',
+ `last_sync_date` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_wg_mac` (`wg_mac`),
+ KEY `idx_online_status` (`online_status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅虹簿鐏电綉鍏抽暅鍍�';
+
+CREATE TABLE IF NOT EXISTS `yw_conditioner_gateway_log` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `creator` int DEFAULT NULL,
+ `create_date` datetime DEFAULT NULL,
+ `editor` int DEFAULT NULL,
+ `edit_date` datetime DEFAULT NULL,
+ `isdeleted` int DEFAULT 0,
+ `remark` varchar(500) DEFAULT NULL,
+ `gateway_id` int DEFAULT NULL,
+ `wg_mac` varchar(64) DEFAULT NULL,
+ `old_status` varchar(20) DEFAULT NULL,
+ `new_status` varchar(20) DEFAULT NULL,
+ `log_time` datetime DEFAULT NULL,
+ `source` varchar(32) DEFAULT NULL COMMENT 'manual/schedule',
+ PRIMARY KEY (`id`),
+ KEY `idx_gateway_id` (`gateway_id`),
+ KEY `idx_wg_mac` (`wg_mac`),
+ KEY `idx_log_time` (`log_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='缃戝叧涓婁笅绾胯褰�';
+
+CREATE TABLE IF NOT EXISTS `yw_conditioner_meter` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `creator` int DEFAULT NULL,
+ `create_date` datetime DEFAULT NULL,
+ `editor` int DEFAULT NULL,
+ `edit_date` datetime DEFAULT NULL,
+ `isdeleted` int DEFAULT 0,
+ `remark` varchar(500) DEFAULT NULL,
+ `platform_db_id` int DEFAULT NULL,
+ `db_name` varchar(128) DEFAULT NULL,
+ `db_adr` varchar(64) DEFAULT NULL,
+ `wg_mac` varchar(64) DEFAULT NULL,
+ `wg_id` int DEFAULT NULL,
+ `xy_id` int DEFAULT NULL,
+ `xy_name` varchar(128) DEFAULT NULL,
+ `db_bb` int DEFAULT NULL COMMENT '鍙樻瘮',
+ `standby_share` varchar(32) DEFAULT NULL COMMENT '寰呮満鍒嗘憡',
+ `outdoor_loop` int DEFAULT NULL COMMENT '澶栨満鍥炶矾鍙�',
+ `power_kw` decimal(12,4) DEFAULT NULL,
+ `total_dl` decimal(12,4) DEFAULT NULL,
+ `db_data` text,
+ `db_uptime` varchar(32) DEFAULT NULL,
+ `last_sync_date` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx_platform_db_id` (`platform_db_id`),
+ KEY `idx_wg_mac` (`wg_mac`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅虹簿鐏电數琛ㄩ暅鍍�';
+
+CREATE TABLE IF NOT EXISTS `yw_conditioner_billing` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `creator` int DEFAULT NULL,
+ `create_date` datetime DEFAULT NULL,
+ `editor` int DEFAULT NULL,
+ `edit_date` datetime DEFAULT NULL,
+ `isdeleted` int DEFAULT 0,
+ `remark` varchar(500) DEFAULT NULL,
+ `platform_dev_id` int DEFAULT NULL,
+ `dev_name` varchar(128) DEFAULT NULL,
+ `wg_mac` varchar(64) DEFAULT NULL,
+ `kw_type` int DEFAULT NULL COMMENT '0鏃堕暱1鑳借��2鐢佃〃',
+ `fan_arg` decimal(12,4) DEFAULT NULL,
+ `fan_kw` decimal(12,4) DEFAULT NULL,
+ `hig_kw` decimal(12,4) DEFAULT NULL,
+ `mid_kw` decimal(12,4) DEFAULT NULL,
+ `low_kw` decimal(12,4) DEFAULT NULL,
+ `kt_dj` decimal(12,4) DEFAULT NULL,
+ `last_sync_date` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx_platform_dev_id` (`platform_dev_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅虹簿鐏佃璐圭郴鏁伴暅鍍�';
+
+CREATE TABLE IF NOT EXISTS `yw_conditioner_actions` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `creator` int DEFAULT NULL,
+ `create_date` datetime DEFAULT NULL,
+ `editor` int DEFAULT NULL,
+ `edit_date` datetime DEFAULT NULL,
+ `isdeleted` int DEFAULT 0,
+ `remark` varchar(500) DEFAULT NULL,
+ `conditioner_id` int DEFAULT NULL,
+ `platform_dev_id` int DEFAULT NULL,
+ `dev_name` varchar(128) DEFAULT NULL,
+ `wg_mac` varchar(64) DEFAULT NULL,
+ `action_type` int NOT NULL COMMENT '1寮�鍏�2妯″紡3椋庨��4娓╁害5閿佸畾6鏌ョ數閲�7鏌ュ姛鐜�',
+ `action_content` varchar(500) DEFAULT NULL,
+ `result_status` int DEFAULT 1 COMMENT '0澶辫触1鎴愬姛',
+ `result_msg` varchar(500) DEFAULT NULL,
+ `source` varchar(64) DEFAULT NULL COMMENT '鎿嶄綔鏉ユ簮',
+ `request_body` text,
+ `response_body` text,
+ PRIMARY KEY (`id`),
+ KEY `idx_conditioner_id` (`conditioner_id`),
+ KEY `idx_platform_dev_id` (`platform_dev_id`),
+ KEY `idx_create_date` (`create_date`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='绌鸿皟璁惧鎺у埗璁板綍';
+
+CREATE TABLE IF NOT EXISTS `yw_conditioner_usage` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `creator` int DEFAULT NULL,
+ `create_date` datetime DEFAULT NULL,
+ `editor` int DEFAULT NULL,
+ `edit_date` datetime DEFAULT NULL,
+ `isdeleted` int DEFAULT 0,
+ `remark` varchar(500) DEFAULT NULL,
+ `platform_dev_id` int NOT NULL,
+ `dev_name` varchar(128) DEFAULT NULL,
+ `usage_date` date NOT NULL,
+ `sum_time` decimal(12,2) DEFAULT NULL,
+ `sum_dl` decimal(12,4) DEFAULT NULL,
+ `sum_df` decimal(12,4) DEFAULT NULL,
+ `gs_id` int DEFAULT NULL,
+ `sync_date` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_dev_date_gs` (`platform_dev_id`,`usage_date`,`gs_id`),
+ KEY `idx_usage_date` (`usage_date`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='璁惧鏃ョ敤閲忛暅鍍�';
+
+-- 鎵╁睍 yw_conditioner锛堥娆℃墽琛岋紱鍒楀凡瀛樺湪鏃朵細鎶ラ敊鍙拷鐣ワ級
+ALTER TABLE `yw_conditioner` ADD COLUMN `platform_dev_id` int DEFAULT NULL COMMENT '骞冲彴璁惧ID';
+ALTER TABLE `yw_conditioner` ADD COLUMN `wg_id` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `wg_mac` varchar(64) DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `wg_qid` varchar(64) DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `pid` varchar(16) DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `online` varchar(20) DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `pwr` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `mode` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `fan` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `fan_set` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `temp` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `temp_set` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `kt_lock` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `stop_logo` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `uptime` varchar(32) DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `floor_id` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `floor_name` varchar(64) DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `room_id` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `room_name` varchar(64) DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `dev_type_id` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `dev_type_name` varchar(64) DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `lock_pwr` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `lock_mode` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `lock_fan` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `lock_min_temp` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `lock_max_temp` int DEFAULT NULL;
+ALTER TABLE `yw_conditioner` ADD COLUMN `last_sync_date` datetime DEFAULT NULL;
diff --git a/server/system_service/src/main/java/com/doumee/config/cloudfilter/LoginHandlerInterceptor.java b/server/system_service/src/main/java/com/doumee/config/cloudfilter/LoginHandlerInterceptor.java
index 428da3f..abd19e5 100644
--- a/server/system_service/src/main/java/com/doumee/config/cloudfilter/LoginHandlerInterceptor.java
+++ b/server/system_service/src/main/java/com/doumee/config/cloudfilter/LoginHandlerInterceptor.java
@@ -27,6 +27,9 @@
public class LoginHandlerInterceptor implements HandlerInterceptor {
+ /** 涓庡墠绔� BasePage.adminCode 涓�鑷达紝瓒呯骇绠$悊鍛樿烦杩囨帴鍙f潈闄愮爜鏍¢獙 */
+ private static final String ADMIN_ROLE_CODE = "admin";
+
private RedisTemplate<String,Object> stringRedisTemplate;
@@ -55,21 +58,23 @@
LoginUserInfo user = checkLogin(token);
if (handlerMethod.hasMethodAnnotation(CloudRequiredPermission.class)) {
CloudRequiredPermission p = handlerMethod.getMethodAnnotation(CloudRequiredPermission.class);
- if(p.value()!=null && p.value().length>0){
+ if (p.value() != null && p.value().length > 0 && !isAdminUser(user)) {
boolean hasPermission = false;
- for(String s :p.value()){
- if(user.getPermissions()!=null){
- for(String t :user.getPermissions()){
- if(StringUtils.equals(t,s)){
+ for (String s : p.value()) {
+ if (user.getPermissions() != null) {
+ for (String t : user.getPermissions()) {
+ if (StringUtils.equals(t, s)) {
hasPermission = true;
break;
}
}
}
+ if (hasPermission) {
+ break;
+ }
}
- if(!hasPermission) {
- //娌℃湁鎿嶄綔鏉冮檺
- throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"娌℃湁璇ユ搷浣滄潈闄�");
+ if (!hasPermission) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "娌℃湁璇ユ搷浣滄潈闄�");
}
}
}
@@ -118,6 +123,18 @@
return body;
}
+ private boolean isAdminUser(LoginUserInfo user) {
+ if (user == null || user.getRoles() == null) {
+ return false;
+ }
+ for (String role : user.getRoles()) {
+ if (StringUtils.equals(role, ADMIN_ROLE_CODE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private LoginUserInfo checkLogin(String token) {
if (token == null || token.isEmpty()) {
throw new BusinessException(ResponseStatus.NO_LOGIN.getCode(),"鏈櫥褰�");
diff --git a/server/system_service/src/main/java/com/doumee/core/utils/Constants.java b/server/system_service/src/main/java/com/doumee/core/utils/Constants.java
index 8fe6c89..b12f95f 100644
--- a/server/system_service/src/main/java/com/doumee/core/utils/Constants.java
+++ b/server/system_service/src/main/java/com/doumee/core/utils/Constants.java
@@ -169,6 +169,11 @@
public static final String ELECTRICAL_NOTIFY_URL = "notify_url";
public static final String ELECTRICAL_API_URL = "api_url";
public static final String ELECTRICAL_API2_URL = "api_url2";
+ /** 鏅虹簿鐏电┖璋冨钩鍙板弬鏁帮紙SYSTEM_DICT.code锛� */
+ public static final String CONDITIONER_PARAM = "CONDITIONER_PARAM";
+ public static final String CONDITIONER_BASE_URL = "base_url";
+ public static final String CONDITIONER_USERNAME = "username";
+ public static final String CONDITIONER_PASSWORD = "password";
public static final String TMS_ORDER_DETAIL_URL ="TMS_ORDER_DETAIL_URL" ;
public static final String TMS_LOCK_STATUS_URL ="TMS_LOCK_STATUS_URL" ;
public static final String TMS_INTERFACE_URL_PREFIX ="TMS_INTERFACE_URL_PREFIX" ;
diff --git a/server/system_timer/src/main/java/com/doumee/jobs/fegin/VisitServiceFegin.java b/server/system_timer/src/main/java/com/doumee/jobs/fegin/VisitServiceFegin.java
index b9de78e..15fa368 100644
--- a/server/system_timer/src/main/java/com/doumee/jobs/fegin/VisitServiceFegin.java
+++ b/server/system_timer/src/main/java/com/doumee/jobs/fegin/VisitServiceFegin.java
@@ -96,6 +96,22 @@
@GetMapping("/timer/yw/syncElectricalMeterData")
ApiResponse syncElectricalMeterData();
+ @ApiOperation("銆愰槣瀹佽繍缁淬�戞竻鐞嗕笁涓湀鍓嶇數琛ㄦ帴鍙f棩蹇�")
+ @GetMapping("/timer/yw/cleanElectricalLog")
+ ApiResponse cleanElectricalLog();
+
+ @ApiOperation("銆愰槣瀹佽繍缁淬�戝畾鏃跺悓姝ユ櫤绮剧伒缃戝叧鍦ㄧ嚎鐘舵��")
+ @GetMapping("/timer/yw/syncConditionerGatewayStatus")
+ ApiResponse syncConditionerGatewayStatus();
+
+ @ApiOperation("銆愰槣瀹佽繍缁淬�戝畾鏃跺悓姝ユ櫤绮剧伒绌鸿皟鍐呮満杩愯鎬�")
+ @GetMapping("/timer/yw/syncConditionerIndoorUnits")
+ ApiResponse syncConditionerIndoorUnits();
+
+ @ApiOperation("銆愰槣瀹佽繍缁淬�戝畾鏃跺悓姝ュ墠涓�鏃ョ┖璋冨鑱旀満鐢ㄩ噺")
+ @GetMapping("/timer/yw/syncConditionerUsagePreviousDay")
+ ApiResponse syncConditionerUsagePreviousDay();
+
@ApiOperation("銆愰槣瀹佽繍缁淬�戝畾鏃跺鐞嗗悎鍚岃繃鏈�")
@GetMapping("/timer/yw/ywDealContractTimeOutTimer")
ApiResponse ywDealContractTimeOutTimer();
diff --git a/server/visits/admin_timer/src/main/java/com/doumee/TimerApplication.java b/server/visits/admin_timer/src/main/java/com/doumee/TimerApplication.java
index 21c86e7..8541170 100644
--- a/server/visits/admin_timer/src/main/java/com/doumee/TimerApplication.java
+++ b/server/visits/admin_timer/src/main/java/com/doumee/TimerApplication.java
@@ -7,7 +7,6 @@
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
-import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 鍚姩绫�
@@ -16,7 +15,6 @@
*/
@Slf4j
@EnableAsync
-@EnableScheduling
@SpringBootApplication
@MapperScan("com.doumee.dao.*")
@EnableDiscoveryClient
diff --git a/server/visits/admin_timer/src/main/java/com/doumee/api/YwTimerController.java b/server/visits/admin_timer/src/main/java/com/doumee/api/YwTimerController.java
index c08f79e..ca761db 100644
--- a/server/visits/admin_timer/src/main/java/com/doumee/api/YwTimerController.java
+++ b/server/visits/admin_timer/src/main/java/com/doumee/api/YwTimerController.java
@@ -109,6 +109,30 @@
return ApiResponse.success("鐢佃〃鎺ュ彛鏃ュ織娓呯悊鎴愬姛");
}
+ @Autowired
+ private com.doumee.service.business.ConditionerBizService conditionerBizService;
+
+ @ApiOperation("瀹氭椂鍚屾鏅虹簿鐏电綉鍏冲湪绾跨姸鎬�")
+ @GetMapping("/syncConditionerGatewayStatus")
+ public ApiResponse syncConditionerGatewayStatus() {
+ conditionerBizService.syncGatewayStatus();
+ return ApiResponse.success("瀹氭椂鍚屾缃戝叧鐘舵�佹垚鍔�");
+ }
+
+ @ApiOperation("瀹氭椂鍚屾鏅虹簿鐏电┖璋冨唴鏈鸿繍琛屾��")
+ @GetMapping("/syncConditionerIndoorUnits")
+ public ApiResponse syncConditionerIndoorUnits() {
+ conditionerBizService.syncIndoorUnits();
+ return ApiResponse.success("瀹氭椂鍚屾绌鸿皟鍐呮満鐘舵�佹垚鍔�");
+ }
+
+ @ApiOperation("瀹氭椂鍚屾鍓嶄竴鏃ョ┖璋冨鑱旀満鐢ㄩ噺鎶ヨ〃")
+ @GetMapping("/syncConditionerUsagePreviousDay")
+ public ApiResponse syncConditionerUsagePreviousDay() {
+ String msg = conditionerBizService.syncUsagePreviousDay();
+ return ApiResponse.success(msg);
+ }
+
@ApiOperation("瀹氭椂鏇存柊鎴挎簮绉熻祦鐘舵��")
@GetMapping("/ywRoomStatusTimer")
public ApiResponse ywRoomStatusTimer() {
diff --git a/server/visits/admin_timer/src/main/java/com/doumee/job/ElectricalScheduleJob.java b/server/visits/admin_timer/src/main/java/com/doumee/job/ElectricalScheduleJob.java
deleted file mode 100644
index 42dbca7..0000000
--- a/server/visits/admin_timer/src/main/java/com/doumee/job/ElectricalScheduleJob.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.doumee.job;
-
-import com.doumee.service.business.YwElectricalBizService;
-import com.doumee.service.business.YwElectricalService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-@Slf4j
-@Component
-public class ElectricalScheduleJob {
-
- @Autowired
- private YwElectricalService ywElectricalService;
- @Autowired
- private YwElectricalBizService ywElectricalBizService;
-
- /** 姣�5鍒嗛挓鍚屾閲囬泦鍣ㄥ湪绾跨姸鎬� */
- @Scheduled(cron = "0 */5 * * * ?")
- public void syncElectricalStatus() {
- try {
- ywElectricalService.getElectricalStatus();
- } catch (Exception e) {
- log.error("syncElectricalStatus failed", e);
- }
- }
-
- /** 姣忓皬鏃�0鍒�30绉掓壒閲忔妱琛� */
- @Scheduled(cron = "30 0 * * * ?")
- public void syncMeterData() {
- try {
- ywElectricalBizService.syncMeterDataScheduled();
- } catch (Exception e) {
- log.error("syncMeterData failed", e);
- }
- }
-
- /** 姣忔棩娓呯悊3涓湀鍓嶇殑鎺ュ彛鏃ュ織 */
- @Scheduled(cron = "0 30 2 * * ?")
- public void cleanElectricalLog() {
- try {
- ywElectricalBizService.cleanLogBeforeThreeMonths();
- } catch (Exception e) {
- log.error("cleanElectricalLog failed", e);
- }
- }
-}
diff --git a/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerActionsCloudController.java b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerActionsCloudController.java
new file mode 100644
index 0000000..1954e5b
--- /dev/null
+++ b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerActionsCloudController.java
@@ -0,0 +1,49 @@
+package com.doumee.cloud.admin;
+
+import com.doumee.api.BaseController;
+import com.doumee.config.annotation.CloudRequiredPermission;
+import com.doumee.core.annotation.excel.ExcelExporter;
+import com.doumee.core.model.ApiResponse;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.model.YwConditionerActions;
+import com.doumee.service.business.YwConditionerActionsService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+
+@Api(tags = "绌鸿皟璁惧鎺у埗璁板綍")
+@RestController
+@RequestMapping(Constants.CLOUD_SERVICE_URL_INDEX + "/business/ywConditionerActions")
+public class YwConditionerActionsCloudController extends BaseController {
+
+ @Autowired
+ private YwConditionerActionsService actionsService;
+
+ @ApiOperation("鍒嗛〉鏌ヨ")
+ @PostMapping("/page")
+ @CloudRequiredPermission("business:ywconditioneractions:query")
+ public ApiResponse<PageData<YwConditionerActions>> findPage(@RequestBody PageWrap<YwConditionerActions> pageWrap,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ if (pageWrap.getModel() != null) {
+ pageWrap.getModel().setLoginUserInfo(this.getLoginUser(token));
+ }
+ return ApiResponse.success(actionsService.findPage(pageWrap));
+ }
+
+ @ApiOperation("瀵煎嚭Excel")
+ @PostMapping("/exportExcel")
+ @CloudRequiredPermission("business:ywconditioneractions:exportExcel")
+ public void exportExcel(@RequestBody PageWrap<YwConditionerActions> pageWrap, HttpServletResponse response,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ if (pageWrap.getModel() != null) {
+ pageWrap.getModel().setLoginUserInfo(this.getLoginUser(token));
+ }
+ ExcelExporter.build(YwConditionerActions.class)
+ .export(actionsService.findPage(pageWrap).getRecords(), "绌鸿皟璁惧鎺у埗璁板綍", response);
+ }
+}
diff --git a/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerBillingCloudController.java b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerBillingCloudController.java
new file mode 100644
index 0000000..56da312
--- /dev/null
+++ b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerBillingCloudController.java
@@ -0,0 +1,43 @@
+package com.doumee.cloud.admin;
+
+import com.doumee.api.BaseController;
+import com.doumee.config.annotation.CloudRequiredPermission;
+import com.doumee.core.annotation.pr.PreventRepeat;
+import com.doumee.core.model.ApiResponse;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.model.YwConditionerBilling;
+import com.doumee.service.business.YwConditionerBillingService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "绌鸿皟璁¤垂绯绘暟")
+@RestController
+@RequestMapping(Constants.CLOUD_SERVICE_URL_INDEX + "/business/ywConditionerBilling")
+public class YwConditionerBillingCloudController extends BaseController {
+
+ @Autowired
+ private YwConditionerBillingService billingService;
+
+ @ApiOperation("鍒嗛〉鏌ヨ")
+ @PostMapping("/page")
+ @CloudRequiredPermission("business:ywconditionerbilling:query")
+ public ApiResponse<PageData<YwConditionerBilling>> findPage(@RequestBody PageWrap<YwConditionerBilling> pageWrap,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ if (pageWrap.getModel() != null) {
+ pageWrap.getModel().setLoginUserInfo(this.getLoginUser(token));
+ }
+ return ApiResponse.success(billingService.findPage(pageWrap));
+ }
+
+ @PreventRepeat
+ @ApiOperation("鍚屾璁¤垂绯绘暟")
+ @PostMapping("/syncAll")
+ @CloudRequiredPermission("business:ywconditionerbilling:sync")
+ public ApiResponse<String> syncAll(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(billingService.syncAll());
+ }
+}
diff --git a/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerCloudController.java b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerCloudController.java
index 56fc4e7..9f79a7a 100644
--- a/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerCloudController.java
+++ b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerCloudController.java
@@ -7,7 +7,10 @@
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.dto.YwConditionerLockDTO;
+import com.doumee.dao.business.dto.YwConditionerOperateDTO;
import com.doumee.dao.business.model.YwConditioner;
+import com.doumee.dao.business.model.YwConditionerActions;
import com.doumee.service.business.YwConditionerService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -18,6 +21,7 @@
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* 绌鸿皟璁惧淇℃伅
@@ -93,4 +97,65 @@
pageWrap.getModel().setLoginUserInfo(this.getLoginUser(token));
ExcelExporter.build(YwConditioner.class).export(ywConditionerService.findPage(pageWrap).getRecords(), "绌鸿皟璁惧淇℃伅", response);
}
+
+ @ApiOperation("鍗$墖鍒嗛〉")
+ @PostMapping("/cardPage")
+ @CloudRequiredPermission("business:ywconditioner:query")
+ public ApiResponse<PageData<YwConditioner>> findCardPage(@RequestBody PageWrap<YwConditioner> pageWrap,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ if (pageWrap.getModel() != null) {
+ pageWrap.getModel().setLoginUserInfo(this.getLoginUser(token));
+ }
+ return ApiResponse.success(ywConditionerService.findCardPage(pageWrap));
+ }
+
+ @PreventRepeat
+ @ApiOperation("鍏ㄩ噺鍚屾")
+ @PostMapping("/syncAll")
+ @CloudRequiredPermission("business:ywconditioner:sync")
+ public ApiResponse<String> syncAll(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(ywConditionerService.syncAll());
+ }
+
+ @PreventRepeat
+ @ApiOperation("鍚屾璁惧涓庣姸鎬�")
+ @PostMapping("/syncDevicesAndStatus")
+ @CloudRequiredPermission("business:ywconditioner:sync")
+ public ApiResponse<String> syncDevicesAndStatus(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(ywConditionerService.syncDevicesAndStatus());
+ }
+
+ @ApiOperation("璁惧鎺у埗")
+ @PostMapping("/operate")
+ @CloudRequiredPermission("business:ywconditioner:operate")
+ public ApiResponse<String> operate(@RequestBody YwConditionerOperateDTO dto,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(ywConditionerService.operate(dto, this.getLoginUser(token)));
+ }
+
+ @ApiOperation("璁惧閿佸畾")
+ @PostMapping("/lock")
+ @CloudRequiredPermission("business:ywconditioner:operate")
+ public ApiResponse<String> lock(@RequestBody YwConditionerLockDTO dto,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(ywConditionerService.lock(dto, this.getLoginUser(token)));
+ }
+
+ @ApiOperation("鍗曡澶囨帶鍒跺巻鍙�")
+ @PostMapping("/historyPage")
+ @CloudRequiredPermission("business:ywconditioner:query")
+ public ApiResponse<PageData<YwConditionerActions>> historyPage(@RequestBody PageWrap<YwConditionerActions> pageWrap,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ if (pageWrap.getModel() != null) {
+ pageWrap.getModel().setLoginUserInfo(this.getLoginUser(token));
+ }
+ return ApiResponse.success(ywConditionerService.historyPage(pageWrap));
+ }
+
+ @ApiOperation("缃戝叧涓嬫媺")
+ @GetMapping("/gatewayOptions")
+ @CloudRequiredPermission("business:ywconditioner:query")
+ public ApiResponse<List<Map<String, Object>>> gatewayOptions(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(ywConditionerService.gatewayOptions());
+ }
}
diff --git a/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerGatewayCloudController.java b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerGatewayCloudController.java
new file mode 100644
index 0000000..71ca916
--- /dev/null
+++ b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerGatewayCloudController.java
@@ -0,0 +1,55 @@
+package com.doumee.cloud.admin;
+
+import com.doumee.api.BaseController;
+import com.doumee.config.annotation.CloudRequiredPermission;
+import com.doumee.core.annotation.pr.PreventRepeat;
+import com.doumee.core.model.ApiResponse;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.model.YwConditionerGateway;
+import com.doumee.dao.business.model.YwConditionerGatewayLog;
+import com.doumee.service.business.YwConditionerGatewayService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "绌鸿皟缃戝叧绠$悊")
+@RestController
+@RequestMapping(Constants.CLOUD_SERVICE_URL_INDEX + "/business/ywConditionerGateway")
+public class YwConditionerGatewayCloudController extends BaseController {
+
+ @Autowired
+ private YwConditionerGatewayService gatewayService;
+
+ @ApiOperation("鍒嗛〉鏌ヨ")
+ @PostMapping("/page")
+ @CloudRequiredPermission("business:ywconditionergateway:query")
+ public ApiResponse<PageData<YwConditionerGateway>> findPage(@RequestBody PageWrap<YwConditionerGateway> pageWrap,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ if (pageWrap.getModel() != null) {
+ pageWrap.getModel().setLoginUserInfo(this.getLoginUser(token));
+ }
+ return ApiResponse.success(gatewayService.findPage(pageWrap));
+ }
+
+ @PreventRepeat
+ @ApiOperation("鍚屾缃戝叧")
+ @PostMapping("/syncAll")
+ @CloudRequiredPermission("business:ywconditionergateway:sync")
+ public ApiResponse<String> syncAll(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(gatewayService.syncAll());
+ }
+
+ @ApiOperation("缃戝叧涓婁笅绾胯褰�")
+ @PostMapping("/gatewayLogPage")
+ @CloudRequiredPermission("business:ywconditionergateway:query")
+ public ApiResponse<PageData<YwConditionerGatewayLog>> gatewayLogPage(@RequestBody PageWrap<YwConditionerGatewayLog> pageWrap,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ if (pageWrap.getModel() != null) {
+ pageWrap.getModel().setLoginUserInfo(this.getLoginUser(token));
+ }
+ return ApiResponse.success(gatewayService.gatewayLogPage(pageWrap));
+ }
+}
diff --git a/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerMeterCloudController.java b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerMeterCloudController.java
new file mode 100644
index 0000000..95587a3
--- /dev/null
+++ b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerMeterCloudController.java
@@ -0,0 +1,59 @@
+package com.doumee.cloud.admin;
+
+import com.doumee.api.BaseController;
+import com.doumee.config.annotation.CloudRequiredPermission;
+import com.doumee.core.annotation.pr.PreventRepeat;
+import com.doumee.core.model.ApiResponse;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.model.YwConditionerMeter;
+import com.doumee.service.business.YwConditionerMeterService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "绌鸿皟鐢佃〃绠$悊")
+@RestController
+@RequestMapping(Constants.CLOUD_SERVICE_URL_INDEX + "/business/ywConditionerMeter")
+public class YwConditionerMeterCloudController extends BaseController {
+
+ @Autowired
+ private YwConditionerMeterService meterService;
+
+ @ApiOperation("鍒嗛〉鏌ヨ")
+ @PostMapping("/page")
+ @CloudRequiredPermission("business:ywconditionermeter:query")
+ public ApiResponse<PageData<YwConditionerMeter>> findPage(@RequestBody PageWrap<YwConditionerMeter> pageWrap,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ if (pageWrap.getModel() != null) {
+ pageWrap.getModel().setLoginUserInfo(this.getLoginUser(token));
+ }
+ return ApiResponse.success(meterService.findPage(pageWrap));
+ }
+
+ @PreventRepeat
+ @ApiOperation("鍚屾鐢佃〃")
+ @PostMapping("/syncAll")
+ @CloudRequiredPermission("business:ywconditionermeter:sync")
+ public ApiResponse<String> syncAll(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(meterService.syncAll());
+ }
+
+ @ApiOperation("鏌ョ數閲�")
+ @PostMapping("/queryEnergy/{id}")
+ @CloudRequiredPermission("business:ywconditionermeter:operate")
+ public ApiResponse<String> queryEnergy(@PathVariable Integer id,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(meterService.queryEnergy(id, this.getLoginUser(token)));
+ }
+
+ @ApiOperation("鏌ュ姛鐜�")
+ @PostMapping("/queryPower/{id}")
+ @CloudRequiredPermission("business:ywconditionermeter:operate")
+ public ApiResponse<String> queryPower(@PathVariable Integer id,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(meterService.queryPower(id, this.getLoginUser(token)));
+ }
+}
diff --git a/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerReportCloudController.java b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerReportCloudController.java
new file mode 100644
index 0000000..116939f
--- /dev/null
+++ b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerReportCloudController.java
@@ -0,0 +1,60 @@
+package com.doumee.cloud.admin;
+
+import com.doumee.api.BaseController;
+import com.doumee.config.annotation.CloudRequiredPermission;
+import com.doumee.core.annotation.pr.PreventRepeat;
+import com.doumee.core.model.ApiResponse;
+import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.dto.YwConditionerReportQueryDTO;
+import com.doumee.dao.business.dto.YwConditionerUsageReportPageDTO;
+import com.doumee.service.business.YwConditionerReportService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
+
+@Api(tags = "绌鸿皟鐢ㄩ噺鏁版嵁鎶ヨ〃")
+@RestController
+@RequestMapping(Constants.CLOUD_SERVICE_URL_INDEX + "/business/ywConditionerReport")
+public class YwConditionerReportCloudController extends BaseController {
+
+ @Autowired
+ private YwConditionerReportService reportService;
+
+ @ApiOperation("閫忚鍒嗛〉鏌ヨ")
+ @PostMapping("/page")
+ @CloudRequiredPermission("business:ywconditionerreport:query")
+ public ApiResponse<YwConditionerUsageReportPageDTO> findPage(@RequestBody YwConditionerReportQueryDTO query,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(reportService.findPage(query));
+ }
+
+ @PreventRepeat
+ @ApiOperation("鍚屾鐢ㄩ噺")
+ @PostMapping("/syncUsage")
+ @CloudRequiredPermission("business:ywconditionerreport:sync")
+ public ApiResponse<String> syncUsage(@RequestBody YwConditionerReportQueryDTO query,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(reportService.syncUsage(query));
+ }
+
+ @ApiOperation("鍟嗘埛涓嬫媺")
+ @GetMapping("/merchantOptions")
+ @CloudRequiredPermission("business:ywconditionerreport:query")
+ public ApiResponse<List<Map<String, Object>>> merchantOptions(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(reportService.merchantOptions());
+ }
+
+ @ApiOperation("瀵煎嚭Excel")
+ @PostMapping("/exportExcel")
+ @CloudRequiredPermission("business:ywconditionerreport:exportExcel")
+ public void exportExcel(@RequestBody YwConditionerReportQueryDTO query,
+ HttpServletResponse response,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ reportService.exportExcel(query, response);
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/ConditionerToolTestUtil.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/ConditionerToolTestUtil.java
new file mode 100644
index 0000000..57b413f
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/ConditionerToolTestUtil.java
@@ -0,0 +1,57 @@
+package com.doumee.core.conditoner;
+
+import com.alibaba.fastjson.JSON;
+import com.doumee.core.conditoner.model.ConditionerConstant;
+import com.doumee.core.conditoner.model.request.ConditionerSessionRequest;
+import com.doumee.core.conditoner.model.request.GetDevOneRequest;
+import com.doumee.core.conditoner.model.response.ConditionerBaseResponse;
+import com.doumee.core.conditoner.model.response.DeviceStatusResponse;
+import com.doumee.core.conditoner.model.response.LoginDataResponse;
+
+import java.util.List;
+
+/**
+ * 鏅虹簿鐏垫帴鍙e啋鐑熸祴璇曪紙main 鏂规硶鑱旇皟锛夈��
+ * <p>
+ * 杩愯鍓嶈纭 {@link ConditionerConstant} 涓� base_url / username / password 宸查厤缃紝
+ * 鎴栧凡閫氳繃瀛楀吀 {@code CONDITIONER_PARAM} 鐢� {@link com.doumee.service.business.impl.ConditionerConfigService} 鍔犺浇銆�
+ * </p>
+ */
+public class ConditionerToolTestUtil {
+
+ public static void main(String[] args) {
+ System.out.println("=== 鏅虹簿鐏� login ===");
+ ConditionerBaseResponse<LoginDataResponse> loginResp = ConditionerUtil.login();
+ print(loginResp);
+
+ if (loginResp == null || !loginResp.isSuccess()) {
+ System.out.println("鐧诲綍澶辫触锛岀粓姝㈠悗缁祴璇�");
+ return;
+ }
+
+ System.out.println("session kt_token=" + ConditionerConstant.kt_token + ", kt_dwid=" + ConditionerConstant.kt_dwid);
+
+ System.out.println("\n=== getDevList ===");
+ ConditionerSessionRequest session = new ConditionerSessionRequest();
+ session.fillSessionDefaults();
+ ConditionerBaseResponse<List<DeviceStatusResponse>> devList = ConditionerUtil.getDevList(session);
+ print(devList);
+
+ if (devList != null && devList.getData() != null && !devList.getData().isEmpty()) {
+ DeviceStatusResponse first = devList.getData().get(0);
+ System.out.println("\n=== getDevOne (first device) ===");
+ GetDevOneRequest oneReq = new GetDevOneRequest();
+ oneReq.setWg_mac(first.getWg_mac());
+ oneReq.setWg_qid(first.getWg_qid());
+ oneReq.fillSessionDefaults();
+ print(ConditionerUtil.getDevOne(oneReq));
+ }
+
+ System.out.println("\n=== getUser ===");
+ print(ConditionerUtil.getUser(session));
+ }
+
+ private static void print(Object obj) {
+ System.out.println(JSON.toJSONString(obj, true));
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/ConditionerUtil.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/ConditionerUtil.java
new file mode 100644
index 0000000..95dd398
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/ConditionerUtil.java
@@ -0,0 +1,553 @@
+package com.doumee.core.conditoner;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.doumee.core.conditoner.model.ConditionerConstant;
+import com.doumee.core.conditoner.model.request.*;
+import com.doumee.core.conditoner.model.response.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鏅虹簿鐏电┖璋冨钩鍙� HTTP 宸ュ叿绫汇��
+ * <p>
+ * 鍗忚锛欸ET 璇诲彇锛圦uery 鍙傛暟锛夛紱POST 鎺у埗/鍐欏叆锛圝SON Body锛夈��
+ * 鎴愬姛鏍囪瘑 {@code code=200}銆傜櫥褰曞悗鍏叡鍙傛暟 {@code kt_token}銆亄@code kt_dwid}銆亄@code kt_sonid}銆�
+ * </p>
+ */
+@Slf4j
+public class ConditionerUtil {
+
+ private static final int HTTP_RETRY = 3;
+
+ private ConditionerUtil() {
+ }
+
+ // ==================== 浜屻�佸熀纭�鎺у埗 ====================
+
+ public static ConditionerBaseResponse<LoginDataResponse> login() {
+ return login(null);
+ }
+
+ public static ConditionerBaseResponse<LoginDataResponse> login(LoginRequest req) {
+ LoginRequest r = req != null ? req : new LoginRequest();
+ JSONObject body = new JSONObject();
+ body.put("username", StringUtils.isNotBlank(r.getUsername()) ? r.getUsername() : ConditionerConstant.username);
+ body.put("password", StringUtils.isNotBlank(r.getPassword()) ? r.getPassword() : ConditionerConstant.password);
+ try {
+ String raw = doPostRaw("/login", body.toJSONString());
+ ConditionerBaseResponse<LoginDataResponse> resp = parseObjectResponse(raw, LoginDataResponse.class);
+ applyLoginSession(resp);
+ return resp;
+ } catch (Exception e) {
+ log.error("conditioner login failed", e);
+ return null;
+ }
+ }
+
+ public static ConditionerBaseResponse<List<DeviceStatusResponse>> getDevList(ConditionerSessionRequest req) {
+ return getList("/getDevList", req, DeviceStatusResponse.class);
+ }
+
+ public static ConditionerBaseResponse<DeviceStatusResponse> getDevOne(GetDevOneRequest req) {
+ if (req == null) {
+ return null;
+ }
+ req.fillSessionDefaults();
+ try {
+ String raw = doGetRaw("/getDevOne", toQueryMap(req));
+ return parseObjectResponse(raw, DeviceStatusResponse.class);
+ } catch (Exception e) {
+ log.error("conditioner getDevOne failed", e);
+ return null;
+ }
+ }
+
+ public static ConditionerBaseResponse<Object> devCtr(DevControlRequest req) {
+ return postJson("/devCtr", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> devManyCtr(DevManyControlRequest req) {
+ return postJson("/devManyCtr", req, Object.class);
+ }
+ public static ConditionerBaseResponse<Object> devLockManyCtr(DevLockControlRequest req) {
+ return postJson("/devManyCtr", req, Object.class);
+ }
+
+ // ==================== 涓夈�佽澶囩鐞� ====================
+
+ public static ConditionerBaseResponse<List<GatewayInfoResponse>> getWg(ConditionerSessionRequest req) {
+ return getList("/getWg", req, GatewayInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addWg(WgManageRequest req) {
+ return postJson("/addWg", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeWg(WgManageRequest req) {
+ return postJson("/changeWg", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delWg(WgManageRequest req) {
+ return postJson("/delWg", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> wgWithArea(WgWithAreaRequest req) {
+ return postJson("/wgWithArea", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<DeviceArchiveResponse>> getDev(ConditionerSessionRequest req) {
+ return getList("/getDev", req, DeviceArchiveResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addDev(DevManageRequest req) {
+ return postJson("/addDev", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeDev(DevManageRequest req) {
+ return postJson("/changeDev", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delDev(DevManageRequest req) {
+ return postJson("/delDev", req, Object.class);
+ }
+
+ // ==================== 鍥涖�佸尯鍩熺鐞� ====================
+
+ public static ConditionerBaseResponse<List<RoomInfoResponse>> getRoom(ConditionerSessionRequest req) {
+ return getList("/getRoom", req, RoomInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addRoom(RoomManageRequest req) {
+ return postJson("/addRoom", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeRoom(RoomManageRequest req) {
+ return postJson("/changeRoom", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delRoom(RoomManageRequest req) {
+ return postJson("/delRoom", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<FloorInfoResponse>> getFloor(ConditionerPageRequest req) {
+ return getList("/getFloor", req, FloorInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addFloor(FloorManageRequest req) {
+ return postJson("/addFloor", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeFloor(FloorManageRequest req) {
+ return postJson("/changeFloor", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delFloor(FloorManageRequest req) {
+ return postJson("/delFloor", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<UnitInfoResponse>> getUnit(ConditionerPageRequest req) {
+ return getList("/getUnit", req, UnitInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addUnit(UnitManageRequest req) {
+ return postJson("/addUnit", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeUnit(UnitManageRequest req) {
+ return postJson("/changeUnit", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delUnit(UnitManageRequest req) {
+ return postJson("/delUnit", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<BuildingInfoResponse>> getBuilding(ConditionerPageRequest req) {
+ return getList("/getBuilding", req, BuildingInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addBuilding(BuildingManageRequest req) {
+ return postJson("/addBuilding", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeBuilding(BuildingManageRequest req) {
+ return postJson("/changeBuilding", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delBuilding(BuildingManageRequest req) {
+ return postJson("/delBuilding", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<AreaInfoResponse>> getArea(ConditionerPageRequest req) {
+ return getList("/getArea", req, AreaInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addArea(AreaManageRequest req) {
+ return postJson("/addArea", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeArea(AreaManageRequest req) {
+ return postJson("/changeArea", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delArea(AreaManageRequest req) {
+ return postJson("/delArea", req, Object.class);
+ }
+
+ // ==================== 浜斻�佸畾鏃剁鐞� ====================
+
+ public static ConditionerBaseResponse<List<TimingInfoResponse>> getTiming(ConditionerSessionRequest req) {
+ return getList("/getTiming", req, TimingInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addTiming(TimingManageRequest req) {
+ return postJson("/addTiming", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeTiming(TimingManageRequest req) {
+ return postJson("/changeTiming", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delTiming(TimingManageRequest req) {
+ return postJson("/delTiming", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> timingWithArea(TimingWithAreaRequest req) {
+ return postJson("/timingWithArea", req, Object.class);
+ }
+
+ // ==================== 鍏�佹暟鎹煡璇� ====================
+
+ public static ConditionerBaseResponse<List<Object>> getLogWg(LogQueryRequest req) {
+ return getList("/getLogWg", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<Object>> getLogDev(LogQueryRequest req) {
+ return getList("/getLogDev", req, Object.class);
+ }
+
+ // ==================== 涓冦�佽閲忕鐞� ====================
+
+ public static ConditionerBaseResponse<List<DlSjXsResponse>> getDlSjXs(ConditionerSessionRequest req) {
+ return getList("/getDlSjXs", req, DlSjXsResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeDlSjXs(ChangeDlSjXsRequest req) {
+ return postJson("/changeDlSjXs", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<MeterDbInfoResponse>> getDb(MeterDbManageRequest req) {
+ return getList("/glDb/getDb", req, MeterDbInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addDb(MeterDbManageRequest req) {
+ return postJson("/glDb/addDb", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeDb(MeterDbManageRequest req) {
+ return postJson("/glDb/changeDb", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delDb(MeterDbManageRequest req) {
+ return postJson("/glDb/delDb", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<Object>> getDbDaySum(DbDaySumQueryRequest req) {
+ return getList("/getDbDaySum", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<Object>> getDayDl(DayDlQueryRequest req) {
+ return getList("/getDayDl", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<Object>> getMoonDl(MoonDlQueryRequest req) {
+ return getList("/getMoonDl", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<CompanyGsInfoResponse>> getGs(CompanyGsManageRequest req) {
+ return getList("/getGs", req, CompanyGsInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addGs(CompanyGsManageRequest req) {
+ return postJson("/addGs", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeGs(CompanyGsManageRequest req) {
+ return postJson("/changeGs", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delGs(CompanyGsManageRequest req) {
+ return postJson("/delGs", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> gsWithArea(GsWithAreaRequest req) {
+ return postJson("/gsWithArea", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addMoney(AddMoneyRequest req) {
+ return postJson("/addMoney", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> cleanMoney(CompanyGsManageRequest req) {
+ return postJson("/cleanMoney", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<List<Object>> getCzLog(LogQueryRequest req) {
+ return getList("/getCzLog", req, Object.class);
+ }
+
+ // ==================== 鍏�佽处鍙风鐞� ====================
+
+ public static ConditionerBaseResponse<List<UserInfoResponse>> getUser(ConditionerSessionRequest req) {
+ return getList("/getUser", req, UserInfoResponse.class);
+ }
+
+ public static ConditionerBaseResponse<Object> addUser(UserManageRequest req) {
+ return postJson("/addUser", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeUser(UserManageRequest req) {
+ return postJson("/changeUser", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> changeUserPwd(UserManageRequest req) {
+ return postJson("/changeUserPwd", req, Object.class);
+ }
+
+ public static ConditionerBaseResponse<Object> delUser(UserManageRequest req) {
+ return postJson("/delUser", req, Object.class);
+ }
+
+ // ==================== 浼氳瘽 ====================
+
+ public static void applyLoginSession(ConditionerBaseResponse<LoginDataResponse> resp) {
+ if (resp == null || !resp.isSuccess() || resp.getData() == null) {
+ return;
+ }
+ LoginDataResponse data = resp.getData();
+ if (StringUtils.isNotBlank(data.getKt_token())) {
+ ConditionerConstant.kt_token = data.getKt_token();
+ }
+ if (data.getKt_dwid() != null) {
+ ConditionerConstant.kt_dwid = String.valueOf(data.getKt_dwid());
+ }
+ if (data.getKt_sonid() != null) {
+ ConditionerConstant.kt_sonid = String.valueOf(data.getKt_sonid());
+ }
+ }
+
+ // ==================== HTTP / 瑙f瀽 ====================
+
+ private static <T> ConditionerBaseResponse<List<T>> getList(String path, ConditionerSessionRequest req, Class<T> itemClass) {
+ if (req != null) {
+ req.fillSessionDefaults();
+ } else {
+ req = new ConditionerSessionRequest();
+ req.fillSessionDefaults();
+ }
+ try {
+ String raw = doGetRaw(path, toQueryMap(req));
+ return parseListResponse(raw, itemClass);
+ } catch (Exception e) {
+ log.error("conditioner GET {} failed", path, e);
+ return null;
+ }
+ }
+
+ private static <T> ConditionerBaseResponse<T> postJson(String path, ConditionerSessionRequest req, Class<T> dataClass) {
+ if (req != null) {
+ req.fillSessionDefaults();
+ }
+ try {
+ String raw = doPostRaw(path, JSON.toJSONString(req));
+ return parseObjectResponse(raw, dataClass);
+ } catch (Exception e) {
+ log.error("conditioner POST {} failed", path, e);
+ return null;
+ }
+ }
+
+ private static String resolveUrl(String path) {
+ String base = StringUtils.defaultIfBlank(ConditionerConstant.base_url, ConditionerConstant.DEFAULT_BASE_URL);
+ if (base.endsWith("/")) {
+ base = base.substring(0, base.length() - 1);
+ }
+ if (!path.startsWith("/")) {
+ path = "/" + path;
+ }
+ return base + path;
+ }
+
+ private static String doGetRaw(String path, Map<String, String> params) throws Exception {
+ String url = buildGetUrl(path, params);
+ log.info("conditioner GET url={}", maskUrl(url));
+ HttpClient client = HttpClientBuilder.create().build();
+ HttpGet get = new HttpGet(url);
+ HttpResponse response = executeWithRetry(client, get);
+ String resp = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
+ log.info("conditioner GET response={}", abbreviate(resp));
+ return resp;
+ }
+
+ private static String doPostRaw(String path, String jsonBody) throws Exception {
+ String url = resolveUrl(path);
+ log.info("conditioner POST url={}, body={}", url, maskJson(jsonBody));
+ HttpClient client = HttpClientBuilder.create().build();
+ HttpPost post = new HttpPost(url);
+ post.setHeader("Content-Type", "application/json;charset=UTF-8");
+ post.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));
+ HttpResponse response = executeWithRetry(client, post);
+ String resp = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
+ log.info("conditioner POST response={}", abbreviate(resp));
+ return resp;
+ }
+
+ private static HttpResponse executeWithRetry(HttpClient client, org.apache.http.client.methods.HttpUriRequest request) throws Exception {
+ HttpResponse execute = null;
+ int retry = HTTP_RETRY;
+ while (retry-- > 0) {
+ try {
+ execute = client.execute(request);
+ break;
+ } catch (Exception e) {
+ if (retry <= 0) {
+ throw e;
+ }
+ Thread.sleep(5000);
+ }
+ }
+ if (execute == null) {
+ throw new Exception("conditioner http request failed");
+ }
+ return execute;
+ }
+
+ private static String buildGetUrl(String path, Map<String, String> params) throws Exception {
+ StringBuilder sb = new StringBuilder(resolveUrl(path));
+ if (params == null || params.isEmpty()) {
+ return sb.toString();
+ }
+ sb.append("?");
+ boolean first = true;
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ if (StringUtils.isBlank(entry.getValue())) {
+ continue;
+ }
+ if (!first) {
+ sb.append("&");
+ }
+ sb.append(urlEncode(entry.getKey())).append("=").append(urlEncode(entry.getValue()));
+ first = false;
+ }
+ return sb.toString();
+ }
+
+ private static Map<String, String> toQueryMap(Object obj) {
+ Map<String, String> map = new LinkedHashMap<>();
+ if (obj == null) {
+ return map;
+ }
+ JSONObject json = (JSONObject) JSON.toJSON(obj);
+ for (Map.Entry<String, Object> entry : json.entrySet()) {
+ Object val = entry.getValue();
+ if (val != null) {
+ map.put(entry.getKey(), String.valueOf(val));
+ }
+ }
+ return map;
+ }
+
+ private static <T> ConditionerBaseResponse<T> parseObjectResponse(String raw, Class<T> dataClass) {
+ if (StringUtils.isBlank(raw)) {
+ return null;
+ }
+ try {
+ JSONObject root = JSON.parseObject(raw);
+ ConditionerBaseResponse<T> resp = new ConditionerBaseResponse<>();
+ resp.setCode(root.getInteger("code"));
+ resp.setMessage(root.getString("message"));
+ Object data = root.get("data");
+ if (data != null && dataClass != null) {
+ if (data instanceof JSONObject) {
+ resp.setData(((JSONObject) data).toJavaObject(dataClass));
+ } else if (data instanceof String) {
+ String text = ((String) data).trim();
+ if (text.startsWith("{")) {
+ resp.setData(JSON.parseObject(text, dataClass));
+ }
+ } else if (!(data instanceof JSONArray)) {
+ resp.setData(JSON.parseObject(JSON.toJSONString(data), dataClass));
+ }
+ }
+ return resp;
+ } catch (Exception e) {
+ log.error("conditioner parse object response failed: {}", abbreviate(raw), e);
+ return null;
+ }
+ }
+
+ private static <T> ConditionerBaseResponse<List<T>> parseListResponse(String raw, Class<T> itemClass) {
+ if (StringUtils.isBlank(raw)) {
+ return null;
+ }
+ try {
+ JSONObject root = JSON.parseObject(raw);
+ ConditionerBaseResponse<List<T>> resp = new ConditionerBaseResponse<>();
+ resp.setCode(root.getInteger("code"));
+ resp.setMessage(root.getString("message"));
+ Object data = root.get("data");
+ if (data instanceof JSONArray) {
+ resp.setData(((JSONArray) data).toJavaList(itemClass));
+ } else if (data instanceof String) {
+ String text = ((String) data).trim();
+ if (text.startsWith("[")) {
+ resp.setData(JSON.parseArray(text, itemClass));
+ }
+ }
+ return resp;
+ } catch (Exception e) {
+ log.error("conditioner parse list response failed: {}", abbreviate(raw), e);
+ return null;
+ }
+ }
+
+ private static String urlEncode(String value) throws Exception {
+ return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
+ }
+
+ private static String maskUrl(String url) {
+ if (url == null) {
+ return null;
+ }
+ return url.replaceAll("(kt_token=)[^&]+", "$1***");
+ }
+
+ private static String maskJson(String json) {
+ if (json == null) {
+ return null;
+ }
+ return json.replaceAll("(\\\"password\\\"\\s*:\\s*\\\")[^\\\"]+(\\\")", "$1***$2")
+ .replaceAll("(\\\"kt_token\\\"\\s*:\\s*\\\")[^\\\"]+(\\\")", "$1***$2");
+ }
+
+ private static String abbreviate(String text) {
+ if (text == null) {
+ return null;
+ }
+ return text.length() > 500 ? text.substring(0, 500) + "..." : text;
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/ConditionerConstant.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/ConditionerConstant.java
new file mode 100644
index 0000000..c7b6df4
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/ConditionerConstant.java
@@ -0,0 +1,57 @@
+package com.doumee.core.conditoner.model;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 鏅虹簿鐏靛钩鍙拌繛鎺ヤ笌浼氳瘽鍙傛暟锛堝彲鐢� {@link com.doumee.service.business.impl.ConditionerConfigService} 浠庡瓧鍏稿姞杞斤級
+ */
+public class ConditionerConstant {
+
+ public static final String DEFAULT_BASE_URL = "http://119.45.163.5:1125/zjl/API";
+ public static final String DEFAULT_USERNAME = "admin";
+ public static final String DEFAULT_PASSWORD = "12345678";
+ public static final String DEFAULT_KT_SONID = "0";
+
+ /** API 鏍瑰湴鍧�锛屽 http://119.45.163.5:1125/zjl/API */
+ public static String base_url = DEFAULT_BASE_URL;
+ public static String username = DEFAULT_USERNAME;
+ public static String password = DEFAULT_PASSWORD;
+
+ /** 鐧诲綍鍚庝細璇濓紙login 鎴愬姛鍚庡啓鍏ワ級 */
+ public static String kt_token;
+ public static String kt_dwid;
+ public static String kt_sonid = DEFAULT_KT_SONID;
+
+ private ConditionerConstant() {
+ }
+
+ /**
+ * getDevList / getDevOne 璁惧 online锛�88銆�66 琛ㄧず鍦ㄧ嚎锛屽叾瀹冧负绂荤嚎銆�
+ */
+ public static String normalizeDeviceOnline(Object online) {
+ if (online == null) {
+ return "绂荤嚎";
+ }
+ String s = String.valueOf(online).trim();
+ if (StringUtils.isBlank(s) || "null".equalsIgnoreCase(s)) {
+ return "绂荤嚎";
+ }
+ if ("88".equals(s) || "66".equals(s) || "1".equals(s)
+ || "鍦ㄧ嚎".equalsIgnoreCase(s) || "online".equalsIgnoreCase(s)) {
+ return "鍦ㄧ嚎";
+ }
+ return "绂荤嚎";
+ }
+
+ /** 缃戝叧 wg_status锛堥�氬父涓轰腑鏂囥�屽湪绾裤��/銆岀绾裤�嶏級 */
+ public static String normalizeGatewayOnline(String status) {
+ if (StringUtils.isBlank(status)) {
+ return "绂荤嚎";
+ }
+ String s = status.trim();
+ if ("1".equals(s) || "鍦ㄧ嚎".equalsIgnoreCase(s) || "online".equalsIgnoreCase(s)) {
+ return "鍦ㄧ嚎";
+ }
+ return "绂荤嚎";
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/AddMoneyRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/AddMoneyRequest.java
new file mode 100644
index 0000000..a651454
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/AddMoneyRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class AddMoneyRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("鍏徃 ID")
+ private Integer id;
+
+ @ApiModelProperty("鍏呭�奸噾棰�")
+ private String cz_money;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/AreaManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/AreaManageRequest.java
new file mode 100644
index 0000000..f532869
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/AreaManageRequest.java
@@ -0,0 +1,19 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class AreaManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("灏忓尯 ID")
+ private Integer area_id;
+
+ @ApiModelProperty("灏忓尯鍚嶇О")
+ private String area_name;
+
+ @ApiModelProperty("澶囨敞")
+ private String area_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/BuildingManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/BuildingManageRequest.java
new file mode 100644
index 0000000..0385995
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/BuildingManageRequest.java
@@ -0,0 +1,22 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class BuildingManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("妤兼爧 ID")
+ private Integer building_id;
+
+ @ApiModelProperty("灏忓尯 ID")
+ private Integer area_id;
+
+ @ApiModelProperty("妤兼爧鍚嶇О")
+ private String building_name;
+
+ @ApiModelProperty("澶囨敞")
+ private String building_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ChangeDlSjXsRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ChangeDlSjXsRequest.java
new file mode 100644
index 0000000..0f49f82
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ChangeDlSjXsRequest.java
@@ -0,0 +1,24 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ChangeDlSjXsRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("璁惧 ID")
+ private Integer dev_id;
+
+ @ApiModelProperty("0 鏃堕暱 1 鑳借�� 2 鐢佃〃")
+ private Integer kw_type;
+
+ private BigDecimal fan_arg;
+ private BigDecimal fan_kw;
+ private BigDecimal hig_kw;
+ private BigDecimal mid_kw;
+ private BigDecimal low_kw;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/CompanyGsManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/CompanyGsManageRequest.java
new file mode 100644
index 0000000..a215ed7
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/CompanyGsManageRequest.java
@@ -0,0 +1,40 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyGsManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("鍏徃 ID")
+ private Integer id;
+
+ @ApiModelProperty("鍏徃鍚嶇О")
+ private String gs_name;
+
+ @ApiModelProperty("鍏呭�煎紑鍏�")
+ private Integer is_pwr;
+
+ @ApiModelProperty("澶滈棿涓嶅仠鏈�")
+ private Integer is_rest_stop;
+
+ @ApiModelProperty("鍓╀綑閲戦")
+ private Object left_money;
+
+ @ApiModelProperty("鏄惁鍋滄満")
+ private Integer is_stop;
+
+ @ApiModelProperty("璁惧鍒楄〃")
+ private List<Integer> li_dev;
+
+ @ApiModelProperty("鍒嗘憡姣斾緥")
+ private Map<String, Object> d_dev;
+
+ @ApiModelProperty("澶囨敞")
+ private String gs_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ConditionerPageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ConditionerPageRequest.java
new file mode 100644
index 0000000..d722cca
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ConditionerPageRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ConditionerPageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("椤电爜")
+ private Integer page;
+
+ @ApiModelProperty("姣忛〉鏉℃暟")
+ private Integer pageSize;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ConditionerSessionRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ConditionerSessionRequest.java
new file mode 100644
index 0000000..22ddda8
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/ConditionerSessionRequest.java
@@ -0,0 +1,39 @@
+package com.doumee.core.conditoner.model.request;
+
+import com.doumee.core.conditoner.model.ConditionerConstant;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+
+/**
+ * 鐧诲綍鍚庝細璇濆叕鍏卞弬鏁帮紙GET query / POST body锛�
+ */
+@Data
+public class ConditionerSessionRequest implements Serializable {
+
+ @ApiModelProperty("鐧诲綍 token")
+ private String kt_token;
+
+ @ApiModelProperty("鍏徃 ID")
+ private String kt_dwid;
+
+ @ApiModelProperty("瀛愯处鍙� ID锛岄粯璁� 0")
+ private String kt_sonid;
+
+ @ApiModelProperty("鍗曞厓 ID锛堥儴鍒嗘帴鍙i渶瑕侊級")
+ private String kt_unit;
+
+ public void fillSessionDefaults() {
+ if (StringUtils.isBlank(kt_token)) {
+ kt_token = ConditionerConstant.kt_token;
+ }
+ if (StringUtils.isBlank(kt_dwid)) {
+ kt_dwid = ConditionerConstant.kt_dwid;
+ }
+ if (StringUtils.isBlank(kt_sonid)) {
+ kt_sonid = ConditionerConstant.kt_sonid;
+ }
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DayDlQueryRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DayDlQueryRequest.java
new file mode 100644
index 0000000..9965fc5
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DayDlQueryRequest.java
@@ -0,0 +1,22 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DayDlQueryRequest extends ConditionerPageRequest {
+
+ @ApiModelProperty("寮�濮嬫棩鏈�")
+ private String start_time;
+
+ @ApiModelProperty("缁撴潫鏃ユ湡")
+ private String end_time;
+
+ @ApiModelProperty("鍏徃 ID 绛涢��")
+ private Integer gs_id;
+
+ @ApiModelProperty("璁惧 ID 绛涢��")
+ private Integer dev_id;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DbDaySumQueryRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DbDaySumQueryRequest.java
new file mode 100644
index 0000000..8bcf297
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DbDaySumQueryRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DbDaySumQueryRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("寮�濮嬫棩鏈�")
+ private String start_time;
+
+ @ApiModelProperty("缁撴潫鏃ユ湡")
+ private String end_time;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevControlRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevControlRequest.java
new file mode 100644
index 0000000..e65a314
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevControlRequest.java
@@ -0,0 +1,28 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DevControlRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("姘存満 sj銆佸鑱旀満 dlj")
+ private String pid;
+
+ @ApiModelProperty("缃戝叧 MAC")
+ private String wg_mac;
+
+ @ApiModelProperty("璁惧鍦板潃")
+ private String wg_qid;
+
+ @ApiModelProperty("set_one 鍗曟潯鎺у埗")
+ private String ctr_type;
+
+ @ApiModelProperty("pwr/mode/fan/temp/many_ctr/find_dn/find_power 绛�")
+ private String set_type;
+
+ @ApiModelProperty("璁剧疆鍊硷紝鍙负鏁板瓧鎴� JSON 瀵硅薄")
+ private Object set_val;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevLockControlRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevLockControlRequest.java
new file mode 100644
index 0000000..2462452
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevLockControlRequest.java
@@ -0,0 +1,19 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DevLockControlRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("set_many 澶氳澶囨帶鍒�")
+ private String ctr_type;
+
+ @ApiModelProperty("璁惧鎺у埗椤瑰垪琛�")
+ private List<DevLockManyControlItem> li_data;
+
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevLockManyControlItem.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevLockManyControlItem.java
new file mode 100644
index 0000000..e2aa5a7
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevLockManyControlItem.java
@@ -0,0 +1,25 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class DevLockManyControlItem implements Serializable {
+
+ @ApiModelProperty("缃戝叧 MAC")
+ private String wg_mac;
+
+ @ApiModelProperty("璁惧鍦板潃")
+ private String wg_qid;
+
+ @ApiModelProperty("姘存満 sj銆佸鑱旀満 dlj")
+ private String pid;
+
+ @ApiModelProperty("lock_many")
+ private String set_type;
+
+ @ApiModelProperty("璁剧疆鍊�")
+ private Object set_val;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManageRequest.java
new file mode 100644
index 0000000..20a1c19
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManageRequest.java
@@ -0,0 +1,34 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DevManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("璁惧 ID")
+ private Integer dev_id;
+
+ @ApiModelProperty("缃戝叧 ID")
+ private Integer wg_id;
+
+ @ApiModelProperty("璁惧鍦板潃")
+ private String wg_qid;
+
+ @ApiModelProperty("妤煎眰 ID")
+ private Integer floor_id;
+
+ @ApiModelProperty("鎴块棿 ID")
+ private Integer room_id;
+
+ @ApiModelProperty("璁惧鍚嶇О")
+ private String dev_name;
+
+ @ApiModelProperty("璁惧绫诲瀷 ID")
+ private Integer dev_type_id;
+
+ @ApiModelProperty("澶囨敞")
+ private String dev_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManyControlItem.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManyControlItem.java
new file mode 100644
index 0000000..500a38d
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManyControlItem.java
@@ -0,0 +1,25 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class DevManyControlItem implements Serializable {
+
+ @ApiModelProperty("缃戝叧 MAC")
+ private String wg_mac;
+
+ @ApiModelProperty("璁惧鍦板潃")
+ private String wg_qid;
+
+ @ApiModelProperty("姘存満 sj銆佸鑱旀満 dlj")
+ private String pid;
+
+ @ApiModelProperty("pwr/mode/fan/temp/many_ctr 绛�")
+ private String set_type;
+
+ @ApiModelProperty("璁剧疆鍊�")
+ private Object set_val;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManyControlRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManyControlRequest.java
new file mode 100644
index 0000000..e6eea05
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/DevManyControlRequest.java
@@ -0,0 +1,24 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DevManyControlRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("set_many 澶氳澶囨帶鍒�")
+ private String ctr_type;
+
+ @ApiModelProperty("璁惧鎺у埗椤瑰垪琛�")
+ private List<DevManyControlItem> li_data;
+
+ @ApiModelProperty("閿佸畾绛夊鍔熻兘鎺у埗鏃剁殑璁剧疆绫诲瀷")
+ private String set_type;
+
+ @ApiModelProperty("閿佸畾绛夋帶鍒舵椂鐨勮缃��")
+ private Object set_val;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/FloorManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/FloorManageRequest.java
new file mode 100644
index 0000000..270e29c
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/FloorManageRequest.java
@@ -0,0 +1,19 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FloorManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("妤煎眰 ID")
+ private Integer floor_id;
+
+ @ApiModelProperty("妤煎眰鍚嶇О")
+ private String floor_name;
+
+ @ApiModelProperty("澶囨敞")
+ private String floor_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/GetDevOneRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/GetDevOneRequest.java
new file mode 100644
index 0000000..c7dd6b3
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/GetDevOneRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class GetDevOneRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("缃戝叧 MAC")
+ private String wg_mac;
+
+ @ApiModelProperty("璁惧鍦板潃")
+ private String wg_qid;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/GsWithAreaRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/GsWithAreaRequest.java
new file mode 100644
index 0000000..388ba90
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/GsWithAreaRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class GsWithAreaRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("鍏徃 ID")
+ private Integer id;
+
+ @ApiModelProperty("鎴块棿 ID")
+ private Integer room_id;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/LogQueryRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/LogQueryRequest.java
new file mode 100644
index 0000000..f9c6e57
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/LogQueryRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LogQueryRequest extends ConditionerPageRequest {
+
+ @ApiModelProperty("寮�濮嬫棩鏈� yyyy-MM-dd")
+ private String start_time;
+
+ @ApiModelProperty("缁撴潫鏃ユ湡 yyyy-MM-dd")
+ private String end_time;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/LoginRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/LoginRequest.java
new file mode 100644
index 0000000..1e97fc4
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/LoginRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class LoginRequest implements Serializable {
+
+ @ApiModelProperty("鐢ㄦ埛鍚嶏紝榛樿鍙栧瓧鍏搁厤缃�")
+ private String username;
+
+ @ApiModelProperty("瀵嗙爜锛岄粯璁ゅ彇瀛楀吀閰嶇疆")
+ private String password;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/MeterDbManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/MeterDbManageRequest.java
new file mode 100644
index 0000000..32c89f2
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/MeterDbManageRequest.java
@@ -0,0 +1,42 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MeterDbManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("鐢佃〃 ID")
+ private Integer db_id;
+
+ @ApiModelProperty("缃戝叧 ID")
+ private Integer wg_id;
+
+ @ApiModelProperty("鍗忚 ID")
+ private Integer xy_id;
+
+ @ApiModelProperty("鐢佃〃鍦板潃")
+ private String db_adr;
+
+ @ApiModelProperty("鐢佃〃鍚嶇О")
+ private String db_name;
+
+ @ApiModelProperty("鍙樻瘮")
+ private Integer db_bb;
+
+ @ApiModelProperty("澶栨満鏃ヨ�楃數")
+ private Integer db_rhd;
+
+ @ApiModelProperty("澶囨敞")
+ private String db_bz;
+
+ @ApiModelProperty("鍏宠仈璁惧 ID 鍒楄〃")
+ private List<Integer> li_dev;
+
+ @ApiModelProperty("绛涢�� MAC")
+ private String db_mac;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/MoonDlQueryRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/MoonDlQueryRequest.java
new file mode 100644
index 0000000..ab497d2
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/MoonDlQueryRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MoonDlQueryRequest extends ConditionerPageRequest {
+
+ @ApiModelProperty("鏈堜唤 yyyy-MM")
+ private String date;
+
+ @ApiModelProperty("鍏徃 ID 绛涢��")
+ private Integer gs_id;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/RoomManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/RoomManageRequest.java
new file mode 100644
index 0000000..686e870
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/RoomManageRequest.java
@@ -0,0 +1,22 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class RoomManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("鎴块棿 ID")
+ private Integer room_id;
+
+ @ApiModelProperty("妤煎眰 ID")
+ private Integer floor_id;
+
+ @ApiModelProperty("鎴块棿鍚嶇О")
+ private String room_name;
+
+ @ApiModelProperty("澶囨敞")
+ private String room_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/TimingManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/TimingManageRequest.java
new file mode 100644
index 0000000..8866d05
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/TimingManageRequest.java
@@ -0,0 +1,46 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class TimingManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("瀹氭椂 ID")
+ private Integer id;
+
+ @ApiModelProperty("瀹氭椂鏃堕棿 HH:mm")
+ private String uptime;
+
+ @ApiModelProperty("璁惧 ID 鍒楄〃")
+ private List<Integer> li_devid;
+
+ @ApiModelProperty("瀹氭椂寮�鍏� 0/1")
+ private Integer time_pwr;
+
+ @ApiModelProperty("pwr/lock/many/scene")
+ private String time_type;
+
+ @ApiModelProperty("鍦烘櫙鍐呭")
+ private Map<String, Object> d_scene;
+
+ @ApiModelProperty("0 鍛ㄦ湡 1 鏃ユ湡")
+ private Integer date_type;
+
+ @ApiModelProperty("鍛ㄦ湡 0-6")
+ private String time_week;
+
+ @ApiModelProperty("鏃ユ湡 yyyy-MM-dd")
+ private String time_date;
+
+ @ApiModelProperty("澶囨敞")
+ private String time_bz;
+
+ private Integer sceneId;
+ private String sceneName;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/TimingWithAreaRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/TimingWithAreaRequest.java
new file mode 100644
index 0000000..c87c990
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/TimingWithAreaRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class TimingWithAreaRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("瀹氭椂 ID")
+ private Integer id;
+
+ @ApiModelProperty("鎴块棿 ID")
+ private Integer room_id;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/UnitManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/UnitManageRequest.java
new file mode 100644
index 0000000..eee23eb
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/UnitManageRequest.java
@@ -0,0 +1,22 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class UnitManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("鍗曞厓 ID")
+ private Integer unit_id;
+
+ @ApiModelProperty("妤兼爧 ID")
+ private Integer building_id;
+
+ @ApiModelProperty("鍗曞厓鍚嶇О")
+ private String unit_name;
+
+ @ApiModelProperty("澶囨敞")
+ private String unit_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/UserManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/UserManageRequest.java
new file mode 100644
index 0000000..715dedd
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/UserManageRequest.java
@@ -0,0 +1,31 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class UserManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("鐢ㄦ埛 ID")
+ private Integer user_id;
+
+ @ApiModelProperty("鐢ㄦ埛鍚�")
+ private String username;
+
+ @ApiModelProperty("瀵嗙爜")
+ private String password;
+
+ @ApiModelProperty("鏉冮檺")
+ private Integer kt_qx;
+
+ @ApiModelProperty("绛夌骇")
+ private Integer kt_level;
+
+ @ApiModelProperty("鍙帶鍒跺尯鍩�")
+ private String kt_area;
+
+ @ApiModelProperty("澶囨敞")
+ private String kt_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/WgManageRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/WgManageRequest.java
new file mode 100644
index 0000000..29e2c9d
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/WgManageRequest.java
@@ -0,0 +1,19 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WgManageRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("缃戝叧 ID")
+ private Integer wg_id;
+
+ @ApiModelProperty("缃戝叧 MAC")
+ private String wg_mac;
+
+ @ApiModelProperty("澶囨敞")
+ private String wg_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/WgWithAreaRequest.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/WgWithAreaRequest.java
new file mode 100644
index 0000000..d6144be
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/request/WgWithAreaRequest.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WgWithAreaRequest extends ConditionerSessionRequest {
+
+ @ApiModelProperty("缃戝叧 ID")
+ private Integer id;
+
+ @ApiModelProperty("鎴块棿 ID")
+ private Integer room_id;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/AreaInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/AreaInfoResponse.java
new file mode 100644
index 0000000..0a94ccc
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/AreaInfoResponse.java
@@ -0,0 +1,13 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class AreaInfoResponse implements Serializable {
+
+ private Integer area_id;
+ private String area_name;
+ private String area_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/BuildingInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/BuildingInfoResponse.java
new file mode 100644
index 0000000..5c9b599
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/BuildingInfoResponse.java
@@ -0,0 +1,14 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class BuildingInfoResponse implements Serializable {
+
+ private Integer building_id;
+ private Integer area_id;
+ private String building_name;
+ private String building_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/CompanyGsInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/CompanyGsInfoResponse.java
new file mode 100644
index 0000000..51daf01
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/CompanyGsInfoResponse.java
@@ -0,0 +1,23 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class CompanyGsInfoResponse implements Serializable {
+
+ private Integer id;
+ private String uptime;
+ private Integer is_pwr;
+ private Integer is_rest_stop;
+ private String gs_name;
+ private Object left_money;
+ private Object left_money_y;
+ private Integer is_stop;
+ private List<Integer> li_dev;
+ private Map<String, Object> d_dev;
+ private String gs_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/ConditionerBaseResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/ConditionerBaseResponse.java
new file mode 100644
index 0000000..0697ce5
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/ConditionerBaseResponse.java
@@ -0,0 +1,28 @@
+package com.doumee.core.conditoner.model.response;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 鏅虹簿鐏� API 缁熶竴鍝嶅簲锛歝ode=200 琛ㄧず鎴愬姛
+ */
+@ApiModel("鏅虹簿鐏靛搷搴斿璞�")
+@Data
+public class ConditionerBaseResponse<T> implements Serializable {
+
+ @ApiModelProperty("200 鎴愬姛锛�500 绛夎〃绀哄け璐�")
+ private Integer code;
+
+ @ApiModelProperty("鎻愮ず淇℃伅")
+ private String message;
+
+ @ApiModelProperty("涓氬姟鏁版嵁")
+ private T data;
+
+ public boolean isSuccess() {
+ return code != null && code == 200;
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DeviceArchiveResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DeviceArchiveResponse.java
new file mode 100644
index 0000000..eb5c2d9
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DeviceArchiveResponse.java
@@ -0,0 +1,22 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class DeviceArchiveResponse implements Serializable {
+
+ private String wg_mac;
+ private String floor_name;
+ private String room_name;
+ private String dev_type_name;
+ private Integer dev_id;
+ private Integer floor_id;
+ private Integer room_id;
+ private Integer wg_id;
+ private String wg_qid;
+ private String dev_name;
+ private String dev_bz;
+ private Integer dev_type_id;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DeviceStatusResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DeviceStatusResponse.java
new file mode 100644
index 0000000..afa4739
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DeviceStatusResponse.java
@@ -0,0 +1,40 @@
+package com.doumee.core.conditoner.model.response;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class DeviceStatusResponse implements Serializable {
+
+ private Integer dev_id;
+ private String online;
+ private String xh;
+ private Integer kt_lock;
+ private Integer pwr;
+ private Integer mode;
+ private Integer fan;
+ private Integer fan_set;
+ private Integer sf_pwr;
+ private Integer temp;
+ private Integer temp_set;
+ private Integer stop_logo;
+ private String sum_runtime;
+ private String hig_runtime;
+ private String mid_runtime;
+ private String low_runtime;
+ private Integer wg_id;
+ private String wg_mac;
+ private String wg_qid;
+ private String dev_name;
+ private Integer floor_id;
+ private String floor_name;
+ private Integer room_id;
+ private String room_name;
+ private Integer dev_type_id;
+ private String dev_type_name;
+ private String uptime;
+ @ApiModelProperty("姘存満 sj銆佸鑱旀満 dlj")
+ private String pid;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DlSjXsResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DlSjXsResponse.java
new file mode 100644
index 0000000..2a20b2c
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/DlSjXsResponse.java
@@ -0,0 +1,22 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+public class DlSjXsResponse implements Serializable {
+
+ private Integer dev_id;
+ private BigDecimal kt_dj;
+ private Integer kw_type;
+ private BigDecimal hig_kw;
+ private BigDecimal mid_kw;
+ private BigDecimal low_kw;
+ private BigDecimal fan_arg;
+ private BigDecimal fan_kw;
+ private BigDecimal cold_kw;
+ private BigDecimal heat_kw;
+ private BigDecimal day_kw;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/FloorInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/FloorInfoResponse.java
new file mode 100644
index 0000000..fb15330
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/FloorInfoResponse.java
@@ -0,0 +1,13 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class FloorInfoResponse implements Serializable {
+
+ private Integer floor_id;
+ private String floor_name;
+ private String floor_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/GatewayInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/GatewayInfoResponse.java
new file mode 100644
index 0000000..9d48314
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/GatewayInfoResponse.java
@@ -0,0 +1,16 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Map;
+
+@Data
+public class GatewayInfoResponse implements Serializable {
+
+ private Integer wg_id;
+ private String wg_mac;
+ private String wg_bz;
+ private String wg_status;
+ private Map<String, Object> area;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/LoginDataResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/LoginDataResponse.java
new file mode 100644
index 0000000..ab0787f
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/LoginDataResponse.java
@@ -0,0 +1,25 @@
+package com.doumee.core.conditoner.model.response;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class LoginDataResponse implements Serializable {
+
+ @ApiModelProperty("鐧诲綍 token")
+ private String kt_token;
+
+ @ApiModelProperty("鍏徃 ID")
+ private Object kt_dwid;
+
+ @ApiModelProperty("瀛愯处鍙� ID")
+ private Object kt_sonid;
+
+ @ApiModelProperty("鐢ㄦ埛鍚�")
+ private String username;
+
+ @ApiModelProperty("鐢ㄦ埛 ID")
+ private Integer user_id;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/MeterDbInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/MeterDbInfoResponse.java
new file mode 100644
index 0000000..4ecb48a
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/MeterDbInfoResponse.java
@@ -0,0 +1,24 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class MeterDbInfoResponse implements Serializable {
+
+ private Integer db_id;
+ private String wg_mac;
+ private Integer wg_id;
+ private String xy_name;
+ private Integer xy_id;
+ private String db_adr;
+ private String db_name;
+ private Integer db_bb;
+ private Integer db_rhd;
+ private List<Integer> li_dev;
+ private String db_bz;
+ private Object db_data;
+ private String db_uptime;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/RoomInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/RoomInfoResponse.java
new file mode 100644
index 0000000..2946218
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/RoomInfoResponse.java
@@ -0,0 +1,14 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class RoomInfoResponse implements Serializable {
+
+ private Integer room_id;
+ private Integer floor_id;
+ private String room_name;
+ private String room_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/TimingInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/TimingInfoResponse.java
new file mode 100644
index 0000000..e061142
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/TimingInfoResponse.java
@@ -0,0 +1,20 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class TimingInfoResponse implements Serializable {
+
+ private Integer id;
+ private Integer time_pwr;
+ private String time_week;
+ private String time_type;
+ private String uptime;
+ private List<Integer> li_devid;
+ private Map<String, Object> d_scene;
+ private String time_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/UnitInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/UnitInfoResponse.java
new file mode 100644
index 0000000..bfe7fbc
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/UnitInfoResponse.java
@@ -0,0 +1,14 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class UnitInfoResponse implements Serializable {
+
+ private Integer unit_id;
+ private Integer building_id;
+ private String unit_name;
+ private String unit_bz;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/UserInfoResponse.java b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/UserInfoResponse.java
new file mode 100644
index 0000000..7bdbe83
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/core/conditoner/model/response/UserInfoResponse.java
@@ -0,0 +1,23 @@
+package com.doumee.core.conditoner.model.response;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class UserInfoResponse implements Serializable {
+
+ private Integer user_id;
+ private String kt_token;
+ private Integer kt_dwid;
+ private String username;
+ private Integer kt_qx;
+ private Integer kt_level;
+ private Integer kt_sonid;
+ private String kt_area;
+ private Integer cz_pwr;
+ private String gs_name;
+ private String kt_bz;
+ private String end_time;
+ private String kt_unit;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerActionsMapper.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerActionsMapper.java
new file mode 100644
index 0000000..d80f148
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerActionsMapper.java
@@ -0,0 +1,7 @@
+package com.doumee.dao.business;
+
+import com.doumee.dao.business.model.YwConditionerActions;
+import com.github.yulichang.base.MPJBaseMapper;
+
+public interface YwConditionerActionsMapper extends MPJBaseMapper<YwConditionerActions> {
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerBillingMapper.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerBillingMapper.java
new file mode 100644
index 0000000..485eda7
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerBillingMapper.java
@@ -0,0 +1,7 @@
+package com.doumee.dao.business;
+
+import com.doumee.dao.business.model.YwConditionerBilling;
+import com.github.yulichang.base.MPJBaseMapper;
+
+public interface YwConditionerBillingMapper extends MPJBaseMapper<YwConditionerBilling> {
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerGatewayLogMapper.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerGatewayLogMapper.java
new file mode 100644
index 0000000..5642c3b
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerGatewayLogMapper.java
@@ -0,0 +1,7 @@
+package com.doumee.dao.business;
+
+import com.doumee.dao.business.model.YwConditionerGatewayLog;
+import com.github.yulichang.base.MPJBaseMapper;
+
+public interface YwConditionerGatewayLogMapper extends MPJBaseMapper<YwConditionerGatewayLog> {
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerGatewayMapper.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerGatewayMapper.java
new file mode 100644
index 0000000..b1c24e2
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerGatewayMapper.java
@@ -0,0 +1,7 @@
+package com.doumee.dao.business;
+
+import com.doumee.dao.business.model.YwConditionerGateway;
+import com.github.yulichang.base.MPJBaseMapper;
+
+public interface YwConditionerGatewayMapper extends MPJBaseMapper<YwConditionerGateway> {
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerMeterMapper.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerMeterMapper.java
new file mode 100644
index 0000000..c1995a4
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerMeterMapper.java
@@ -0,0 +1,7 @@
+package com.doumee.dao.business;
+
+import com.doumee.dao.business.model.YwConditionerMeter;
+import com.github.yulichang.base.MPJBaseMapper;
+
+public interface YwConditionerMeterMapper extends MPJBaseMapper<YwConditionerMeter> {
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerUsageMapper.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerUsageMapper.java
new file mode 100644
index 0000000..f1543d8
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/YwConditionerUsageMapper.java
@@ -0,0 +1,7 @@
+package com.doumee.dao.business;
+
+import com.doumee.dao.business.model.YwConditionerUsage;
+import com.github.yulichang.base.MPJBaseMapper;
+
+public interface YwConditionerUsageMapper extends MPJBaseMapper<YwConditionerUsage> {
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerLockDTO.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerLockDTO.java
new file mode 100644
index 0000000..b8fd0bc
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerLockDTO.java
@@ -0,0 +1,22 @@
+package com.doumee.dao.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class YwConditionerLockDTO {
+
+ @ApiModelProperty("鍐呮満涓婚敭")
+ private Integer id;
+
+ @ApiModelProperty("鏄惁閿佸畾寮�鍏� -1涓嶉攣 0鍏� 1寮�")
+ private Integer lockPwr;
+ private Integer pwr;
+ private Integer mode;
+ private Integer fan;
+ private Integer minTemp;
+ private Integer maxTemp;
+
+ @ApiModelProperty("鎿嶄綔鏉ユ簮")
+ private String source;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerOperateDTO.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerOperateDTO.java
new file mode 100644
index 0000000..e4a3a54
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerOperateDTO.java
@@ -0,0 +1,20 @@
+package com.doumee.dao.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class YwConditionerOperateDTO {
+
+ @ApiModelProperty("鍐呮満涓婚敭")
+ private Integer id;
+
+ @ApiModelProperty("1寮�鍏�2妯″紡3椋庨��4娓╁害")
+ private Integer actionType;
+
+ @ApiModelProperty("璁剧疆鍊�")
+ private Object setVal;
+
+ @ApiModelProperty("鎿嶄綔鏉ユ簮")
+ private String source;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerReportQueryDTO.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerReportQueryDTO.java
new file mode 100644
index 0000000..189b85c
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerReportQueryDTO.java
@@ -0,0 +1,29 @@
+package com.doumee.dao.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class YwConditionerReportQueryDTO {
+
+ @ApiModelProperty("鎶ヨ〃绫诲瀷锛歞ay/month")
+ private String reportType;
+
+ @ApiModelProperty("鏈堜唤 yyyy-MM")
+ private String month;
+
+ @ApiModelProperty("鏃ユ姤琛ㄥ紑濮嬫棩鏈� yyyy-MM-dd")
+ private String startTime;
+
+ @ApiModelProperty("鏃ユ姤琛ㄧ粨鏉熸棩鏈� yyyy-MM-dd")
+ private String endTime;
+
+ @ApiModelProperty("鍏徃ID")
+ private Integer gsId;
+
+ @ApiModelProperty("璁惧鍏抽敭瀛�")
+ private String devKeyword;
+
+ private Integer page;
+ private Integer capacity;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerUsageReportPageDTO.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerUsageReportPageDTO.java
new file mode 100644
index 0000000..2ae90da
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerUsageReportPageDTO.java
@@ -0,0 +1,16 @@
+package com.doumee.dao.business.dto;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class YwConditionerUsageReportPageDTO {
+
+ private List<String> dateColumns = new ArrayList<>();
+ private List<YwConditionerUsageReportVO> records = new ArrayList<>();
+ private long total;
+ private int page;
+ private int capacity;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerUsageReportVO.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerUsageReportVO.java
new file mode 100644
index 0000000..2662789
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerUsageReportVO.java
@@ -0,0 +1,27 @@
+package com.doumee.dao.business.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Data
+public class YwConditionerUsageReportVO {
+
+ private Integer devId;
+ private String devName;
+ private String floorName;
+ private String roomName;
+ private BigDecimal totalTime;
+ private BigDecimal totalDl;
+ private BigDecimal totalDf;
+ private Map<String, YwConditionerUsageDailyVO> daily = new LinkedHashMap<>();
+
+ @Data
+ public static class YwConditionerUsageDailyVO {
+ private BigDecimal time;
+ private BigDecimal dl;
+ private BigDecimal df;
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditioner.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditioner.java
index a36ddc0..21b9db0 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditioner.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditioner.java
@@ -5,6 +5,7 @@
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -81,4 +82,57 @@
@ApiModelProperty(value = "绱鐢ㄧ數閲戦")
@ExcelColumn(name = "绱鐢ㄧ數閲戦")
private BigDecimal useAmount;
+
+ @ApiModelProperty("骞冲彴璁惧ID")
+ private Integer platformDevId;
+ private Integer wgId;
+ private String wgMac;
+
+ @TableField(exist = false)
+ @ApiModelProperty("缃戝叧澶囨敞锛堝叧鑱旂綉鍏宠〃锛�")
+ private String wgBz;
+
+ private String wgQid;
+ @ApiModelProperty("姘存満 sj銆佸鑱旀満 dlj")
+ private String pid;
+ @ApiModelProperty("鍦ㄧ嚎/绂荤嚎")
+ private String online;
+ private Integer pwr;
+ private Integer mode;
+ private Integer fan;
+ private Integer fanSet;
+ private Integer temp;
+ private Integer tempSet;
+ private Integer ktLock;
+ private Integer stopLogo;
+ private String uptime;
+ private Integer floorId;
+ private String floorName;
+ private Integer roomId;
+ private String roomName;
+ private Integer devTypeId;
+ private String devTypeName;
+ private Integer lockPwr;
+ private Integer lockMode;
+ private Integer lockFan;
+ private Integer lockMinTemp;
+ private Integer lockMaxTemp;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date lastSyncDate;
+
+ @TableField(exist = false)
+ @ApiModelProperty("鍗$墖绛涢��-璁惧鍚嶇О/缂栧彿")
+ private String devKeyword;
+
+ @TableField(exist = false)
+ @ApiModelProperty("鍗$墖绛涢��-鍦ㄧ嚎鐘舵��")
+ private String onlineFilter;
+
+ @TableField(exist = false)
+ @ApiModelProperty("鍗$墖绛涢��-寮�鍏崇姸鎬侊細1寮�鏈�0鍏虫満")
+ private Integer pwrFilter;
+
+ @TableField(exist = false)
+ @ApiModelProperty("鍗$墖绛涢��-缃戝叧MAC")
+ private String wgMacFilter;
}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerActions.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerActions.java
new file mode 100644
index 0000000..e57f2c5
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerActions.java
@@ -0,0 +1,57 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.doumee.core.model.LoginUserModel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ApiModel("绌鸿皟璁惧鎺у埗璁板綍")
+@TableName("yw_conditioner_actions")
+public class YwConditionerActions extends LoginUserModel {
+
+ @TableId(type = IdType.AUTO)
+ private Integer id;
+ private Integer creator;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date createDate;
+ private Integer editor;
+ private Date editDate;
+ private Integer isdeleted;
+ private String remark;
+
+ private Integer conditionerId;
+ private Integer platformDevId;
+ private String devName;
+ private String wgMac;
+ @ApiModelProperty("1寮�鍏�2妯″紡3椋庨��4娓╁害5閿佸畾6鏌ョ數閲�7鏌ュ姛鐜�")
+ private Integer actionType;
+ private String actionContent;
+ @ApiModelProperty("0澶辫触1鎴愬姛")
+ private Integer resultStatus;
+ private String resultMsg;
+ private String source;
+ private String requestBody;
+ private String responseBody;
+
+ @TableField(exist = false)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date operateTimeBegin;
+
+ @TableField(exist = false)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date operateTimeEnd;
+
+ @TableField(exist = false)
+ private String devKeyword;
+
+ @TableField(exist = false)
+ private String onlineFilter;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerBilling.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerBilling.java
new file mode 100644
index 0000000..553bd41
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerBilling.java
@@ -0,0 +1,48 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.doumee.core.model.LoginUserModel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@ApiModel("鏅虹簿鐏佃璐圭郴鏁伴暅鍍�")
+@TableName("yw_conditioner_billing")
+public class YwConditionerBilling extends LoginUserModel {
+
+ @TableId(type = IdType.AUTO)
+ private Integer id;
+ private Integer creator;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date createDate;
+ private Integer editor;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date editDate;
+ private Integer isdeleted;
+ private String remark;
+
+ private Integer platformDevId;
+ private String devName;
+ private String wgMac;
+ @ApiModelProperty("0鏃堕暱1鑳借��2鐢佃〃")
+ private Integer kwType;
+ private BigDecimal fanArg;
+ private BigDecimal fanKw;
+ private BigDecimal higKw;
+ private BigDecimal midKw;
+ private BigDecimal lowKw;
+ private BigDecimal ktDj;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date lastSyncDate;
+
+ @TableField(exist = false)
+ private String devKeyword;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerGateway.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerGateway.java
new file mode 100644
index 0000000..023dae9
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerGateway.java
@@ -0,0 +1,45 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.doumee.core.model.LoginUserModel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ApiModel("鏅虹簿鐏电綉鍏抽暅鍍�")
+@TableName("yw_conditioner_gateway")
+public class YwConditionerGateway extends LoginUserModel {
+
+ @TableId(type = IdType.AUTO)
+ private Integer id;
+ private Integer creator;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date createDate;
+ private Integer editor;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date editDate;
+ private Integer isdeleted;
+ private String remark;
+
+ @ApiModelProperty("骞冲彴缃戝叧ID")
+ private Integer platformWgId;
+ @ApiModelProperty("缃戝叧MAC")
+ private String wgMac;
+ @ApiModelProperty("澶囨敞")
+ private String wgBz;
+ @ApiModelProperty("鍦ㄧ嚎/绂荤嚎")
+ private String onlineStatus;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date lastSyncDate;
+
+ @TableField(exist = false)
+ @ApiModelProperty("鍏抽敭瀛�(MAC/澶囨敞)")
+ private String keyword;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerGatewayLog.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerGatewayLog.java
new file mode 100644
index 0000000..4c2271d
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerGatewayLog.java
@@ -0,0 +1,46 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.doumee.core.model.LoginUserModel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ApiModel("缃戝叧涓婁笅绾胯褰�")
+@TableName("yw_conditioner_gateway_log")
+public class YwConditionerGatewayLog extends LoginUserModel {
+
+ @TableId(type = IdType.AUTO)
+ private Integer id;
+ private Integer creator;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date createDate;
+ private Integer editor;
+ private Date editDate;
+ private Integer isdeleted;
+ private String remark;
+
+ private Integer gatewayId;
+ private String wgMac;
+ private String oldStatus;
+ private String newStatus;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date logTime;
+ @ApiModelProperty("manual/schedule")
+ private String source;
+
+ @TableField(exist = false)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date logTimeBegin;
+
+ @TableField(exist = false)
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date logTimeEnd;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerMeter.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerMeter.java
new file mode 100644
index 0000000..2221bb7
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerMeter.java
@@ -0,0 +1,58 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.doumee.core.model.LoginUserModel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@ApiModel("鏅虹簿鐏电數琛ㄩ暅鍍�")
+@TableName("yw_conditioner_meter")
+public class YwConditionerMeter extends LoginUserModel {
+
+ @TableId(type = IdType.AUTO)
+ private Integer id;
+ private Integer creator;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date createDate;
+ private Integer editor;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date editDate;
+ private Integer isdeleted;
+ private String remark;
+
+ private Integer platformDbId;
+ private String dbName;
+ private String dbAdr;
+ private String wgMac;
+ private Integer wgId;
+ private Integer xyId;
+ private String xyName;
+ @ApiModelProperty("鍙樻瘮")
+ private Integer dbBb;
+ @ApiModelProperty("寰呮満鍒嗘憡")
+ private String standbyShare;
+ @ApiModelProperty("澶栨満鍥炶矾鍙�")
+ private Integer outdoorLoop;
+ private BigDecimal powerKw;
+ private BigDecimal totalDl;
+ private String dbData;
+ private String dbUptime;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date lastSyncDate;
+
+ @TableField(exist = false)
+ @ApiModelProperty("璁惧淇℃伅鍏抽敭瀛�")
+ private String keyword;
+
+ @TableField(exist = false)
+ private String wgMacFilter;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerUsage.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerUsage.java
new file mode 100644
index 0000000..2e31c2d
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditionerUsage.java
@@ -0,0 +1,39 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.doumee.core.model.LoginUserModel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@ApiModel("璁惧鏃ョ敤閲忛暅鍍�")
+@TableName("yw_conditioner_usage")
+public class YwConditionerUsage extends LoginUserModel {
+
+ @TableId(type = IdType.AUTO)
+ private Integer id;
+ private Integer creator;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date createDate;
+ private Integer editor;
+ private Date editDate;
+ private Integer isdeleted;
+ private String remark;
+
+ private Integer platformDevId;
+ private String devName;
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ private Date usageDate;
+ private BigDecimal sumTime;
+ private BigDecimal sumDl;
+ private BigDecimal sumDf;
+ private Integer gsId;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date syncDate;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/ConditionerBizService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/ConditionerBizService.java
new file mode 100644
index 0000000..3ed7fe2
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/ConditionerBizService.java
@@ -0,0 +1,49 @@
+package com.doumee.service.business;
+
+import com.doumee.core.model.LoginUserInfo;
+import com.doumee.dao.business.dto.YwConditionerLockDTO;
+import com.doumee.dao.business.dto.YwConditionerOperateDTO;
+import com.doumee.dao.business.dto.YwConditionerReportQueryDTO;
+import com.doumee.dao.business.dto.YwConditionerUsageReportPageDTO;
+import com.doumee.dao.business.model.YwConditioner;
+import com.doumee.dao.business.model.YwConditionerActions;
+import com.doumee.dao.business.model.YwConditionerMeter;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ConditionerBizService {
+
+ void ensureLogin();
+
+ String syncGateways(String source);
+
+ String syncGatewayStatus();
+
+ String syncMeters();
+
+ String syncBilling();
+
+ String syncIndoorUnits();
+
+ String syncUsage(YwConditionerReportQueryDTO query);
+
+ /** 瀹氭椂浠诲姟锛氬悓姝ュ墠涓�鏃ョ敤閲忔姤琛ㄦ暟鎹� */
+ String syncUsagePreviousDay();
+
+ String operate(YwConditionerOperateDTO dto, LoginUserInfo user);
+
+ String lock(YwConditionerLockDTO dto, LoginUserInfo user);
+
+ String queryMeterEnergy(Integer meterId, LoginUserInfo user);
+
+ String queryMeterPower(Integer meterId, LoginUserInfo user);
+
+ void saveAction(YwConditionerActions action, LoginUserInfo user);
+
+ YwConditionerUsageReportPageDTO queryUsageReport(YwConditionerReportQueryDTO query);
+
+ List<Map<String, Object>> gatewayOptions();
+
+ String syncAll();
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerActionsService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerActionsService.java
new file mode 100644
index 0000000..634e647
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerActionsService.java
@@ -0,0 +1,10 @@
+package com.doumee.service.business;
+
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.model.YwConditionerActions;
+
+public interface YwConditionerActionsService {
+
+ PageData<YwConditionerActions> findPage(PageWrap<YwConditionerActions> pageWrap);
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerBillingService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerBillingService.java
new file mode 100644
index 0000000..8b1923d
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerBillingService.java
@@ -0,0 +1,12 @@
+package com.doumee.service.business;
+
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.model.YwConditionerBilling;
+
+public interface YwConditionerBillingService {
+
+ PageData<YwConditionerBilling> findPage(PageWrap<YwConditionerBilling> pageWrap);
+
+ String syncAll();
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerGatewayService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerGatewayService.java
new file mode 100644
index 0000000..1c2ebb3
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerGatewayService.java
@@ -0,0 +1,15 @@
+package com.doumee.service.business;
+
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.model.YwConditionerGateway;
+import com.doumee.dao.business.model.YwConditionerGatewayLog;
+
+public interface YwConditionerGatewayService {
+
+ PageData<YwConditionerGateway> findPage(PageWrap<YwConditionerGateway> pageWrap);
+
+ PageData<YwConditionerGatewayLog> gatewayLogPage(PageWrap<YwConditionerGatewayLog> pageWrap);
+
+ String syncAll();
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerMeterService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerMeterService.java
new file mode 100644
index 0000000..99b4f67
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerMeterService.java
@@ -0,0 +1,17 @@
+package com.doumee.service.business;
+
+import com.doumee.core.model.LoginUserInfo;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.model.YwConditionerMeter;
+
+public interface YwConditionerMeterService {
+
+ PageData<YwConditionerMeter> findPage(PageWrap<YwConditionerMeter> pageWrap);
+
+ String syncAll();
+
+ String queryEnergy(Integer meterId, LoginUserInfo user);
+
+ String queryPower(Integer meterId, LoginUserInfo user);
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerReportService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerReportService.java
new file mode 100644
index 0000000..ce739a1
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerReportService.java
@@ -0,0 +1,20 @@
+package com.doumee.service.business;
+
+import com.doumee.dao.business.dto.YwConditionerReportQueryDTO;
+import com.doumee.dao.business.dto.YwConditionerUsageReportPageDTO;
+import com.doumee.dao.business.dto.YwConditionerUsageReportVO;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
+
+public interface YwConditionerReportService {
+
+ YwConditionerUsageReportPageDTO findPage(YwConditionerReportQueryDTO query);
+
+ String syncUsage(YwConditionerReportQueryDTO query);
+
+ List<Map<String, Object>> merchantOptions();
+
+ void exportExcel(YwConditionerReportQueryDTO query, HttpServletResponse response);
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerService.java
index 0714f90..682e7e0 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerService.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerService.java
@@ -3,8 +3,13 @@
import com.doumee.core.model.LoginUserInfo;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.dto.YwConditionerLockDTO;
+import com.doumee.dao.business.dto.YwConditionerOperateDTO;
import com.doumee.dao.business.model.YwConditioner;
+import com.doumee.dao.business.model.YwConditionerActions;
+
import java.util.List;
+import java.util.Map;
/**
* 绌鸿皟璁惧淇℃伅Service瀹氫箟
@@ -18,4 +23,18 @@
void updateById(YwConditioner ywConditioner);
YwConditioner findById(Integer id);
PageData<YwConditioner> findPage(PageWrap<YwConditioner> pageWrap);
+
+ PageData<YwConditioner> findCardPage(PageWrap<YwConditioner> pageWrap);
+
+ String syncAll();
+
+ String syncDevicesAndStatus();
+
+ String operate(YwConditionerOperateDTO dto, LoginUserInfo user);
+
+ String lock(YwConditionerLockDTO dto, LoginUserInfo user);
+
+ PageData<YwConditionerActions> historyPage(PageWrap<YwConditionerActions> pageWrap);
+
+ List<Map<String, Object>> gatewayOptions();
}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/ConditionerBizServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/ConditionerBizServiceImpl.java
new file mode 100644
index 0000000..5e2c44f
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/ConditionerBizServiceImpl.java
@@ -0,0 +1,1031 @@
+package com.doumee.service.business.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.doumee.core.conditoner.ConditionerUtil;
+import com.doumee.core.conditoner.model.ConditionerConstant;
+import com.doumee.core.conditoner.model.request.*;
+import com.doumee.core.conditoner.model.response.*;
+import com.doumee.core.constants.ResponseStatus;
+import com.doumee.core.exception.BusinessException;
+import com.doumee.core.model.LoginUserInfo;
+import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.*;
+import com.doumee.dao.business.dto.*;
+import com.doumee.dao.business.model.*;
+import com.doumee.service.business.ConditionerBizService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+@Service
+public class ConditionerBizServiceImpl implements ConditionerBizService {
+
+ public static final int ACTION_PWR = 1;
+ public static final int ACTION_MODE = 2;
+ public static final int ACTION_FAN = 3;
+ public static final int ACTION_TEMP = 4;
+ public static final int ACTION_LOCK = 5;
+ public static final int ACTION_QUERY_DL = 6;
+ public static final int ACTION_QUERY_POWER = 7;
+
+ private static final String PID_DLJ = "dlj";
+ private static final String LOCK_SET_TYPE = "lock_many";
+ private static final long ADJUST_OPERATE_INTERVAL_MS = 2000L;
+ private static volatile boolean syncing = false;
+ /** 鍚屼竴鍐呮満锛氭俯搴�/椋庨��/妯″紡鎿嶄綔鏈�灏忛棿闅旓紙姣锛� */
+ private static final Map<Integer, Long> LAST_ADJUST_OPERATE_MS = new ConcurrentHashMap<>();
+
+ @Autowired
+ private YwConditionerGatewayMapper gatewayMapper;
+ @Autowired
+ private YwConditionerGatewayLogMapper gatewayLogMapper;
+ @Autowired
+ private YwConditionerMeterMapper meterMapper;
+ @Autowired
+ private YwConditionerBillingMapper billingMapper;
+ @Autowired
+ private YwConditionerMapper conditionerMapper;
+ @Autowired
+ private YwConditionerActionsMapper actionsMapper;
+ @Autowired
+ private YwConditionerUsageMapper usageMapper;
+
+ @Override
+ public void ensureLogin() {
+ if (StringUtils.isNotBlank(ConditionerConstant.kt_token)) {
+ return;
+ }
+ ConditionerBaseResponse<LoginDataResponse> resp = ConditionerUtil.login();
+ if (resp == null || !resp.isSuccess()) {
+ throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(),
+ resp != null ? resp.getMessage() : "鏅虹簿鐏靛钩鍙扮櫥褰曞け璐�");
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String syncGateways(String source) {
+ ensureLogin();
+ ConditionerSessionRequest req = new ConditionerSessionRequest();
+ ConditionerBaseResponse<List<GatewayInfoResponse>> resp = ConditionerUtil.getWg(req);
+ if (resp == null || !resp.isSuccess()) {
+ throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "鍚屾缃戝叧澶辫触"));
+ }
+ List<GatewayInfoResponse> list = resp.getData() != null ? resp.getData() : Collections.emptyList();
+ Date now = new Date();
+ int add = 0, upd = 0, logs = 0;
+ for (GatewayInfoResponse item : list) {
+ if (StringUtils.isBlank(item.getWg_mac())) {
+ continue;
+ }
+ String newStatus = ConditionerConstant.normalizeGatewayOnline(item.getWg_status());
+ YwConditionerGateway local = gatewayMapper.selectOne(new QueryWrapper<YwConditionerGateway>().lambda()
+ .eq(YwConditionerGateway::getIsdeleted, Constants.ZERO)
+ .eq(YwConditionerGateway::getWgMac, item.getWg_mac())
+ .last(" limit 1 "));
+ if (local == null) {
+ local = new YwConditionerGateway();
+ local.setCreator(0);
+ local.setCreateDate(now);
+ local.setIsdeleted(Constants.ZERO);
+ local.setPlatformWgId(item.getWg_id());
+ local.setWgMac(item.getWg_mac());
+ local.setWgBz(item.getWg_bz());
+ local.setOnlineStatus(newStatus);
+ local.setLastSyncDate(now);
+ gatewayMapper.insert(local);
+ add++;
+ } else {
+ String oldStatus = local.getOnlineStatus();
+ local.setPlatformWgId(item.getWg_id());
+ local.setWgBz(item.getWg_bz());
+ local.setOnlineStatus(newStatus);
+ local.setLastSyncDate(now);
+ local.setEditDate(now);
+ gatewayMapper.updateById(local);
+ upd++;
+ if (!Objects.equals(oldStatus, newStatus)) {
+ writeGatewayLog(local.getId(), item.getWg_mac(), oldStatus, newStatus, source, now);
+ logs++;
+ }
+ }
+ }
+ return "鍚屾缃戝叧锛氭柊澧炪��" + add + "銆戞洿鏂般��" + upd + "銆戠姸鎬佸彉鏇淬��" + logs + "銆�";
+ }
+
+ @Override
+ public String syncGatewayStatus() {
+ return syncGateways("schedule");
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String syncMeters() {
+ ensureLogin();
+ MeterDbManageRequest req = new MeterDbManageRequest();
+ ConditionerBaseResponse<List<MeterDbInfoResponse>> resp = ConditionerUtil.getDb(req);
+ if (resp == null || !resp.isSuccess()) {
+ throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "鍚屾鐢佃〃澶辫触"));
+ }
+ List<MeterDbInfoResponse> list = resp.getData() != null ? resp.getData() : Collections.emptyList();
+ Date now = new Date();
+ int add = 0, upd = 0;
+ for (MeterDbInfoResponse item : list) {
+ if (item.getDb_id() == null) {
+ continue;
+ }
+ YwConditionerMeter local = meterMapper.selectOne(new QueryWrapper<YwConditionerMeter>().lambda()
+ .eq(YwConditionerMeter::getIsdeleted, Constants.ZERO)
+ .eq(YwConditionerMeter::getPlatformDbId, item.getDb_id())
+ .last(" limit 1 "));
+ if (local == null) {
+ local = new YwConditionerMeter();
+ local.setCreator(0);
+ local.setCreateDate(now);
+ local.setIsdeleted(Constants.ZERO);
+ add++;
+ } else {
+ upd++;
+ }
+ fillMeter(local, item, now);
+ if (local.getId() == null) {
+ meterMapper.insert(local);
+ } else {
+ meterMapper.updateById(local);
+ }
+ }
+ return "鍚屾鐢佃〃锛氭柊澧炪��" + add + "銆戞洿鏂般��" + upd + "銆�";
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String syncBilling() {
+ ensureLogin();
+ ConditionerSessionRequest req = new ConditionerSessionRequest();
+ ConditionerBaseResponse<List<DlSjXsResponse>> resp = ConditionerUtil.getDlSjXs(req);
+ if (resp == null || !resp.isSuccess()) {
+ throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "鍚屾璁¤垂绯绘暟澶辫触"));
+ }
+ List<DlSjXsResponse> list = resp.getData() != null ? resp.getData() : Collections.emptyList();
+ Date now = new Date();
+ int add = 0, upd = 0;
+ for (DlSjXsResponse item : list) {
+ if (item.getDev_id() == null) {
+ continue;
+ }
+ YwConditionerBilling local = billingMapper.selectOne(new QueryWrapper<YwConditionerBilling>().lambda()
+ .eq(YwConditionerBilling::getIsdeleted, Constants.ZERO)
+ .eq(YwConditionerBilling::getPlatformDevId, item.getDev_id())
+ .last(" limit 1 "));
+ if (local == null) {
+ local = new YwConditionerBilling();
+ local.setCreator(0);
+ local.setCreateDate(now);
+ local.setIsdeleted(Constants.ZERO);
+ add++;
+ } else {
+ upd++;
+ }
+ local.setPlatformDevId(item.getDev_id());
+ local.setKwType(item.getKw_type());
+ local.setFanArg(item.getFan_arg());
+ local.setFanKw(item.getFan_kw());
+ local.setHigKw(item.getHig_kw());
+ local.setMidKw(item.getMid_kw());
+ local.setLowKw(item.getLow_kw());
+ local.setKtDj(item.getKt_dj());
+ local.setLastSyncDate(now);
+ local.setEditDate(now);
+ YwConditioner dev = findConditionerByPlatformDevId(item.getDev_id());
+ if (dev != null) {
+ local.setDevName(dev.getName());
+ local.setWgMac(dev.getWgMac());
+ }
+ if (local.getId() == null) {
+ billingMapper.insert(local);
+ } else {
+ billingMapper.updateById(local);
+ }
+ }
+ return "鍚屾璁¤垂绯绘暟锛氭柊澧炪��" + add + "銆戞洿鏂般��" + upd + "銆�";
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String syncIndoorUnits() {
+ ensureLogin();
+ ConditionerSessionRequest session = new ConditionerSessionRequest();
+ ConditionerBaseResponse<List<DeviceStatusResponse>> statusResp = ConditionerUtil.getDevList(session);
+ if (statusResp == null || !statusResp.isSuccess()) {
+ throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(statusResp, "鍚屾鍐呮満鐘舵�佸け璐�"));
+ }
+ ConditionerBaseResponse<List<DeviceArchiveResponse>> archiveResp = ConditionerUtil.getDev(session);
+ Map<Integer, DeviceArchiveResponse> archiveMap = new HashMap<>();
+ if (archiveResp != null && archiveResp.getData() != null) {
+ for (DeviceArchiveResponse a : archiveResp.getData()) {
+ if (a.getDev_id() != null) {
+ archiveMap.put(a.getDev_id(), a);
+ }
+ }
+ }
+ List<DeviceStatusResponse> list = statusResp.getData() != null ? statusResp.getData() : Collections.emptyList();
+ Date now = new Date();
+ int add = 0, upd = 0;
+ for (DeviceStatusResponse item : list) {
+ if (item.getDev_id() == null) {
+ continue;
+ }
+ YwConditioner local = findConditionerByPlatformDevId(item.getDev_id());
+ if (local == null) {
+ local = new YwConditioner();
+ local.setCreator(0);
+ local.setCreateDate(now);
+ local.setIsdeleted(Constants.ZERO);
+ local.setCode(String.valueOf(item.getDev_id()));
+ local.setPlatformDevId(item.getDev_id());
+ add++;
+ } else {
+ upd++;
+ }
+ fillConditionerFromStatus(local, item, archiveMap.get(item.getDev_id()), now);
+ if (local.getId() == null) {
+ conditionerMapper.insert(local);
+ } else {
+ conditionerMapper.updateById(local);
+ }
+ }
+ return "鍚屾璁惧涓庣姸鎬侊細鏂板銆�" + add + "銆戞洿鏂般��" + upd + "銆�";
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String syncUsage(YwConditionerReportQueryDTO query) {
+ YwConditionerReportQueryDTO q = query != null ? query : new YwConditionerReportQueryDTO();
+ ReportDateRange range = resolveReportDateRange(q);
+ ensureLogin();
+ DayDlQueryRequest req = new DayDlQueryRequest();
+ req.setStart_time(range.start.toString());
+ req.setEnd_time(range.end.toString());
+ Integer gsId = q.getGsId();
+ if (gsId != null) {
+ req.setGs_id(gsId);
+ }
+ req.setPage(1);
+ req.setPageSize(5000);
+ ConditionerBaseResponse<List<Object>> resp = ConditionerUtil.getDayDl(req);
+ if (resp == null || !resp.isSuccess()) {
+ throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "鍚屾鐢ㄩ噺澶辫触"));
+ }
+ List<Object> list = resp.getData() != null ? resp.getData() : Collections.emptyList();
+ Date now = new Date();
+ int upsert = 0;
+ for (Object row : list) {
+ JSONObject o = (JSONObject) JSON.toJSON(row);
+ Integer devId = o.getInteger("dev_id");
+ String uptime = o.getString("uptime");
+ if (devId == null || StringUtils.isBlank(uptime)) {
+ continue;
+ }
+ Date usageDate = parseDate(uptime);
+ if (usageDate == null) {
+ continue;
+ }
+ YwConditionerUsage usage = usageMapper.selectOne(new QueryWrapper<YwConditionerUsage>().lambda()
+ .eq(YwConditionerUsage::getIsdeleted, Constants.ZERO)
+ .eq(YwConditionerUsage::getPlatformDevId, devId)
+ .eq(YwConditionerUsage::getUsageDate, usageDate)
+ .eq(gsId != null, YwConditionerUsage::getGsId, gsId)
+ .isNull(gsId == null, YwConditionerUsage::getGsId)
+ .last(" limit 1 "));
+ if (usage == null) {
+ usage = new YwConditionerUsage();
+ usage.setCreator(0);
+ usage.setCreateDate(now);
+ usage.setIsdeleted(Constants.ZERO);
+ usage.setPlatformDevId(devId);
+ usage.setGsId(gsId);
+ }
+ usage.setDevName(o.getString("dev_name"));
+ usage.setUsageDate(usageDate);
+ usage.setSumTime(o.getBigDecimal("sum_time"));
+ usage.setSumDl(o.getBigDecimal("sum_dl"));
+ usage.setSumDf(o.getBigDecimal("sum_df"));
+ usage.setSyncDate(now);
+ usage.setEditDate(now);
+ if (usage.getId() == null) {
+ usageMapper.insert(usage);
+ } else {
+ usageMapper.updateById(usage);
+ }
+ upsert++;
+ }
+ return "鍚屾鐢ㄩ噺锛氬鐞嗐��" + upsert + "銆戞潯";
+ }
+
+ @Override
+ public String syncUsagePreviousDay() {
+ LocalDate yesterday = LocalDate.now().minusDays(1);
+ YwConditionerReportQueryDTO query = new YwConditionerReportQueryDTO();
+ query.setReportType("day");
+ query.setStartTime(yesterday.toString());
+ query.setEndTime(yesterday.toString());
+ return syncUsage(query);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String operate(YwConditionerOperateDTO dto, LoginUserInfo user) {
+ if (dto == null || dto.getId() == null || dto.getActionType() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST);
+ }
+ YwConditioner dev = requireConditioner(dto.getId());
+ ensureDeviceOnline(dev);
+ ensureLogin();
+ String setType;
+ Object setVal = dto.getSetVal();
+ int actionType = dto.getActionType();
+ reserveAdjustOperateInterval(dev.getId(), actionType);
+ switch (actionType) {
+ case ACTION_PWR:
+ setType = "pwr";
+ break;
+ case ACTION_MODE:
+ setType = "mode";
+ break;
+ case ACTION_FAN:
+ setType = "fan";
+ break;
+ case ACTION_TEMP:
+ setType = "temp";
+ if (setVal != null) {
+ try {
+ setVal = Integer.parseInt(String.valueOf(setVal)) * 10;
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ break;
+ default:
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "涓嶆敮鎸佺殑鎿嶄綔绫诲瀷");
+ }
+ DevControlRequest req = buildDevControl(dev, setType, setVal);
+ String reqJson = JSON.toJSONString(req);
+ ConditionerBaseResponse<Object> resp = ConditionerUtil.devCtr(req);
+ boolean ok = resp != null && resp.isSuccess();
+ String actionDesc = formatOperateDescription(actionType, dto.getSetVal());
+ saveActionRecord(dev, actionType, actionDesc, ok, resp, reqJson, dto.getSource(), user);
+ if (!ok) {
+ throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "鎺у埗澶辫触"));
+ }
+ // 娓╁害/椋庨��/妯″紡锛氭帶鍒舵垚鍔熷嵆鎸夎瀹氬�兼洿鏂版湰鍦帮紝涓嶅啀鎷夊彇涓夋柟璁惧鐘舵��
+ if (actionType == ACTION_PWR) {
+ refreshDevStatus(dev);
+ }
+ applyOperateResultToDevice(dev, actionType, dto.getSetVal());
+ dev.setEditDate(new Date());
+ dev.setLastSyncDate(new Date());
+ conditionerMapper.updateById(dev);
+ return "鎿嶄綔鎴愬姛";
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String lock(YwConditionerLockDTO dto, LoginUserInfo user) {
+ if (dto == null || dto.getId() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST);
+ }
+ YwConditioner dev = requireConditioner(dto.getId());
+ ensureDeviceOnline(dev);
+ ensureLogin();
+ JSONObject lockVal = buildLockSetVal(dto);
+ DevLockControlRequest req = buildLockManyControlRequest(dev, lockVal);
+ String reqJson = JSON.toJSONString(req);
+ ConditionerBaseResponse<Object> resp = ConditionerUtil.devLockManyCtr(req);
+ boolean ok = resp != null && resp.isSuccess();
+ saveActionRecord(dev, ACTION_LOCK, lockVal.toJSONString(), ok, resp, reqJson, dto.getSource(), user);
+ if (!ok) {
+ throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "閿佸畾澶辫触"));
+ }
+ applyLockResultToDevice(dev, dto);
+ refreshDevStatus(dev);
+ applyLockResultToDevice(dev, dto);
+ Date now = new Date();
+ dev.setEditDate(now);
+ dev.setLastSyncDate(now);
+ conditionerMapper.updateById(dev);
+ return Objects.equals(dto.getLockPwr(), 0) ? "瑙i攣鎴愬姛" : "閿佸畾鎴愬姛";
+ }
+
+ private JSONObject buildLockSetVal(YwConditionerLockDTO dto) {
+ JSONObject lockVal = new JSONObject();
+ lockVal.put("lock_pwr", dto.getLockPwr() != null ? dto.getLockPwr() : -1);
+ lockVal.put("pwr", dto.getPwr() != null ? dto.getPwr() : -1);
+ lockVal.put("mode", dto.getMode() != null ? dto.getMode() : -1);
+ lockVal.put("fan", dto.getFan() != null ? dto.getFan() : -1);
+ lockVal.put("min_temp", dto.getMinTemp() != null ? dto.getMinTemp() : -1);
+ lockVal.put("max_temp", dto.getMaxTemp() != null ? dto.getMaxTemp() : -1);
+ return lockVal;
+ }
+
+ private void applyLockResultToDevice(YwConditioner dev, YwConditionerLockDTO dto) {
+ dev.setLockPwr(dto.getLockPwr());
+ dev.setLockMode(dto.getMode());
+ dev.setLockFan(dto.getFan());
+ dev.setLockMinTemp(dto.getMinTemp());
+ dev.setLockMaxTemp(dto.getMaxTemp());
+ dev.setKtLock(hasActiveLock(dto) ? 1 : 0);
+ }
+
+ private DevLockControlRequest buildLockManyControlRequest(YwConditioner dev, JSONObject lockVal) {
+ DevLockControlRequest req = new DevLockControlRequest();
+ req.fillSessionDefaults();
+ req.setCtr_type("set_many");
+ DevLockManyControlItem item = new DevLockManyControlItem();
+ item.setWg_mac(dev.getWgMac());
+ item.setWg_qid(dev.getWgQid());
+ item.setPid(StringUtils.defaultIfBlank(dev.getPid(), PID_DLJ));
+ item.setSet_type(LOCK_SET_TYPE);
+ item.setSet_val(lockVal);
+ req.setLi_data(Collections.singletonList(item));
+ return req;
+ }
+
+ private boolean hasActiveLock(YwConditionerLockDTO dto) {
+ return Objects.equals(dto.getLockPwr(), 1)
+ || (dto.getMode() != null && dto.getMode() >= 0)
+ || (dto.getFan() != null && dto.getFan() >= 0)
+ || (dto.getMinTemp() != null && dto.getMinTemp() >= 0)
+ || (dto.getMaxTemp() != null && dto.getMaxTemp() >= 0);
+ }
+
+ /**
+ * 鍚屼竴鍙板唴鏈猴細璁惧畾娓╁害/椋庨��/妯″紡鎿嶄綔闂撮殧涓嶄綆浜� 2 绉�
+ */
+ private void reserveAdjustOperateInterval(Integer deviceId, int actionType) {
+ if (deviceId == null
+ || (actionType != ACTION_MODE && actionType != ACTION_FAN && actionType != ACTION_TEMP)) {
+ return;
+ }
+ long now = System.currentTimeMillis();
+ Long last = LAST_ADJUST_OPERATE_MS.get(deviceId);
+ if (last != null && now - last < ADJUST_OPERATE_INTERVAL_MS) {
+ long waitSec = (ADJUST_OPERATE_INTERVAL_MS - (now - last) + 999) / 1000;
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),
+ "鎿嶄綔杩囦簬棰戠箒锛岃" + waitSec + "绉掑悗鍐嶈瘯");
+ }
+ LAST_ADJUST_OPERATE_MS.put(deviceId, now);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String queryMeterEnergy(Integer meterId, LoginUserInfo user) {
+ return queryMeter(meterId, "find_dn", ACTION_QUERY_DL, user);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String queryMeterPower(Integer meterId, LoginUserInfo user) {
+ return queryMeter(meterId, "find_power", ACTION_QUERY_POWER, user);
+ }
+
+ private String queryMeter(Integer meterId, String setType, int actionType, LoginUserInfo user) {
+ YwConditionerMeter meter = meterMapper.selectById(meterId);
+ if (meter == null || Objects.equals(meter.getIsdeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+ ensureLogin();
+ DevControlRequest req = buildMeterQueryRequest(meter, setType);
+ String reqJson = JSON.toJSONString(req);
+ ConditionerBaseResponse<Object> resp = ConditionerUtil.devCtr(req);
+ boolean ok = resp != null && resp.isSuccess();
+ YwConditionerActions action = new YwConditionerActions();
+ action.setActionType(actionType);
+ action.setWgMac(meter.getWgMac());
+ action.setDevName(meter.getDbName());
+ action.setActionContent(setType);
+ action.setRequestBody(reqJson);
+ action.setResponseBody(resp != null ? JSON.toJSONString(resp) : null);
+ action.setResultStatus(ok ? Constants.ONE : Constants.ZERO);
+ action.setResultMsg(ok ? "鎴愬姛" : apiMsg(resp, "澶辫触"));
+ action.setSource("admin");
+ saveAction(action, user);
+ if (!ok) {
+ throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "鏌ヨ澶辫触"));
+ }
+ if (ACTION_QUERY_DL == actionType && resp.getData() != null) {
+ try {
+ BigDecimal dl = new BigDecimal(String.valueOf(resp.getData()));
+ meter.setTotalDl(dl);
+ } catch (Exception ignored) {
+ }
+ }
+ if (ACTION_QUERY_POWER == actionType && resp.getData() != null) {
+ try {
+ BigDecimal kw = new BigDecimal(String.valueOf(resp.getData()));
+ meter.setPowerKw(kw);
+ } catch (Exception ignored) {
+ }
+ }
+ meter.setLastSyncDate(new Date());
+ meterMapper.updateById(meter);
+ return "鏌ヨ鎴愬姛";
+ }
+
+ @Override
+ public void saveAction(YwConditionerActions action, LoginUserInfo user) {
+ Date now = new Date();
+ if (action.getCreateDate() == null) {
+ action.setCreateDate(now);
+ }
+ if (action.getCreator() == null && user != null) {
+ action.setCreator(user.getId());
+ }
+ action.setIsdeleted(Constants.ZERO);
+ actionsMapper.insert(action);
+ }
+
+ @Override
+ public YwConditionerUsageReportPageDTO queryUsageReport(YwConditionerReportQueryDTO query) {
+ YwConditionerReportQueryDTO q = query != null ? query : new YwConditionerReportQueryDTO();
+ ReportDateRange range = resolveReportDateRange(q);
+ LocalDate start = range.start;
+ LocalDate end = range.end;
+ List<String> dateColumns = new ArrayList<>();
+ DateTimeFormatter colFmt = range.dayMode
+ ? DateTimeFormatter.ofPattern("yyyy-MM-dd")
+ : DateTimeFormatter.ofPattern("M.d");
+ for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) {
+ dateColumns.add(d.format(colFmt));
+ }
+ QueryWrapper<YwConditionerUsage> wrapper = new QueryWrapper<>();
+ wrapper.lambda()
+ .eq(YwConditionerUsage::getIsdeleted, Constants.ZERO)
+ .ge(YwConditionerUsage::getUsageDate, java.sql.Date.valueOf(start))
+ .le(YwConditionerUsage::getUsageDate, java.sql.Date.valueOf(end));
+ if (q.getGsId() != null) {
+ wrapper.lambda().eq(YwConditionerUsage::getGsId, q.getGsId());
+ }
+ if (StringUtils.isNotBlank(q.getDevKeyword())) {
+ wrapper.lambda().and(w -> w.like(YwConditionerUsage::getDevName, q.getDevKeyword())
+ .or().eq(YwConditionerUsage::getPlatformDevId, parseIntOrNull(q.getDevKeyword())));
+ }
+ List<YwConditionerUsage> rows = usageMapper.selectList(wrapper);
+ Map<Integer, YwConditionerUsageReportVO> grouped = new LinkedHashMap<>();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ for (YwConditionerUsage row : rows) {
+ YwConditionerUsageReportVO vo = grouped.computeIfAbsent(row.getPlatformDevId(), k -> {
+ YwConditionerUsageReportVO n = new YwConditionerUsageReportVO();
+ n.setDevId(row.getPlatformDevId());
+ n.setDevName(row.getDevName());
+ n.setTotalTime(BigDecimal.ZERO);
+ n.setTotalDl(BigDecimal.ZERO);
+ n.setTotalDf(BigDecimal.ZERO);
+ return n;
+ });
+ if (StringUtils.isBlank(vo.getDevName()) && StringUtils.isNotBlank(row.getDevName())) {
+ vo.setDevName(row.getDevName());
+ }
+ String dayKey = sdf.format(row.getUsageDate());
+ YwConditionerUsageReportVO.YwConditionerUsageDailyVO daily = new YwConditionerUsageReportVO.YwConditionerUsageDailyVO();
+ daily.setTime(row.getSumTime());
+ daily.setDl(row.getSumDl());
+ daily.setDf(row.getSumDf());
+ vo.getDaily().put(dayKey, daily);
+ vo.setTotalTime(add(vo.getTotalTime(), row.getSumTime()));
+ vo.setTotalDl(add(vo.getTotalDl(), row.getSumDl()));
+ vo.setTotalDf(add(vo.getTotalDf(), row.getSumDf()));
+ }
+ List<YwConditionerUsageReportVO> all = new ArrayList<>(grouped.values());
+ enrichReportDeviceInfo(all);
+ int page = q.getPage() != null && q.getPage() > 0 ? q.getPage() : 1;
+ int capacity = q.getCapacity() != null && q.getCapacity() > 0 ? q.getCapacity() : 20;
+ int from = (page - 1) * capacity;
+ int to = Math.min(from + capacity, all.size());
+ List<YwConditionerUsageReportVO> pageRecords = from >= all.size()
+ ? Collections.emptyList() : all.subList(from, to);
+
+ YwConditionerUsageReportPageDTO result = new YwConditionerUsageReportPageDTO();
+ result.setDateColumns(dateColumns);
+ result.setRecords(pageRecords);
+ result.setTotal(all.size());
+ result.setPage(page);
+ result.setCapacity(capacity);
+ return result;
+ }
+
+ @Override
+ public List<Map<String, Object>> gatewayOptions() {
+ List<YwConditionerGateway> list = gatewayMapper.selectList(new QueryWrapper<YwConditionerGateway>().lambda()
+ .eq(YwConditionerGateway::getIsdeleted, Constants.ZERO)
+ .orderByAsc(YwConditionerGateway::getWgMac));
+ return list.stream().map(g -> {
+ Map<String, Object> m = new LinkedHashMap<>();
+ m.put("id", g.getId());
+ m.put("wgMac", g.getWgMac());
+ m.put("label", g.getWgMac() + (StringUtils.isNotBlank(g.getWgBz()) ? " (" + g.getWgBz() + ")" : ""));
+ return m;
+ }).collect(Collectors.toList());
+ }
+
+ @Override
+ public String syncAll() {
+ if (syncing) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鍚屾浠诲姟姝e湪鎵ц锛岃绋嶅悗");
+ }
+ syncing = true;
+ try {
+ StringBuilder sb = new StringBuilder();
+ sb.append(syncGateways("manual")).append("锛�");
+ sb.append(syncMeters()).append("锛�");
+ sb.append(syncBilling()).append("锛�");
+ sb.append(syncIndoorUnits());
+ return sb.toString();
+ } finally {
+ syncing = false;
+ }
+ }
+
+ private void refreshDevStatus(YwConditioner dev) {
+ GetDevOneRequest oneReq = new GetDevOneRequest();
+ oneReq.setWg_mac(dev.getWgMac());
+ oneReq.setWg_qid(dev.getWgQid());
+ oneReq.fillSessionDefaults();
+ ConditionerBaseResponse<DeviceStatusResponse> resp = ConditionerUtil.getDevOne(oneReq);
+ if (resp != null && resp.isSuccess() && resp.getData() != null) {
+ fillConditionerFromStatus(dev, resp.getData(), null, new Date());
+ }
+ }
+
+ private void applyOperateResultToDevice(YwConditioner dev, int actionType, Object setVal) {
+ if (setVal == null) {
+ return;
+ }
+ Integer val = parseIntOrNull(String.valueOf(setVal));
+ if (val == null) {
+ return;
+ }
+ switch (actionType) {
+ case ACTION_PWR:
+ dev.setPwr(val);
+ break;
+ case ACTION_MODE:
+ dev.setMode(val);
+ break;
+ case ACTION_FAN:
+ dev.setFanSet(val);
+ dev.setFan(val);
+ break;
+ case ACTION_TEMP:
+ dev.setTempSet(val <= 50 ? val * 10 : val);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /** 鍐呮満鎺у埗鎿嶄綔鍙鎻忚堪锛屽啓鍏ユ帶鍒跺巻鍙� actionContent */
+ private String formatOperateDescription(int actionType, Object setVal) {
+ Integer val = setVal == null ? null : parseIntOrNull(String.valueOf(setVal));
+ switch (actionType) {
+ case ACTION_PWR:
+ if (val == null) {
+ return "璁剧疆寮�鍏�";
+ }
+ return String.format("璁剧疆寮�鍏充负銆�%s銆�", val == 1 ? "寮�鏈�" : "鍏虫満");
+ case ACTION_MODE:
+ if (val == null) {
+ return "璁剧疆妯″紡";
+ }
+ return String.format("璁剧疆妯″紡涓恒��%s銆�", modeLabel(val));
+ case ACTION_FAN:
+ if (val == null) {
+ return "璁剧疆椋庨��";
+ }
+ return String.format("璁剧疆椋庨�熶负銆�%s銆�", fanLabel(val));
+ case ACTION_TEMP:
+ if (val == null) {
+ return "璁剧疆娓╁害";
+ }
+ return String.format("璁剧疆娓╁害涓恒��%s銆�", formatTempDisplay(val));
+ default:
+ return setVal == null ? "璁惧鎺у埗" : String.valueOf(setVal);
+ }
+ }
+
+ private String modeLabel(int mode) {
+ switch (mode) {
+ case 1:
+ return "鍒剁儹";
+ case 2:
+ return "鍒跺喎";
+ case 3:
+ return "閫侀";
+ case 4:
+ return "闄ゆ箍";
+ default:
+ return String.valueOf(mode);
+ }
+ }
+
+ private String fanLabel(int fan) {
+ switch (fan) {
+ case 1:
+ return "浣庨��";
+ case 2:
+ return "涓��";
+ case 3:
+ return "楂橀��";
+ case 4:
+ return "鑷姩";
+ default:
+ return String.valueOf(fan);
+ }
+ }
+
+ private String formatTempDisplay(int val) {
+ double celsius = val > 100 ? val / 10.0 : val;
+ if (Math.abs(celsius - Math.rint(celsius)) < 0.05) {
+ return String.format("%.0f鈩�", celsius);
+ }
+ return String.format("%.1f鈩�", celsius);
+ }
+
+ private DevControlRequest buildMeterQueryRequest(YwConditionerMeter meter, String setType) {
+ if (StringUtils.isBlank(meter.getWgMac())) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鐢佃〃鏈粦瀹氱綉鍏筹紝璇峰厛鍚屾鐢佃〃");
+ }
+ if (StringUtils.isBlank(meter.getDbAdr())) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鐢佃〃鍦板潃涓虹┖锛岃鍏堝悓姝ョ數琛�");
+ }
+ if (StringUtils.isBlank(meter.getXyName())) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鐢佃〃鍗忚涓虹┖锛岃鍏堝悓姝ョ數琛�");
+ }
+ DevControlRequest req = new DevControlRequest();
+ req.fillSessionDefaults();
+ req.setWg_mac(meter.getWgMac());
+ req.setWg_qid(meter.getDbAdr());
+ req.setCtr_type("set_one");
+ req.setSet_type(setType);
+ JSONObject setVal = new JSONObject();
+ setVal.put("db_bb", meter.getDbBb() != null ? meter.getDbBb() : 1);
+ setVal.put("xy_name", meter.getXyName());
+ req.setSet_val(setVal);
+ return req;
+ }
+
+ private DevControlRequest buildDevControl(YwConditioner dev, String setType, Object setVal) {
+ DevControlRequest req = new DevControlRequest();
+ req.fillSessionDefaults();
+ req.setPid(StringUtils.defaultIfBlank(dev.getPid(), PID_DLJ));
+ req.setWg_mac(dev.getWgMac());
+ req.setWg_qid(dev.getWgQid());
+ req.setCtr_type("set_one");
+ req.setSet_type(setType);
+ req.setSet_val(setVal);
+ return req;
+ }
+
+ private void saveActionRecord(YwConditioner dev, int actionType, String content, boolean ok,
+ ConditionerBaseResponse<?> resp, String reqJson, String source, LoginUserInfo user) {
+ YwConditionerActions action = new YwConditionerActions();
+ action.setConditionerId(dev.getId());
+ action.setPlatformDevId(dev.getPlatformDevId());
+ action.setDevName(dev.getName());
+ action.setWgMac(dev.getWgMac());
+ action.setActionType(actionType);
+ action.setActionContent(content);
+ action.setRequestBody(reqJson);
+ action.setResponseBody(resp != null ? JSON.toJSONString(resp) : null);
+ action.setResultStatus(ok ? Constants.ONE : Constants.ZERO);
+ action.setResultMsg(ok ? "鎴愬姛" : apiMsg(resp, "澶辫触"));
+ action.setSource(StringUtils.defaultIfBlank(source, "admin"));
+ saveAction(action, user);
+ }
+
+ private void writeGatewayLog(Integer gatewayId, String wgMac, String oldStatus, String newStatus,
+ String source, Date now) {
+ YwConditionerGatewayLog log = new YwConditionerGatewayLog();
+ log.setCreator(0);
+ log.setCreateDate(now);
+ log.setIsdeleted(Constants.ZERO);
+ log.setGatewayId(gatewayId);
+ log.setWgMac(wgMac);
+ log.setOldStatus(oldStatus);
+ log.setNewStatus(newStatus);
+ log.setLogTime(now);
+ log.setSource(StringUtils.defaultIfBlank(source, "manual"));
+ gatewayLogMapper.insert(log);
+ }
+
+ private void fillMeter(YwConditionerMeter local, MeterDbInfoResponse item, Date now) {
+ local.setPlatformDbId(item.getDb_id());
+ local.setDbName(item.getDb_name());
+ local.setDbAdr(item.getDb_adr());
+ local.setWgMac(item.getWg_mac());
+ local.setWgId(item.getWg_id());
+ local.setXyId(item.getXy_id());
+ local.setXyName(item.getXy_name());
+ local.setDbBb(item.getDb_bb());
+ local.setOutdoorLoop(item.getDb_rhd());
+ local.setDbUptime(item.getDb_uptime());
+ if (item.getDb_data() != null) {
+ local.setDbData(JSON.toJSONString(item.getDb_data()));
+ }
+ local.setLastSyncDate(now);
+ local.setEditDate(now);
+ }
+
+ private void fillConditionerFromStatus(YwConditioner local, DeviceStatusResponse item,
+ DeviceArchiveResponse archive, Date now) {
+ local.setPlatformDevId(item.getDev_id());
+ local.setWgId(item.getWg_id());
+ local.setWgMac(item.getWg_mac());
+ local.setWgQid(item.getWg_qid());
+ local.setPid(StringUtils.defaultIfBlank(item.getPid(), PID_DLJ));
+ local.setOnline(ConditionerConstant.normalizeDeviceOnline(item.getOnline()));
+ local.setPwr(item.getPwr());
+ local.setMode(item.getMode());
+ local.setFan(item.getFan());
+ local.setFanSet(item.getFan_set());
+ local.setTemp(item.getTemp());
+ local.setTempSet(item.getTemp_set());
+ local.setKtLock(item.getKt_lock());
+ local.setStopLogo(item.getStop_logo());
+ local.setUptime(item.getUptime());
+ local.setFloorId(item.getFloor_id());
+ local.setFloorName(item.getFloor_name());
+ local.setRoomId(item.getRoom_id());
+ local.setRoomName(item.getRoom_name());
+ local.setDevTypeId(item.getDev_type_id());
+ local.setDevTypeName(item.getDev_type_name());
+ if (StringUtils.isNotBlank(item.getDev_name())) {
+ local.setName(item.getDev_name());
+ }
+ if (archive != null) {
+ if (StringUtils.isNotBlank(archive.getDev_name())) {
+ local.setName(archive.getDev_name());
+ }
+ if (archive.getFloor_id() != null) {
+ local.setFloorId(archive.getFloor_id());
+ }
+ if (StringUtils.isNotBlank(archive.getFloor_name())) {
+ local.setFloorName(archive.getFloor_name());
+ }
+ if (archive.getRoom_id() != null) {
+ local.setRoomId(archive.getRoom_id());
+ }
+ if (StringUtils.isNotBlank(archive.getRoom_name())) {
+ local.setRoomName(archive.getRoom_name());
+ }
+ }
+ local.setLastSyncDate(now);
+ local.setEditDate(now);
+ }
+
+ private YwConditioner findConditionerByPlatformDevId(Integer platformDevId) {
+ return conditionerMapper.selectOne(new QueryWrapper<YwConditioner>().lambda()
+ .eq(YwConditioner::getIsdeleted, Constants.ZERO)
+ .eq(YwConditioner::getPlatformDevId, platformDevId)
+ .last(" limit 1 "));
+ }
+
+ private YwConditioner requireConditioner(Integer id) {
+ YwConditioner dev = conditionerMapper.selectById(id);
+ if (dev == null || Objects.equals(dev.getIsdeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+ if (StringUtils.isBlank(dev.getWgMac()) || StringUtils.isBlank(dev.getWgQid())) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "璁惧鏈粦瀹氬钩鍙扮綉鍏充俊鎭紝璇峰厛鍚屾鍐呮満");
+ }
+ return dev;
+ }
+
+ private void ensureDeviceOnline(YwConditioner dev) {
+ if (!"鍦ㄧ嚎".equals(ConditionerConstant.normalizeDeviceOnline(dev.getOnline()))) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "璁惧绂荤嚎浜�,璇锋鏌ュ搴旇澶囩綉缁�");
+ }
+ }
+
+ private YearMonth parseMonth(String month) {
+ if (StringUtils.isBlank(month)) {
+ return YearMonth.now().minusMonths(1);
+ }
+ try {
+ return YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM"));
+ } catch (Exception e) {
+ return YearMonth.now().minusMonths(1);
+ }
+ }
+
+ private void enrichReportDeviceInfo(List<YwConditionerUsageReportVO> records) {
+ if (records == null || records.isEmpty()) {
+ return;
+ }
+ Set<Integer> devIds = records.stream()
+ .map(YwConditionerUsageReportVO::getDevId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ if (devIds.isEmpty()) {
+ return;
+ }
+ List<YwConditioner> devs = conditionerMapper.selectList(new QueryWrapper<YwConditioner>().lambda()
+ .eq(YwConditioner::getIsdeleted, Constants.ZERO)
+ .in(YwConditioner::getPlatformDevId, devIds));
+ Map<Integer, YwConditioner> devMap = devs.stream()
+ .filter(d -> d.getPlatformDevId() != null)
+ .collect(Collectors.toMap(YwConditioner::getPlatformDevId, d -> d, (a, b) -> a));
+ for (YwConditionerUsageReportVO vo : records) {
+ YwConditioner dev = devMap.get(vo.getDevId());
+ if (dev == null) {
+ continue;
+ }
+ vo.setFloorName(dev.getFloorName());
+ vo.setRoomName(dev.getRoomName());
+ if (StringUtils.isNotBlank(dev.getName())) {
+ vo.setDevName(dev.getName());
+ }
+ }
+ }
+
+ private static class ReportDateRange {
+ LocalDate start;
+ LocalDate end;
+ boolean dayMode;
+ }
+
+ private ReportDateRange resolveReportDateRange(YwConditionerReportQueryDTO q) {
+ String reportType = StringUtils.defaultIfBlank(q.getReportType(), "month");
+ ReportDateRange range = new ReportDateRange();
+ if ("day".equalsIgnoreCase(reportType)) {
+ if (StringUtils.isBlank(q.getStartTime()) || StringUtils.isBlank(q.getEndTime())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇烽�夋嫨鏃堕棿娈�");
+ }
+ LocalDate start;
+ LocalDate end;
+ try {
+ start = LocalDate.parse(q.getStartTime());
+ end = LocalDate.parse(q.getEndTime());
+ } catch (Exception e) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏃ユ湡鏍煎紡涓嶆纭�");
+ }
+ if (end.isBefore(start)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "缁撴潫鏃ユ湡涓嶈兘鏃╀簬寮�濮嬫棩鏈�");
+ }
+ long days = ChronoUnit.DAYS.between(start, end) + 1;
+ if (days > 31) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏃堕棿娈典笉鑳借秴杩�31澶�");
+ }
+ range.start = start;
+ range.end = end;
+ range.dayMode = true;
+ return range;
+ }
+ YearMonth ym = parseMonth(q.getMonth());
+ range.start = ym.atDay(1);
+ range.end = ym.atEndOfMonth();
+ range.dayMode = false;
+ return range;
+ }
+
+ private Date parseDate(String text) {
+ try {
+ if (text.length() > 10) {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(text);
+ }
+ return new SimpleDateFormat("yyyy-MM-dd").parse(text);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private Integer parseIntOrNull(String text) {
+ try {
+ return Integer.parseInt(text.trim());
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private BigDecimal add(BigDecimal a, BigDecimal b) {
+ if (a == null) {
+ a = BigDecimal.ZERO;
+ }
+ return b != null ? a.add(b) : a;
+ }
+
+ private String apiMsg(ConditionerBaseResponse<?> resp, String def) {
+ return resp != null && StringUtils.isNotBlank(resp.getMessage()) ? resp.getMessage() : def;
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/ConditionerConfigService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/ConditionerConfigService.java
new file mode 100644
index 0000000..5956a4b
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/ConditionerConfigService.java
@@ -0,0 +1,49 @@
+package com.doumee.service.business.impl;
+
+import com.doumee.biz.system.SystemDictDataBiz;
+import com.doumee.core.conditoner.model.ConditionerConstant;
+import com.doumee.core.utils.Constants;
+import com.doumee.dao.system.model.SystemDictData;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * 浠庢暟鎹瓧鍏� CONDITIONER_PARAM 鍔犺浇鏅虹簿鐏靛钩鍙拌繛鎺ュ弬鏁般��
+ */
+@Slf4j
+@Service
+public class ConditionerConfigService {
+
+ @Autowired
+ private SystemDictDataBiz systemDictDataBiz;
+
+ @PostConstruct
+ public void init() {
+ refreshFromDict();
+ }
+
+ public void refreshFromDict() {
+ ConditionerConstant.base_url = read(Constants.CONDITIONER_BASE_URL, ConditionerConstant.DEFAULT_BASE_URL);
+ ConditionerConstant.username = read(Constants.CONDITIONER_USERNAME, ConditionerConstant.DEFAULT_USERNAME);
+ ConditionerConstant.password = read(Constants.CONDITIONER_PASSWORD, ConditionerConstant.DEFAULT_PASSWORD);
+ log.info("conditioner config loaded from dict {}, base_url={}",
+ Constants.CONDITIONER_PARAM, ConditionerConstant.base_url);
+ }
+
+ private String read(String label, String defaultValue) {
+ try {
+ SystemDictData data = systemDictDataBiz.queryByCode(Constants.CONDITIONER_PARAM, label);
+ if (data != null && StringUtils.isNotBlank(data.getCode())) {
+ return data.getCode().trim();
+ }
+ log.warn("conditioner config [{}] empty, use default", label);
+ } catch (Exception e) {
+ log.warn("conditioner config [{}] load failed, use default", label, e);
+ }
+ return defaultValue;
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerActionsServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerActionsServiceImpl.java
new file mode 100644
index 0000000..e5dba46
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerActionsServiceImpl.java
@@ -0,0 +1,48 @@
+package com.doumee.service.business.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Constants;
+import com.doumee.core.utils.Utils;
+import com.doumee.dao.business.YwConditionerActionsMapper;
+import com.doumee.dao.business.model.YwConditioner;
+import com.doumee.dao.business.model.YwConditionerActions;
+import com.doumee.service.business.YwConditionerActionsService;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class YwConditionerActionsServiceImpl implements YwConditionerActionsService {
+
+ @Autowired
+ private YwConditionerActionsMapper actionsMapper;
+
+ @Override
+ public PageData<YwConditionerActions> findPage(PageWrap<YwConditionerActions> pageWrap) {
+ IPage<YwConditionerActions> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ YwConditionerActions model = pageWrap.getModel() == null ? new YwConditionerActions() : pageWrap.getModel();
+ Utils.MP.blankToNull(model);
+ MPJLambdaWrapper<YwConditionerActions> qw = new MPJLambdaWrapper<>();
+ qw.selectAll(YwConditionerActions.class)
+ .selectAs(YwConditioner::getOnline, YwConditionerActions::getOnlineFilter)
+ .leftJoin(YwConditioner.class, YwConditioner::getId, YwConditionerActions::getConditionerId)
+ .eq(YwConditionerActions::getIsdeleted, Constants.ZERO)
+ .eq(model.getConditionerId() != null, YwConditionerActions::getConditionerId, model.getConditionerId())
+ .eq(model.getActionType() != null, YwConditionerActions::getActionType, model.getActionType())
+ .eq(StringUtils.isNotBlank(model.getWgMac()), YwConditionerActions::getWgMac, model.getWgMac())
+ .eq(StringUtils.isNotBlank(model.getOnlineFilter()), YwConditioner::getOnline, model.getOnlineFilter())
+ .and(StringUtils.isNotBlank(model.getDevKeyword()), w -> w
+ .like(YwConditionerActions::getDevName, model.getDevKeyword())
+ .or().like(YwConditionerActions::getWgMac, model.getDevKeyword()))
+ .ge(model.getOperateTimeBegin() != null, YwConditionerActions::getCreateDate,
+ model.getOperateTimeBegin() != null ? Utils.Date.getStart(model.getOperateTimeBegin()) : null)
+ .le(model.getOperateTimeEnd() != null, YwConditionerActions::getCreateDate,
+ model.getOperateTimeEnd() != null ? Utils.Date.getEnd(model.getOperateTimeEnd()) : null)
+ .orderByDesc(YwConditionerActions::getId);
+ return PageData.from(actionsMapper.selectJoinPage(page, YwConditionerActions.class, qw));
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerBillingServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerBillingServiceImpl.java
new file mode 100644
index 0000000..171bab6
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerBillingServiceImpl.java
@@ -0,0 +1,45 @@
+package com.doumee.service.business.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Constants;
+import com.doumee.core.utils.Utils;
+import com.doumee.dao.business.YwConditionerBillingMapper;
+import com.doumee.dao.business.model.YwConditionerBilling;
+import com.doumee.service.business.ConditionerBizService;
+import com.doumee.service.business.YwConditionerBillingService;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class YwConditionerBillingServiceImpl implements YwConditionerBillingService {
+
+ @Autowired
+ private YwConditionerBillingMapper billingMapper;
+ @Autowired
+ private ConditionerBizService conditionerBizService;
+
+ @Override
+ public PageData<YwConditionerBilling> findPage(PageWrap<YwConditionerBilling> pageWrap) {
+ IPage<YwConditionerBilling> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ YwConditionerBilling model = pageWrap.getModel() == null ? new YwConditionerBilling() : pageWrap.getModel();
+ Utils.MP.blankToNull(model);
+ MPJLambdaWrapper<YwConditionerBilling> qw = new MPJLambdaWrapper<>();
+ qw.selectAll(YwConditionerBilling.class)
+ .eq(YwConditionerBilling::getIsdeleted, Constants.ZERO)
+ .and(StringUtils.isNotBlank(model.getDevKeyword()), w -> w
+ .like(YwConditionerBilling::getDevName, model.getDevKeyword())
+ .or().eq(YwConditionerBilling::getPlatformDevId, model.getDevKeyword()))
+ .orderByDesc(YwConditionerBilling::getId);
+ return PageData.from(billingMapper.selectJoinPage(page, YwConditionerBilling.class, qw));
+ }
+
+ @Override
+ public String syncAll() {
+ return conditionerBizService.syncBilling();
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerGatewayServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerGatewayServiceImpl.java
new file mode 100644
index 0000000..c37588a
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerGatewayServiceImpl.java
@@ -0,0 +1,66 @@
+package com.doumee.service.business.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Constants;
+import com.doumee.core.utils.Utils;
+import com.doumee.dao.business.YwConditionerGatewayLogMapper;
+import com.doumee.dao.business.YwConditionerGatewayMapper;
+import com.doumee.dao.business.model.YwConditionerGateway;
+import com.doumee.dao.business.model.YwConditionerGatewayLog;
+import com.doumee.service.business.ConditionerBizService;
+import com.doumee.service.business.YwConditionerGatewayService;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class YwConditionerGatewayServiceImpl implements YwConditionerGatewayService {
+
+ @Autowired
+ private YwConditionerGatewayMapper gatewayMapper;
+ @Autowired
+ private YwConditionerGatewayLogMapper gatewayLogMapper;
+ @Autowired
+ private ConditionerBizService conditionerBizService;
+
+ @Override
+ public PageData<YwConditionerGateway> findPage(PageWrap<YwConditionerGateway> pageWrap) {
+ IPage<YwConditionerGateway> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ YwConditionerGateway model = pageWrap.getModel() == null ? new YwConditionerGateway() : pageWrap.getModel();
+ Utils.MP.blankToNull(model);
+ MPJLambdaWrapper<YwConditionerGateway> qw = new MPJLambdaWrapper<>();
+ qw.selectAll(YwConditionerGateway.class)
+ .eq(YwConditionerGateway::getIsdeleted, Constants.ZERO)
+ .eq(StringUtils.isNotBlank(model.getOnlineStatus()), YwConditionerGateway::getOnlineStatus, model.getOnlineStatus())
+ .and(StringUtils.isNotBlank(model.getKeyword()), w -> w
+ .like(YwConditionerGateway::getWgMac, model.getKeyword())
+ .or().like(YwConditionerGateway::getWgBz, model.getKeyword()))
+ .orderByDesc(YwConditionerGateway::getId);
+ return PageData.from(gatewayMapper.selectJoinPage(page, YwConditionerGateway.class, qw));
+ }
+
+ @Override
+ public PageData<YwConditionerGatewayLog> gatewayLogPage(PageWrap<YwConditionerGatewayLog> pageWrap) {
+ IPage<YwConditionerGatewayLog> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ YwConditionerGatewayLog model = pageWrap.getModel() == null ? new YwConditionerGatewayLog() : pageWrap.getModel();
+ Utils.MP.blankToNull(model);
+ MPJLambdaWrapper<YwConditionerGatewayLog> qw = new MPJLambdaWrapper<>();
+ qw.selectAll(YwConditionerGatewayLog.class)
+ .eq(YwConditionerGatewayLog::getIsdeleted, Constants.ZERO)
+ .eq(model.getGatewayId() != null, YwConditionerGatewayLog::getGatewayId, model.getGatewayId())
+ .eq(StringUtils.isNotBlank(model.getWgMac()), YwConditionerGatewayLog::getWgMac, model.getWgMac())
+ .ge(model.getLogTimeBegin() != null, YwConditionerGatewayLog::getLogTime, model.getLogTimeBegin())
+ .le(model.getLogTimeEnd() != null, YwConditionerGatewayLog::getLogTime, model.getLogTimeEnd())
+ .orderByDesc(YwConditionerGatewayLog::getLogTime);
+ return PageData.from(gatewayLogMapper.selectJoinPage(page, YwConditionerGatewayLog.class, qw));
+ }
+
+ @Override
+ public String syncAll() {
+ return conditionerBizService.syncGateways("manual");
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerMeterServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerMeterServiceImpl.java
new file mode 100644
index 0000000..63c9270
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerMeterServiceImpl.java
@@ -0,0 +1,58 @@
+package com.doumee.service.business.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.doumee.core.model.LoginUserInfo;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Constants;
+import com.doumee.core.utils.Utils;
+import com.doumee.dao.business.YwConditionerMeterMapper;
+import com.doumee.dao.business.model.YwConditionerMeter;
+import com.doumee.service.business.ConditionerBizService;
+import com.doumee.service.business.YwConditionerMeterService;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class YwConditionerMeterServiceImpl implements YwConditionerMeterService {
+
+ @Autowired
+ private YwConditionerMeterMapper meterMapper;
+ @Autowired
+ private ConditionerBizService conditionerBizService;
+
+ @Override
+ public PageData<YwConditionerMeter> findPage(PageWrap<YwConditionerMeter> pageWrap) {
+ IPage<YwConditionerMeter> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ YwConditionerMeter model = pageWrap.getModel() == null ? new YwConditionerMeter() : pageWrap.getModel();
+ Utils.MP.blankToNull(model);
+ MPJLambdaWrapper<YwConditionerMeter> qw = new MPJLambdaWrapper<>();
+ qw.selectAll(YwConditionerMeter.class)
+ .eq(YwConditionerMeter::getIsdeleted, Constants.ZERO)
+ .eq(StringUtils.isNotBlank(model.getWgMacFilter()), YwConditionerMeter::getWgMac, model.getWgMacFilter())
+ .and(StringUtils.isNotBlank(model.getKeyword()), w -> w
+ .like(YwConditionerMeter::getDbName, model.getKeyword())
+ .or().like(YwConditionerMeter::getDbAdr, model.getKeyword())
+ .or().like(YwConditionerMeter::getWgMac, model.getKeyword()))
+ .orderByDesc(YwConditionerMeter::getId);
+ return PageData.from(meterMapper.selectJoinPage(page, YwConditionerMeter.class, qw));
+ }
+
+ @Override
+ public String syncAll() {
+ return conditionerBizService.syncMeters();
+ }
+
+ @Override
+ public String queryEnergy(Integer meterId, LoginUserInfo user) {
+ return conditionerBizService.queryMeterEnergy(meterId, user);
+ }
+
+ @Override
+ public String queryPower(Integer meterId, LoginUserInfo user) {
+ return conditionerBizService.queryMeterPower(meterId, user);
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerReportServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerReportServiceImpl.java
new file mode 100644
index 0000000..243ae40
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerReportServiceImpl.java
@@ -0,0 +1,155 @@
+package com.doumee.service.business.impl;
+
+import com.doumee.core.conditoner.ConditionerUtil;
+import com.doumee.core.conditoner.model.request.CompanyGsManageRequest;
+import com.doumee.core.conditoner.model.response.CompanyGsInfoResponse;
+import com.doumee.core.conditoner.model.response.ConditionerBaseResponse;
+import com.doumee.dao.business.dto.YwConditionerReportQueryDTO;
+import com.doumee.dao.business.dto.YwConditionerUsageReportPageDTO;
+import com.doumee.dao.business.dto.YwConditionerUsageReportVO;
+import com.doumee.service.business.ConditionerBizService;
+import com.doumee.service.business.YwConditionerReportService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Service
+public class YwConditionerReportServiceImpl implements YwConditionerReportService {
+
+ @Autowired
+ private ConditionerBizService conditionerBizService;
+
+ @Override
+ public YwConditionerUsageReportPageDTO findPage(YwConditionerReportQueryDTO query) {
+ return conditionerBizService.queryUsageReport(query);
+ }
+
+ @Override
+ public String syncUsage(YwConditionerReportQueryDTO query) {
+ return conditionerBizService.syncUsage(query);
+ }
+
+ @Override
+ public List<Map<String, Object>> merchantOptions() {
+ conditionerBizService.ensureLogin();
+ CompanyGsManageRequest req = new CompanyGsManageRequest();
+ ConditionerBaseResponse<List<CompanyGsInfoResponse>> resp = ConditionerUtil.getGs(req);
+ if (resp == null || resp.getData() == null) {
+ return Collections.emptyList();
+ }
+ return resp.getData().stream().map(gs -> {
+ Map<String, Object> m = new LinkedHashMap<>();
+ m.put("gsId", gs.getId());
+ m.put("gsName", gs.getGs_name());
+ return m;
+ }).collect(Collectors.toList());
+ }
+
+ @Override
+ public void exportExcel(YwConditionerReportQueryDTO query, HttpServletResponse response) {
+ if (query == null) {
+ query = new YwConditionerReportQueryDTO();
+ }
+ query.setPage(1);
+ query.setCapacity(1_000_000);
+ YwConditionerUsageReportPageDTO page = findPage(query);
+ try {
+ String fileName = URLEncoder.encode("绌鸿皟鐢ㄩ噺鎶ヨ〃.csv", StandardCharsets.UTF_8.name());
+ response.setContentType("text/csv;charset=UTF-8");
+ response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
+ PrintWriter writer = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));
+ writer.write('\ufeff');
+ List<String> cols = page.getDateColumns() != null ? page.getDateColumns() : Collections.emptyList();
+ StringBuilder header = new StringBuilder("璁惧ID,璁惧鍚嶇О,鎬绘椂闀�(h),鎬荤數閲�(kWh),鎬荤數璐�(鍏�)");
+ for (String col : cols) {
+ header.append(',').append(col).append("鐢甸噺(kWh),")
+ .append(col).append("鏃堕暱(h),")
+ .append(col).append("鐢佃垂(鍏�)");
+ }
+ writer.println(header);
+ if (page.getRecords() != null) {
+ for (YwConditionerUsageReportVO row : page.getRecords()) {
+ StringBuilder line = new StringBuilder();
+ line.append(csv(row.getDevId())).append(',')
+ .append(csv(formatDevDisplayName(row))).append(',')
+ .append(csv(row.getTotalTime())).append(',')
+ .append(csv(row.getTotalDl())).append(',')
+ .append(csv(row.getTotalDf()));
+ for (String col : cols) {
+ String dateKey = colToDateKey(query, col);
+ YwConditionerUsageReportVO.YwConditionerUsageDailyVO day =
+ row.getDaily() != null && dateKey != null ? row.getDaily().get(dateKey) : null;
+ line.append(',').append(csv(day != null ? day.getDl() : ""))
+ .append(',').append(csv(day != null ? day.getTime() : ""))
+ .append(',').append(csv(day != null ? day.getDf() : ""));
+ }
+ writer.println(line);
+ }
+ }
+ writer.flush();
+ } catch (Exception e) {
+ throw new RuntimeException("瀵煎嚭澶辫触", e);
+ }
+ }
+
+ private static String csv(Object val) {
+ if (val == null) {
+ return "";
+ }
+ String s = String.valueOf(val).replace("\"", "\"\"");
+ if (s.contains(",") || s.contains("\"") || s.contains("\n")) {
+ return "\"" + s + "\"";
+ }
+ return s;
+ }
+
+ private static String formatDevDisplayName(YwConditionerUsageReportVO row) {
+ if (row == null) {
+ return "-";
+ }
+ StringBuilder sb = new StringBuilder();
+ appendDisplayPart(sb, row.getFloorName());
+ appendDisplayPart(sb, row.getRoomName());
+ appendDisplayPart(sb, row.getDevName());
+ return sb.length() == 0 ? "-" : sb.toString();
+ }
+
+ private static void appendDisplayPart(StringBuilder sb, String part) {
+ if (StringUtils.isBlank(part)) {
+ return;
+ }
+ if (sb.length() > 0) {
+ sb.append('/');
+ }
+ sb.append(part.trim());
+ }
+
+ private String colToDateKey(YwConditionerReportQueryDTO query, String col) {
+ if (query != null && "day".equalsIgnoreCase(query.getReportType())) {
+ return col;
+ }
+ String month = query != null ? query.getMonth() : null;
+ if (month == null || col == null || !col.contains(".")) {
+ return col;
+ }
+ String[] parts = col.split("\\.");
+ if (parts.length != 2) {
+ return col;
+ }
+ String year = month.split("-")[0];
+ int m = Integer.parseInt(parts[0]);
+ int d = Integer.parseInt(parts[1]);
+ return String.format("%s-%02d-%02d", year, m, d);
+ }
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerServiceImpl.java
index 3f563dc..8c74504 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerServiceImpl.java
@@ -8,8 +8,15 @@
import com.doumee.core.utils.Constants;
import com.doumee.core.utils.DateUtil;
import com.doumee.core.utils.Utils;
+import com.doumee.dao.business.dto.YwConditionerLockDTO;
+import com.doumee.dao.business.dto.YwConditionerOperateDTO;
+import com.doumee.dao.business.YwConditionerGatewayMapper;
import com.doumee.dao.business.YwConditionerMapper;
import com.doumee.dao.business.model.YwConditioner;
+import com.doumee.dao.business.model.YwConditionerActions;
+import com.doumee.dao.business.model.YwConditionerGateway;
+import com.doumee.service.business.ConditionerBizService;
+import com.doumee.service.business.YwConditionerActionsService;
import com.doumee.service.business.YwConditionerService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
@@ -22,8 +29,11 @@
import org.springframework.util.CollectionUtils;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.stream.Collectors;
/**
* 绌鸿皟璁惧淇℃伅Service瀹炵幇
@@ -35,6 +45,12 @@
@Autowired
private YwConditionerMapper ywConditionerMapper;
+ @Autowired
+ private YwConditionerGatewayMapper gatewayMapper;
+ @Autowired
+ private ConditionerBizService conditionerBizService;
+ @Autowired
+ private YwConditionerActionsService ywConditionerActionsService;
@Override
public Integer create(YwConditioner ywConditioner) {
@@ -125,4 +141,115 @@
IPage<YwConditioner> iPage = ywConditionerMapper.selectJoinPage(page, YwConditioner.class, queryWrapper);
return PageData.from(iPage);
}
+
+ @Override
+ public PageData<YwConditioner> findCardPage(PageWrap<YwConditioner> pageWrap) {
+ IPage<YwConditioner> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ Utils.MP.blankToNull(pageWrap.getModel());
+ YwConditioner model = pageWrap.getModel() == null ? new YwConditioner() : pageWrap.getModel();
+ MPJLambdaWrapper<YwConditioner> queryWrapper = new MPJLambdaWrapper<>();
+ queryWrapper.selectAll(YwConditioner.class)
+ .eq(YwConditioner::getIsdeleted, Constants.ZERO)
+ .isNotNull(YwConditioner::getPlatformDevId)
+ .and(StringUtils.isNotBlank(model.getDevKeyword()), w -> w
+ .like(YwConditioner::getName, model.getDevKeyword())
+ .or().like(YwConditioner::getCode, model.getDevKeyword())
+ .or().like(YwConditioner::getRoomName, model.getDevKeyword()))
+ .eq(StringUtils.isNotBlank(model.getOnlineFilter()), YwConditioner::getOnline, model.getOnlineFilter())
+ .eq(model.getPwrFilter() != null, YwConditioner::getPwr, model.getPwrFilter())
+ .eq(StringUtils.isNotBlank(model.getWgMacFilter()), YwConditioner::getWgMac, model.getWgMacFilter())
+ .orderByAsc(YwConditioner::getWgQid)
+ .orderByAsc(YwConditioner::getId);
+ PageData<YwConditioner> pageData = PageData.from(
+ ywConditionerMapper.selectJoinPage(page, YwConditioner.class, queryWrapper));
+ fillGatewayBzFromGateway(pageData.getRecords());
+ return pageData;
+ }
+
+ /**
+ * 浠庢湰鍦扮綉鍏抽暅鍍忚〃 yw_conditioner_gateway 鍏宠仈濉厖澶囨敞锛堜紭鍏堝钩鍙扮綉鍏矷D锛屽叾娆� MAC锛�
+ */
+ private void fillGatewayBzFromGateway(List<YwConditioner> records) {
+ if (CollectionUtils.isEmpty(records)) {
+ return;
+ }
+ List<String> macs = records.stream()
+ .map(YwConditioner::getWgMac)
+ .filter(StringUtils::isNotBlank)
+ .distinct()
+ .collect(Collectors.toList());
+ List<Integer> platformWgIds = records.stream()
+ .map(YwConditioner::getWgId)
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toList());
+ if (macs.isEmpty() && platformWgIds.isEmpty()) {
+ return;
+ }
+ QueryWrapper<YwConditionerGateway> gwQuery = new QueryWrapper<>();
+ gwQuery.lambda().eq(YwConditionerGateway::getIsdeleted, Constants.ZERO);
+ if (!macs.isEmpty() && !platformWgIds.isEmpty()) {
+ gwQuery.lambda().and(w -> w.in(YwConditionerGateway::getWgMac, macs)
+ .or().in(YwConditionerGateway::getPlatformWgId, platformWgIds));
+ } else if (!macs.isEmpty()) {
+ gwQuery.lambda().in(YwConditionerGateway::getWgMac, macs);
+ } else {
+ gwQuery.lambda().in(YwConditionerGateway::getPlatformWgId, platformWgIds);
+ }
+ List<YwConditionerGateway> gateways = gatewayMapper.selectList(gwQuery);
+ Map<String, String> bzByMac = new HashMap<>();
+ Map<Integer, String> bzByPlatformWgId = new HashMap<>();
+ for (YwConditionerGateway gw : gateways) {
+ if (StringUtils.isNotBlank(gw.getWgBz())) {
+ if (StringUtils.isNotBlank(gw.getWgMac())) {
+ bzByMac.putIfAbsent(gw.getWgMac().trim(), gw.getWgBz());
+ }
+ if (gw.getPlatformWgId() != null) {
+ bzByPlatformWgId.putIfAbsent(gw.getPlatformWgId(), gw.getWgBz());
+ }
+ }
+ }
+ for (YwConditioner row : records) {
+ String bz = null;
+ if (StringUtils.isNotBlank(row.getWgMac())) {
+ bz = bzByMac.get(row.getWgMac().trim());
+ }
+ if (StringUtils.isBlank(bz) && row.getWgId() != null) {
+ bz = bzByPlatformWgId.get(row.getWgId());
+ }
+ if (StringUtils.isNotBlank(bz)) {
+ row.setWgBz(bz);
+ }
+ }
+ }
+
+ @Override
+ public String syncAll() {
+ return conditionerBizService.syncAll();
+ }
+
+ @Override
+ public String syncDevicesAndStatus() {
+ return conditionerBizService.syncIndoorUnits();
+ }
+
+ @Override
+ public String operate(YwConditionerOperateDTO dto, LoginUserInfo user) {
+ return conditionerBizService.operate(dto, user);
+ }
+
+ @Override
+ public String lock(YwConditionerLockDTO dto, LoginUserInfo user) {
+ return conditionerBizService.lock(dto, user);
+ }
+
+ @Override
+ public PageData<YwConditionerActions> historyPage(PageWrap<YwConditionerActions> pageWrap) {
+ return ywConditionerActionsService.findPage(pageWrap);
+ }
+
+ @Override
+ public List<Map<String, Object>> gatewayOptions() {
+ return conditionerBizService.gatewayOptions();
+ }
}
--
Gitblit v1.9.3