From 93de43267e1663031fe5dc2f5ae40d128a182a76 Mon Sep 17 00:00:00 2001
From: doum <doum>
Date: 星期四, 18 六月 2026 17:24:51 +0800
Subject: [PATCH] 新增智能电表、空调管理
---
h5/pages/customer/contract/detail.vue | 4
server/db/business.yw_customer_member_openid.sql | 22
h5/main.js | 2
h5/components/customer/cu-workbench-fab.vue | 18
h5/pages/customer/login.vue | 9
server/db/business.yw_conditioner_device.menu.sql | 20
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5AuthServiceImpl.java | 252 +++
h5/pages/index.vue | 255 +-
h5/pages/login.vue | 437 +----
h5/pages/customer/electricity/list.vue | 194 +
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwWxPayOrder.java | 2
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5BizServiceImpl.java | 33
h5/styles/customer.scss | 1115 +++++++++++++++
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwCustomerRechargeRecordVO.java | 2
admin/src/api/business/ywconditionerdevice.js | 23
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwConditionerServiceImpl.java | 148 ++
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/SmsEmailServiceImpl.java | 30
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwContractServiceImpl.java | 16
admin/src/views/business/components/YwCustomerElectricalRechargePanel.vue | 2
admin/src/views/business/components/YwCustomerConditionerRechargePanel.vue | 36
h5/pages/customer/electricity/recharge.vue | 48
h5/pages/customer/index.vue | 100
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwElectricalCharge.java | 4
h5/api/customer.js | 1
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerWxPayServiceImpl.java | 18
h5/pages/customer/bill/list.vue | 4
server/system_service/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java | 22
h5/pages/customer/contract/list.vue | 3
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwConditionerService.java | 7
h5/pages/customer/recharge/record.vue | 236 ++
admin/src/views/business/ywconditionerdevice.vue | 132 +
h5/pages/roleSelect.vue | 42
admin/src/views/business/components/YwConditionerDeviceEdit.vue | 192 ++
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerDeviceAutoBindService.java | 19
h5/pages/customer/pay/result.vue | 3
server/system_service/src/main/java/com/doumee/core/model/LoginUserInfo.java | 9
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwConditionerCloudController.java | 27
h5/pages/customer/conditioner/recharge.vue | 39
h5/pages/customer/bill/pay.vue | 3
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerRechargeBizServiceImpl.java | 179 +
server/db/business.yw_electrical_charge.member.sql | 32
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/web/YwCustomerH5Controller.java | 17
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerEditDTO.java | 14
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/h5/CustomerDeviceH5VO.java | 1
h5/pages/customer/bill/detail.vue | 4
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5BizService.java | 2
admin/src/views/contract/contractList.vue | 10
admin/src/views/business/components/AccountRechargePanel.vue | 36
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5AuthService.java | 5
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java | 281 +++
h5/utils/utils.js | 8
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwConditioner.java | 12
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwElectricalBizServiceImpl.java | 31
53 files changed, 3,336 insertions(+), 825 deletions(-)
diff --git a/admin/src/api/business/ywconditionerdevice.js b/admin/src/api/business/ywconditionerdevice.js
new file mode 100644
index 0000000..155253d
--- /dev/null
+++ b/admin/src/api/business/ywconditionerdevice.js
@@ -0,0 +1,23 @@
+import request from '../../utils/request'
+
+const base = '/visitsAdmin/cloudService/business/ywConditioner'
+
+export function fetchDeviceManagePage (data) {
+ return request.post(base + '/deviceManagePage', data, { trim: true })
+}
+
+export function getManageDetail (id) {
+ return request.get(base + '/manageDetail/' + id)
+}
+
+export function saveManageDetail (data) {
+ return request.post(base + '/saveManageDetail', data)
+}
+
+export function syncDevicesAndStatus (data) {
+ return request.post(base + '/syncDevicesAndStatus', data || {})
+}
+
+export function gatewayOptions () {
+ return request.get(base + '/gatewayOptions')
+}
diff --git a/admin/src/views/business/components/AccountRechargePanel.vue b/admin/src/views/business/components/AccountRechargePanel.vue
index 262f11d..566ac65 100644
--- a/admin/src/views/business/components/AccountRechargePanel.vue
+++ b/admin/src/views/business/components/AccountRechargePanel.vue
@@ -11,7 +11,15 @@
</div>
<el-form label-width="150px">
<el-form-item :label="mode === 'open' ? '寮�鎴烽噾棰�' : '鍏呭�奸噾棰�'">
- <el-input-number v-model="form.money" :min="0" :precision="4" style="width: 200px"/>
+ <el-input-number
+ v-model="form.money"
+ class="money-input-number"
+ :min="0"
+ :precision="4"
+ :step="10"
+ controls-position="right"
+ placeholder="璇疯緭鍏ラ噾棰�"
+ />
</el-form-item>
<el-form-item :label="mode === 'open' ? '寮�鎴峰娉�' : '鍏呭�煎娉�'">
<el-input v-model="form.remark" :placeholder="mode === 'open' ? '寮�鎴峰娉紝鏈�澶�50涓瓧绗�' : '鍏呭�煎娉紝鏈�澶�300涓瓧绗�'" :maxlength="mode === 'open' ? 50 : 300" style="width: 400px"/>
@@ -79,4 +87,30 @@
width: auto;
white-space: nowrap;
}
+.money-input-number {
+ width: 220px !important;
+}
+.money-input-number ::v-deep .el-input {
+ width: 100% !important;
+}
+.money-input-number ::v-deep .el-input__inner {
+ height: 32px;
+ line-height: 32px;
+ text-align: left;
+ padding-left: 12px;
+ padding-right: 42px;
+}
+.money-input-number ::v-deep .el-input-number__increase,
+.money-input-number ::v-deep .el-input-number__decrease {
+ width: 28px;
+ background: #f5f7fa;
+ border-left: 1px solid #dcdfe6;
+}
+.money-input-number ::v-deep .el-input-number__increase {
+ border-bottom: 1px solid #dcdfe6;
+ line-height: 15px;
+}
+.money-input-number ::v-deep .el-input-number__decrease {
+ line-height: 15px;
+}
</style>
diff --git a/admin/src/views/business/components/YwConditionerDeviceEdit.vue b/admin/src/views/business/components/YwConditionerDeviceEdit.vue
new file mode 100644
index 0000000..a886ec2
--- /dev/null
+++ b/admin/src/views/business/components/YwConditionerDeviceEdit.vue
@@ -0,0 +1,192 @@
+<template>
+ <GlobalWindow title="缂栬緫绌鸿皟鍐呮満" :visible.sync="visible" width="720px" :confirm-working="isWorking" @confirm="confirm">
+ <el-form ref="form" :model="form" label-width="120px" class="conditioner-edit-form">
+ <el-form-item label="璁惧鍚嶇О">
+ <span>{{ form.name || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="璁惧缂栧彿">
+ <span>{{ form.code || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="缃戝叧MAC">
+ <span>{{ form.wgMac || '-' }}</span>
+ <span :class="isOnline ? 'green' : 'red'" style="margin-left: 12px">{{ form.online || '绂荤嚎' }}</span>
+ </el-form-item>
+ <el-form-item label="璁惧绫诲瀷">
+ <span>{{ form.devTypeName || form.pid || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="璇烽�夋嫨鎴挎簮">
+ <el-tree
+ ref="roomTree"
+ :data="houseList"
+ show-checkbox
+ node-key="idd"
+ :props="{ children: 'projectDataVOList', label: 'name' }"
+ :default-checked-keys="checkedKeys"
+ @check="onRoomCheck"
+ style="max-height: 320px; overflow: auto; width: 400px; border: 1px solid #dcdfe6; padding: 8px;"
+ />
+ </el-form-item>
+ <el-form-item label="宸查�夋埧婧�">
+ <div class="selected-room-list">
+ <template v-if="selectedRooms.length">
+ <el-tag
+ v-for="room in selectedRooms"
+ :key="room.idd"
+ closable
+ class="selected-room-tag"
+ @close="removeSelectedRoom(room)"
+ >{{ room.roomPath }}</el-tag>
+ </template>
+ <span v-else class="selected-room-empty">-</span>
+ </div>
+ </el-form-item>
+ <el-form-item label="澶囨敞">
+ <el-input
+ v-model="form.remark"
+ type="textarea"
+ :rows="4"
+ maxlength="500"
+ show-word-limit
+ placeholder="璇疯緭鍏ュ娉�"
+ style="width: 400px"
+ />
+ </el-form-item>
+ </el-form>
+ </GlobalWindow>
+</template>
+
+<script>
+import GlobalWindow from '@/components/common/GlobalWindow'
+import { tree } from '@/api/project/ywProject'
+import { getManageDetail, saveManageDetail } from '@/api/business/ywconditionerdevice'
+
+export default {
+ name: 'YwConditionerDeviceEdit',
+ components: { GlobalWindow },
+ data () {
+ return {
+ visible: false,
+ isWorking: false,
+ form: {},
+ houseList: [],
+ checkedKeys: [],
+ selectedRooms: []
+ }
+ },
+ computed: {
+ isOnline () {
+ const o = this.form && this.form.online
+ return o === '鍦ㄧ嚎' || o === 1 || o === '1'
+ }
+ },
+ methods: {
+ open (row) {
+ this.visible = true
+ this.checkedKeys = []
+ this.selectedRooms = []
+ this.loadHouseTree()
+ getManageDetail(row.id).then(data => {
+ this.form = { ...data }
+ if (data.roomIds && data.roomIds.length) {
+ this.checkedKeys = data.roomIds.map(id => '3-' + id)
+ this.$nextTick(() => {
+ if (this.$refs.roomTree) {
+ this.$refs.roomTree.setCheckedKeys(this.checkedKeys)
+ this.syncSelectedRoomsFromTree()
+ }
+ })
+ }
+ }).catch(e => this.$tip.apiFailed(e))
+ },
+ loadHouseTree () {
+ tree({}).then(res => {
+ this.markTreeNodes(res || [])
+ this.houseList = res || []
+ this.$nextTick(() => {
+ if (this.checkedKeys.length && this.$refs.roomTree) {
+ this.$refs.roomTree.setCheckedKeys(this.checkedKeys)
+ this.syncSelectedRoomsFromTree()
+ }
+ })
+ })
+ },
+ markTreeNodes (arr, ancestors) {
+ if (!arr) return
+ arr.forEach(node => {
+ node.idd = node.lv + '-' + node.id
+ node.disabled = node.lv !== 3
+ const names = (ancestors || []).concat(node.name)
+ if (node.lv === 3) {
+ node.roomPath = names.length >= 3 ? names.slice(-3).join('/') : names.join('/')
+ }
+ if (node.projectDataVOList && node.projectDataVOList.length) {
+ this.markTreeNodes(node.projectDataVOList, names)
+ }
+ })
+ },
+ syncSelectedRoomsFromTree () {
+ const treeRef = this.$refs.roomTree
+ if (!treeRef) {
+ this.selectedRooms = []
+ return
+ }
+ const nodes = treeRef.getCheckedNodes(true).filter(n => n.lv === 3)
+ this.selectedRooms = nodes.map(n => ({
+ id: n.id,
+ idd: n.idd,
+ roomPath: n.roomPath || n.name
+ }))
+ this.form.roomIds = nodes.map(n => n.id)
+ this.checkedKeys = nodes.map(n => n.idd)
+ },
+ removeSelectedRoom (room) {
+ const treeRef = this.$refs.roomTree
+ if (!treeRef || !room) return
+ treeRef.setChecked(room.idd, false, true)
+ this.syncSelectedRoomsFromTree()
+ },
+ onRoomCheck () {
+ this.syncSelectedRoomsFromTree()
+ },
+ confirm () {
+ this.isWorking = true
+ saveManageDetail({
+ id: this.form.id,
+ roomIds: this.form.roomIds || [],
+ remark: this.form.remark || ''
+ })
+ .then(() => {
+ this.$tip.success('淇濆瓨鎴愬姛')
+ this.visible = false
+ this.$emit('success')
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isWorking = false })
+ }
+ }
+}
+</script>
+
+<style scoped>
+.green { color: #67c23a; }
+.red { color: #f56c6c; }
+.selected-room-list {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ max-width: 560px;
+ min-height: 32px;
+ gap: 8px;
+}
+.selected-room-tag {
+ max-width: 100%;
+ white-space: normal;
+ height: auto;
+ line-height: 1.5;
+ padding-top: 4px;
+ padding-bottom: 4px;
+}
+.selected-room-empty {
+ color: #909399;
+}
+</style>
diff --git a/admin/src/views/business/components/YwCustomerConditionerRechargePanel.vue b/admin/src/views/business/components/YwCustomerConditionerRechargePanel.vue
index dc8fe75..1e55e2e 100644
--- a/admin/src/views/business/components/YwCustomerConditionerRechargePanel.vue
+++ b/admin/src/views/business/components/YwCustomerConditionerRechargePanel.vue
@@ -8,7 +8,15 @@
</div>
<el-form label-width="120px" size="small">
<el-form-item label="鍏呭�奸噾棰�">
- <el-input-number v-model="form.money" :min="0" :precision="2" style="width: 200px"/>
+ <el-input-number
+ v-model="form.money"
+ class="money-input-number"
+ :min="0"
+ :precision="2"
+ :step="10"
+ controls-position="right"
+ placeholder="璇疯緭鍏ラ噾棰�"
+ />
</el-form-item>
<el-form-item label="鍏呭�煎娉�">
<el-input v-model="form.remark" maxlength="300" style="width: 400px"/>
@@ -103,4 +111,30 @@
.info-block p { margin: 4px 0; line-height: 26px; }
.footer-btns { text-align: right; margin-top: 16px; }
.footer-btns .el-button { margin-left: 8px; }
+.money-input-number {
+ width: 220px !important;
+}
+.money-input-number ::v-deep .el-input {
+ width: 100% !important;
+}
+.money-input-number ::v-deep .el-input__inner {
+ height: 32px;
+ line-height: 32px;
+ text-align: left;
+ padding-left: 12px;
+ padding-right: 42px;
+}
+.money-input-number ::v-deep .el-input-number__increase,
+.money-input-number ::v-deep .el-input-number__decrease {
+ width: 28px;
+ background: #f5f7fa;
+ border-left: 1px solid #dcdfe6;
+}
+.money-input-number ::v-deep .el-input-number__increase {
+ border-bottom: 1px solid #dcdfe6;
+ line-height: 15px;
+}
+.money-input-number ::v-deep .el-input-number__decrease {
+ line-height: 15px;
+}
</style>
diff --git a/admin/src/views/business/components/YwCustomerElectricalRechargePanel.vue b/admin/src/views/business/components/YwCustomerElectricalRechargePanel.vue
index eabda81..7367b5c 100644
--- a/admin/src/views/business/components/YwCustomerElectricalRechargePanel.vue
+++ b/admin/src/views/business/components/YwCustomerElectricalRechargePanel.vue
@@ -23,7 +23,7 @@
<el-button type="warning" plain :loading="isOperating" v-permissions="['business:ywcustomerrecharge:recharge']" @click="resetAccount('resetPostpay')">娓呴浂(鍚庝粯璐�)</el-button>
</div>
-->
- <div v-if="!electricalList.length && !loading" class="empty-tip">璇ュ晢鎴峰皻鏈叧鑱旂數琛紝璇峰厛鍦ㄥ叧鑱旇澶囦腑娣诲姞</div>
+ <div v-if="!electricalList.length && !loading" class="empty-tip">璇ュ晢鎴锋湁鏁堝悎鍚屾埧婧愪笅鏆傛棤鍏宠仈鐢佃〃</div>
</div>
</template>
diff --git a/admin/src/views/business/ywconditionerdevice.vue b/admin/src/views/business/ywconditionerdevice.vue
new file mode 100644
index 0000000..70d3c16
--- /dev/null
+++ b/admin/src/views/business/ywconditionerdevice.vue
@@ -0,0 +1,132 @@
+<template>
+ <TableLayout :permissions="['business:ywconditioner:query']">
+ <template v-slot:table-wrap>
+ <ul class="toolbar">
+ <li>
+ <el-button
+ type="primary"
+ :loading="isSyncing"
+ v-permissions="['business:ywconditioner:sync']"
+ @click="handleSync"
+ >鍚屾绌鸿皟鍐呮満</el-button>
+ </li>
+ </ul>
+ <el-table v-loading="isWorking.search" :data="tableData.list" stripe>
+ <el-table-column label="璁惧绫诲瀷" min-width="100" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ formatDeviceType(row) }}</template>
+ </el-table-column>
+ <el-table-column label="璁惧ID" min-width="140" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ row.code || '-' }}</template>
+ </el-table-column>
+ <el-table-column prop="name" label="璁惧鍚嶇О" min-width="140" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ row.name || row.code || '-' }}</template>
+ </el-table-column>
+ <el-table-column prop="wgMac" label="缃戝叧MAC" min-width="140" align="center" show-overflow-tooltip />
+ <el-table-column prop="roomNames" label="缁戝畾鎴块棿" min-width="160" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ row.roomNames || '-' }}</template>
+ </el-table-column>
+ <el-table-column prop="remark" label="澶囨敞" min-width="160" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ row.remark || '-' }}</template>
+ </el-table-column>
+ <el-table-column label="璁惧鐘舵��" min-width="90" align="center">
+ <template slot-scope="{ row }">
+ <span :class="isOnline(row) ? 'green' : 'red'">{{ row.online || '绂荤嚎' }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鏇存柊鏃堕棿" min-width="150" align="center" show-overflow-tooltip>
+ <template slot-scope="{ row }">{{ formatUpdateTime(row) }}</template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" align="center" min-width="90" fixed="right">
+ <template slot-scope="{ row }">
+ <el-button type="text" @click="openEdit(row)" v-permissions="['business:ywconditioner:update']">缂栬緫</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination @size-change="handleSizeChange" @current-change="handlePageChange" :pagination="tableData.pagination" />
+ </template>
+ <YwConditionerDeviceEdit ref="editWindow" @success="handlePageChange" />
+ </TableLayout>
+</template>
+
+<script>
+import BaseTable from '@/components/base/BaseTable'
+import TableLayout from '@/layouts/TableLayout'
+import Pagination from '@/components/common/Pagination'
+import * as deviceApi from '@/api/business/ywconditionerdevice'
+import YwConditionerDeviceEdit from './components/YwConditionerDeviceEdit'
+
+export default {
+ name: 'YwConditionerDevice',
+ extends: BaseTable,
+ components: { TableLayout, Pagination, YwConditionerDeviceEdit },
+ data () {
+ return {
+ isSyncing: false
+ }
+ },
+ created () {
+ this.module = '绌鸿皟璁惧绠$悊'
+ this.configData['field.id'] = 'id'
+ this.configData['field.main'] = 'name'
+ this.tableData.pagination.pageSize = 10
+ this.search()
+ },
+ methods: {
+ handlePageChange (pageIndex) {
+ this.tableData.pagination.pageIndex = pageIndex || this.tableData.pagination.pageIndex
+ this.isWorking.search = true
+ deviceApi.fetchDeviceManagePage({
+ page: this.tableData.pagination.pageIndex,
+ capacity: this.tableData.pagination.pageSize,
+ model: {},
+ sorts: this.tableData.sorts
+ })
+ .then(data => {
+ this.tableData.list = data.records || []
+ this.tableData.pagination.total = data.total || 0
+ })
+ .catch(() => {})
+ .finally(() => { this.isWorking.search = false })
+ },
+ formatDeviceType (row) {
+ if (row.devTypeName) return row.devTypeName
+ const pid = (row.pid || '').toLowerCase()
+ if (pid === 'dlj') return '澶氳仈鏈�'
+ if (pid === 'sj') return '姘存満'
+ return row.pid || '-'
+ },
+ isOnline (row) {
+ const o = row && row.online
+ return o === '鍦ㄧ嚎' || o === 1 || o === '1'
+ },
+ formatUpdateTime (row) {
+ const val = row.lastSyncDate || row.editDate
+ if (!val) return '-'
+ const text = String(val)
+ return text.length >= 16 ? text.slice(0, 16) : text
+ },
+ handleSync () {
+ this.$dialog.actionConfirm('纭浠庢櫤绮剧伒骞冲彴鍚屾绌鸿皟鍐呮満璁惧淇℃伅鍚楋紵', '鍚屾绌鸿皟鍐呮満')
+ .then(() => {
+ this.isSyncing = true
+ deviceApi.syncDevicesAndStatus()
+ .then(res => {
+ this.$tip.apiSuccess(res || '鍚屾鎴愬姛')
+ this.search()
+ })
+ .catch(e => this.$tip.apiFailed(e))
+ .finally(() => { this.isSyncing = false })
+ })
+ .catch(() => {})
+ },
+ openEdit (row) {
+ this.$refs.editWindow.open(row)
+ }
+ }
+}
+</script>
+
+<style scoped>
+.green { color: #67c23a; }
+.red { color: #f56c6c; }
+</style>
diff --git a/admin/src/views/contract/contractList.vue b/admin/src/views/contract/contractList.vue
index baeffdf..c5c2e9a 100644
--- a/admin/src/views/contract/contractList.vue
+++ b/admin/src/views/contract/contractList.vue
@@ -122,11 +122,15 @@
},
getList (page) {
const { pagination, filters } = this
+ const model = { ...filters }
+ if (model.selDate && model.selDate.length === 2) {
+ model.queryStartTime = model.selDate[0]
+ model.queryEndTime = model.selDate[1]
+ }
+ delete model.selDate
this.loading = true
fetchList({
- model: {
- ...filters
- },
+ model,
sorts: [{ direction: 'DESC', property: 'param1' }],
capacity: pagination.pageSize,
page: page || pagination.page
diff --git a/h5/api/customer.js b/h5/api/customer.js
index 7ca8f0d..ccb628b 100644
--- a/h5/api/customer.js
+++ b/h5/api/customer.js
@@ -9,6 +9,7 @@
data: { phone: data.phone }
})
export const customerGetUserInfo = () => http({ url: `${prefix}/getUserInfo`, method: 'get' })
+export const customerLogout = () => http({ url: `${prefix}/logout`, method: 'post' })
export const customerWxAuthorize = (data) => http({ url: 'visitsAdmin/cloudService/web/visitor/ywWxAuthorize', method: 'get', data: { ...data, userType: 1 } })
export const customerBanners = () => http({ url: `${prefix}/banners`, method: 'get' })
export const customerHome = () => http({ url: `${prefix}/home`, method: 'get' })
diff --git a/h5/components/customer/cu-workbench-fab.vue b/h5/components/customer/cu-workbench-fab.vue
new file mode 100644
index 0000000..87f8eb0
--- /dev/null
+++ b/h5/components/customer/cu-workbench-fab.vue
@@ -0,0 +1,18 @@
+<template>
+ <view class="cu-workbench-fab" @click="goWorkbench">
+ <u-icon name="home-fill" color="#5c6370" size="22" />
+ </view>
+</template>
+
+<script>
+const WORKBENCH_URL = '/pages/customer/index'
+
+export default {
+ name: 'CuWorkbenchFab',
+ methods: {
+ goWorkbench () {
+ uni.reLaunch({ url: WORKBENCH_URL })
+ }
+ }
+}
+</script>
diff --git a/h5/main.js b/h5/main.js
index 3250c70..bc3fb14 100644
--- a/h5/main.js
+++ b/h5/main.js
@@ -1,12 +1,14 @@
import App from './App'
import store from './store/index.js'
import uView from "uview-ui"
+import CuWorkbenchFab from '@/components/customer/cu-workbench-fab.vue'
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
Vue.config.productionTip = false
Vue.use(uView)
+Vue.component('cu-workbench-fab', CuWorkbenchFab)
Vue.prototype.$store = store
Vue.prototype.showToast = (str) => {
setTimeout(() => {
diff --git a/h5/pages/customer/bill/detail.vue b/h5/pages/customer/bill/detail.vue
index 3638804..0b3dc49 100644
--- a/h5/pages/customer/bill/detail.vue
+++ b/h5/pages/customer/bill/detail.vue
@@ -1,6 +1,6 @@
<template>
- <view :class="['cu-page cu-bill-detail', needPayAmount > 0 ? 'cu-page--with-footer' : '']" v-if="bill">
+ <view :class="['cu-page cu-bill-detail cu-page--with-fab', needPayAmount > 0 ? 'cu-page--with-footer' : '']" v-if="bill">
<view :class="['cu-bill-detail-hero', heroClass]">
@@ -208,6 +208,8 @@
</view>
+ <cu-workbench-fab />
+
</view>
</template>
diff --git a/h5/pages/customer/bill/list.vue b/h5/pages/customer/bill/list.vue
index ea842a2..2b93418 100644
--- a/h5/pages/customer/bill/list.vue
+++ b/h5/pages/customer/bill/list.vue
@@ -1,6 +1,6 @@
<template>
- <view class="cu-page cu-bill-page">
+ <view class="cu-page cu-page--with-fab cu-bill-page">
<view class="cu-bill-page__header">
@@ -198,6 +198,8 @@
</view>
+ <cu-workbench-fab />
+
</view>
</template>
diff --git a/h5/pages/customer/bill/pay.vue b/h5/pages/customer/bill/pay.vue
index 9910c28..9535376 100644
--- a/h5/pages/customer/bill/pay.vue
+++ b/h5/pages/customer/bill/pay.vue
@@ -1,5 +1,5 @@
<template>
- <view class="cu-page cu-page--with-footer" v-if="billId">
+ <view class="cu-page cu-page--with-footer cu-page--with-fab" v-if="billId">
<view class="cu-detail-hero cu-detail-hero--warm">
<view class="cu-detail-hero__label">鏈缂磋垂</view>
<view class="cu-detail-hero__amount">
@@ -22,6 +22,7 @@
<view class="cu-page-footer">
<view class="cu-btn cu-btn--primary" @click="submit">纭缂磋垂{{ amount ? ' 楼' + amount : '' }}</view>
</view>
+ <cu-workbench-fab />
</view>
</template>
diff --git a/h5/pages/customer/conditioner/recharge.vue b/h5/pages/customer/conditioner/recharge.vue
index 9c06412..9b3bea9 100644
--- a/h5/pages/customer/conditioner/recharge.vue
+++ b/h5/pages/customer/conditioner/recharge.vue
@@ -1,14 +1,10 @@
<template>
- <view class="cu-page cu-page--with-footer">
- <view class="cu-device-summary" v-if="device">
- <view class="cu-row cu-row--between">
- <text class="cu-name">{{ device.deviceName }}</text>
- <text class="cu-status cu-status--ok">{{ device.statusText }}</text>
- </view>
- <view class="cu-line">鎴块棿锛歿{ device.roomInfo }}</view>
+ <view class="cu-page cu-page--with-footer cu-page--with-fab">
+ <view class="cu-device-summary">
+ <view class="cu-device-summary__title">鍟嗘埛绌鸿皟缁熶竴璐︽埛</view>
<view class="cu-device-summary__balance">
- <view class="cu-device-summary__balance-label">褰撳墠璐︽埛浣欓</view>
- <view class="cu-device-summary__balance-value">{{ device.balance }}</view>
+ <view class="cu-device-summary__balance-label">褰撳墠绌鸿皟鐢佃垂浣欓</view>
+ <view :class="['cu-device-summary__balance-value', balanceTone ? 'cu-device-summary__balance-value--' + balanceTone : '']">{{ balanceText }}</view>
</view>
</view>
@@ -35,26 +31,41 @@
<view class="cu-page-footer">
<view class="cu-btn cu-btn--primary" @click="submit">纭鍏呭�納{ amount ? ' 楼' + amount : '' }}</view>
</view>
+ <cu-workbench-fab />
</view>
</template>
<script>
-import { customerDeviceDetail, customerPayCreate } from '@/api'
+import { customerHome, customerPayCreate } from '@/api'
import { invokeWxPay } from '@/utils/wxpay.js'
+import { getBalanceTone } from '@/utils/utils.js'
+
export default {
data () {
return {
- deviceId: null,
- device: null,
+ gsBalance: null,
amount: '',
remark: '',
quickAmounts: [50, 100, 200, 500]
}
},
- onLoad (q) { this.deviceId = q.id; this.load() },
+ computed: {
+ balanceText () {
+ if (this.gsBalance === null || this.gsBalance === undefined || this.gsBalance === '') return '-'
+ const n = Number(this.gsBalance)
+ return Number.isNaN(n) ? this.gsBalance : n.toFixed(2)
+ },
+ balanceTone () {
+ return getBalanceTone(this.gsBalance)
+ }
+ },
+ onShow () { this.load() },
methods: {
load () {
- customerDeviceDetail({ deviceType: 1, deviceId: this.deviceId }).then(res => { this.device = res.data })
+ customerHome().then(res => {
+ const gs = res.data && res.data.gsConfig
+ this.gsBalance = gs ? gs.leftMoney : null
+ })
},
submit () {
if (!this.amount) return uni.showToast({ title: '璇疯緭鍏ラ噾棰�', icon: 'none' })
diff --git a/h5/pages/customer/contract/detail.vue b/h5/pages/customer/contract/detail.vue
index caabd9c..b3934ac 100644
--- a/h5/pages/customer/contract/detail.vue
+++ b/h5/pages/customer/contract/detail.vue
@@ -1,6 +1,6 @@
<template>
- <view class="cu-page cu-page--with-footer" v-if="contract">
+ <view class="cu-page cu-page--with-footer cu-page--with-fab" v-if="contract">
<view :class="['cu-detail-hero', contract.status === 1 ? 'cu-detail-hero--green' : '']">
@@ -606,6 +606,8 @@
</template>
+ <cu-workbench-fab />
+
</view>
</template>
diff --git a/h5/pages/customer/contract/list.vue b/h5/pages/customer/contract/list.vue
index ac70d09..db65d61 100644
--- a/h5/pages/customer/contract/list.vue
+++ b/h5/pages/customer/contract/list.vue
@@ -1,5 +1,5 @@
<template>
- <view class="cu-page">
+ <view class="cu-page cu-page--with-fab">
<scroll-view scroll-x class="cu-tabs cu-tabs--scroll">
<view
v-for="(t, i) in tabs"
@@ -55,6 +55,7 @@
</view>
<u-empty v-if="!list.length" text="鏆傛棤鍚堝悓" margin-top="80" />
</view>
+ <cu-workbench-fab />
</view>
</template>
diff --git a/h5/pages/customer/electricity/list.vue b/h5/pages/customer/electricity/list.vue
index 97e7891..c6e5e9b 100644
--- a/h5/pages/customer/electricity/list.vue
+++ b/h5/pages/customer/electricity/list.vue
@@ -1,95 +1,173 @@
<template>
- <view class="cu-page">
- <view class="cu-filters">
- <picker :range="typeOptions" range-key="label" @change="onTypeChange">
- <view class="cu-filter">{{ typeLabel }} 鈻�</view>
- </picker>
- <picker :range="statusOptions" range-key="label" @change="onStatusChange">
- <view class="cu-filter">{{ statusLabel }} 鈻�</view>
- </picker>
- </view>
+ <view class="cu-page cu-page--with-fab cu-device-page">
+ <view class="cu-device-page__header">
+ <view class="cu-device-type-tabs">
+ <view
+ :class="['cu-device-type-card', 'cu-device-type-card--electric', tabIdx === 0 ? 'cu-device-type-card--active' : '']"
+ @click="switchTab(0)"
+ >
+ <view class="cu-device-type-card__icon-wrap">
+ <text class="cu-device-type-card__icon">鈿�</text>
+ </view>
+ <view class="cu-device-type-card__text">
+ <text class="cu-device-type-card__label">鐢佃〃</text>
+ <text class="cu-device-type-card__hint">鏅鸿兘鐢佃〃</text>
+ </view>
+ </view>
+ <view
+ :class="['cu-device-type-card', 'cu-device-type-card--conditioner', tabIdx === 1 ? 'cu-device-type-card--active' : '']"
+ @click="switchTab(1)"
+ >
+ <view class="cu-device-type-card__icon-wrap">
+ <text class="cu-device-type-card__icon">鉂勶笍</text>
+ </view>
+ <view class="cu-device-type-card__text">
+ <text class="cu-device-type-card__label">绌鸿皟</text>
+ <text class="cu-device-type-card__hint">绌鸿皟璁惧</text>
+ </view>
+ </view>
+ </view>
- <view class="cu-list-header">
- <text class="cu-list-header__count">鍏� {{ list.length }} 鍙拌澶�</text>
+ <view v-if="tabIdx === 1" class="cu-ac-balance-card">
+ <view class="cu-ac-balance-card__main">
+ <text class="cu-ac-balance-card__label">绌鸿皟鐢佃垂浣欓</text>
+ <text :class="['cu-ac-balance-card__value', acBalanceTone ? 'cu-ac-balance-card__value--' + acBalanceTone : '']">{{ acBalanceText }}</text>
+ </view>
+ <view class="cu-ac-balance-card__btn" @click="goAcRecharge">鍘诲厖鍊�</view>
+ </view>
+
+ <view class="cu-list-header cu-list-header--inline">
+ <text class="cu-list-header__count">鍏� {{ list.length }} 鍙皗{ tabIdx === 0 ? '鐢佃〃' : '绌鸿皟' }}</text>
+ </view>
</view>
<view class="cu-list-wrap">
- <view v-for="item in list" :key="item.deviceType + '-' + item.deviceId" class="cu-list-card">
- <view class="cu-list-card__head">
- <view :class="['cu-list-card__icon', item.deviceType === 0 ? 'cu-list-card__icon--electric' : 'cu-list-card__icon--conditioner']">
- {{ item.deviceType === 0 ? '鈿�' : '鉂勶笍' }}
- </view>
- <view class="cu-list-card__main">
- <view class="cu-list-card__title-row">
- <text class="cu-list-card__title">{{ item.deviceName }}</text>
- <text :class="['cu-status', item.statusCode === 1 ? 'cu-status--ok' : 'cu-status--bad']">{{ item.statusText }}</text>
+ <template v-if="tabIdx === 0">
+ <view v-for="item in list" :key="item.deviceType + '-' + item.deviceId" class="cu-list-card">
+ <view class="cu-list-card__head">
+ <view class="cu-list-card__icon cu-list-card__icon--electric">鈿�</view>
+ <view class="cu-list-card__main">
+ <view class="cu-list-card__title-row">
+ <view class="cu-list-card__title-wrap">
+ <text class="cu-list-card__title">{{ item.deviceName }}</text>
+ </view>
+ <text :class="['cu-status', item.statusCode === 1 ? 'cu-status--ok' : 'cu-status--bad']">{{ item.statusText }}</text>
+ </view>
+ <text class="cu-list-card__sub">{{ item.roomInfo || '鏆傛棤鎴块棿淇℃伅' }}</text>
</view>
- <text class="cu-list-card__sub">{{ item.roomInfo || '鏆傛棤鎴块棿淇℃伅' }}</text>
+ </view>
+
+ <view v-if="item.alarmTags && item.alarmTags.length" class="cu-list-card__tags">
+ <text v-for="tag in item.alarmTags" :key="tag" class="cu-tag">{{ tag }}</text>
+ </view>
+
+ <view class="cu-info-grid">
+ <view class="cu-info-cell">
+ <text class="cu-info-cell__label">鐢佃〃鍦板潃</text>
+ <text class="cu-info-cell__value">{{ item.meterAddress || '-' }}</text>
+ </view>
+ <view class="cu-info-cell">
+ <text class="cu-info-cell__label">璐︽埛浣欓</text>
+ <text :class="['cu-info-cell__value', balanceToneClass(item.balance)]">{{ formatBalance(item.balance) }}</text>
+ </view>
+ </view>
+
+ <view class="cu-list-card__foot">
+ <text class="cu-time">鏇存柊 {{ formatTime(item.updateTime) }}</text>
+ <text class="cu-list-card__arrow" @click="goElectricRecharge(item)">鍘诲厖鍊� 鈫�</text>
</view>
</view>
+ </template>
- <view v-if="item.alarmTags && item.alarmTags.length" class="cu-list-card__tags">
- <text v-for="tag in item.alarmTags" :key="tag" class="cu-tag">{{ tag }}</text>
- </view>
-
- <view class="cu-info-grid">
- <view v-if="item.deviceType === 0" class="cu-info-cell">
- <text class="cu-info-cell__label">鐢佃〃鎴峰彿</text>
- <text class="cu-info-cell__value">{{ item.meterAccountNo || '-' }}</text>
+ <template v-else>
+ <view v-for="item in list" :key="item.deviceType + '-' + item.deviceId" class="cu-list-card cu-list-card--readonly">
+ <view class="cu-list-card__head">
+ <view class="cu-list-card__icon cu-list-card__icon--conditioner">鉂勶笍</view>
+ <view class="cu-list-card__main">
+ <view class="cu-list-card__title-row">
+ <view class="cu-list-card__title-wrap">
+ <text class="cu-list-card__title">{{ item.deviceName }}</text>
+ </view>
+ <text :class="['cu-status', item.statusCode === 1 ? 'cu-status--ok' : 'cu-status--bad']">{{ item.statusText }}</text>
+ </view>
+ <text class="cu-list-card__sub">{{ item.roomInfo || '鏆傛棤鎴块棿淇℃伅' }}</text>
+ </view>
</view>
- <view :class="['cu-info-cell', item.deviceType !== 0 ? 'cu-info-cell--full' : '']">
- <text class="cu-info-cell__label">璐︽埛浣欓</text>
- <text :class="['cu-info-cell__value', item.balanceLow ? 'cu-info-cell__value--danger' : '']">{{ item.balance }}</text>
+
+ <view class="cu-list-card__meta-row">
+ <text class="cu-time">鏇存柊 {{ formatTime(item.updateTime) }}</text>
</view>
</view>
+ </template>
- <view class="cu-list-card__foot">
- <text class="cu-time">鏇存柊 {{ formatTime(item.updateTime) }}</text>
- <text class="cu-list-card__arrow" @click="goRecharge(item)">鍘诲厖鍊� 鈫�</text>
- </view>
- </view>
- <u-empty v-if="!list.length" text="鏆傛棤璁惧" margin-top="80" />
+ <u-empty v-if="!list.length" :text="tabIdx === 0 ? '鏆傛棤鐢佃〃' : '鏆傛棤绌鸿皟璁惧'" margin-top="80" />
</view>
+ <cu-workbench-fab />
</view>
</template>
<script>
-import { customerDevicePage } from '@/api'
+import { customerDevicePage, customerHome } from '@/api'
+import { getBalanceTone } from '@/utils/utils.js'
+
export default {
data () {
return {
list: [],
- typeOptions: [{ label: '鍏ㄩ儴绫诲瀷', value: null }, { label: '鐢佃〃', value: 0 }, { label: '绌鸿皟', value: 1 }],
- statusOptions: [{ label: '鍏ㄩ儴鐘舵��', value: null }, { label: '姝e父', value: 1 }, { label: '寮傚父', value: 2 }],
- typeIdx: 0,
- statusIdx: 0,
+ tabIdx: 0,
+ gsBalance: null,
page: 1
}
},
computed: {
- typeLabel () { return this.typeOptions[this.typeIdx].label },
- statusLabel () { return this.statusOptions[this.statusIdx].label }
+ acBalanceText () {
+ if (this.gsBalance === null || this.gsBalance === undefined || this.gsBalance === '') return '-'
+ return this.formatBalance(this.gsBalance)
+ },
+ acBalanceTone () {
+ return getBalanceTone(this.gsBalance)
+ }
},
- onShow () { this.load() },
+ onShow () {
+ this.load()
+ if (this.tabIdx === 1) this.loadGsBalance()
+ },
methods: {
load () {
customerDevicePage({
page: this.page,
- capacity: 20,
- model: {
- deviceType: this.typeOptions[this.typeIdx].value,
- statusFilter: this.statusOptions[this.statusIdx].value
- }
+ capacity: 200,
+ model: { deviceType: this.tabIdx === 0 ? 0 : 1 }
}).then(res => { this.list = (res.data && res.data.records) || [] })
},
- onTypeChange (e) { this.typeIdx = Number(e.detail.value); this.load() },
- onStatusChange (e) { this.statusIdx = Number(e.detail.value); this.load() },
+ loadGsBalance () {
+ customerHome().then(res => {
+ const gs = res.data && res.data.gsConfig
+ this.gsBalance = gs ? gs.leftMoney : null
+ })
+ },
+ switchTab (i) {
+ if (this.tabIdx === i) return
+ this.tabIdx = i
+ this.list = []
+ this.load()
+ if (i === 1) this.loadGsBalance()
+ },
+ formatBalance (val) {
+ if (val === null || val === undefined || val === '') return '-'
+ const n = Number(val)
+ return Number.isNaN(n) ? val : n.toFixed(2)
+ },
+ balanceToneClass (val) {
+ const tone = getBalanceTone(val)
+ return tone ? `cu-info-cell__value--${tone}` : ''
+ },
formatTime (t) { return t ? String(t).replace('T', ' ').substring(0, 19) : '-' },
- goRecharge (item) {
- const url = item.deviceType === 0
- ? `/pages/customer/electricity/recharge?id=${item.deviceId}`
- : `/pages/customer/conditioner/recharge?id=${item.deviceId}`
- uni.navigateTo({ url })
+ goElectricRecharge (item) {
+ uni.navigateTo({ url: `/pages/customer/electricity/recharge?id=${item.deviceId}` })
+ },
+ goAcRecharge () {
+ uni.navigateTo({ url: '/pages/customer/conditioner/recharge' })
}
}
}
diff --git a/h5/pages/customer/electricity/recharge.vue b/h5/pages/customer/electricity/recharge.vue
index 94bacbc..b8789cd 100644
--- a/h5/pages/customer/electricity/recharge.vue
+++ b/h5/pages/customer/electricity/recharge.vue
@@ -1,15 +1,31 @@
<template>
- <view class="cu-page cu-page--with-footer">
- <view class="cu-device-summary" v-if="device">
- <view class="cu-row cu-row--between">
- <text class="cu-name">{{ device.deviceName }}</text>
- <text class="cu-status cu-status--ok">{{ device.statusText }}</text>
+ <view class="cu-page cu-page--with-footer cu-page--with-fab">
+ <view v-if="device" class="cu-recharge-hero cu-recharge-hero--electric">
+ <view class="cu-recharge-hero__gradient">
+ <view class="cu-recharge-hero__balance-block">
+ <text class="cu-recharge-hero__balance-label">褰撳墠璐︽埛浣欓锛堝厓锛�</text>
+ <text :class="['cu-recharge-hero__balance-value', balanceTone ? 'cu-recharge-hero__balance-value--' + balanceTone : '']">{{ balanceText }}</text>
+ </view>
</view>
- <view class="cu-line">鎴块棿锛歿{ device.roomInfo }}</view>
- <view class="cu-line">鎴峰彿锛歿{ device.meterAccountNo }}</view>
- <view class="cu-device-summary__balance">
- <view class="cu-device-summary__balance-label">褰撳墠璐︽埛浣欓</view>
- <view class="cu-device-summary__balance-value">{{ device.balance }}</view>
+ <view class="cu-recharge-hero__panel">
+ <view class="cu-recharge-hero__device">
+ <view class="cu-recharge-hero__icon cu-recharge-hero__icon--electric">鈿�</view>
+ <view class="cu-recharge-hero__device-main">
+ <view class="cu-recharge-hero__title-row">
+ <view class="cu-recharge-hero__title-wrap">
+ <text class="cu-recharge-hero__title">{{ device.deviceName || '鐢佃〃' }}</text>
+ </view>
+ <text :class="['cu-recharge-hero__status', device.statusCode === 1 ? 'cu-recharge-hero__status--ok' : 'cu-recharge-hero__status--bad']">{{ device.statusText || '鏈煡' }}</text>
+ </view>
+ <text class="cu-recharge-hero__sub">{{ device.roomInfo || '鏆傛棤鎴块棿淇℃伅' }}</text>
+ </view>
+ </view>
+ <view class="cu-recharge-hero__meta">
+ <view class="cu-recharge-hero__meta-item">
+ <text class="cu-recharge-hero__meta-label">鐢佃〃鍦板潃</text>
+ <text class="cu-recharge-hero__meta-value">{{ device.meterAddress || '-' }}</text>
+ </view>
+ </view>
</view>
</view>
@@ -36,12 +52,14 @@
<view class="cu-page-footer">
<view class="cu-btn cu-btn--primary" @click="submit">纭鍏呭�納{ amount ? ' 楼' + amount : '' }}</view>
</view>
+ <cu-workbench-fab />
</view>
</template>
<script>
import { customerDeviceDetail, customerPayCreate } from '@/api'
import { invokeWxPay } from '@/utils/wxpay.js'
+import { getBalanceTone } from '@/utils/utils.js'
export default {
data () {
return {
@@ -53,6 +71,16 @@
}
},
onLoad (q) { this.deviceId = q.id; this.load() },
+ computed: {
+ balanceText () {
+ if (!this.device || this.device.balance === null || this.device.balance === undefined || this.device.balance === '') return '-'
+ const n = Number(this.device.balance)
+ return Number.isNaN(n) ? this.device.balance : n.toFixed(2)
+ },
+ balanceTone () {
+ return getBalanceTone(this.device && this.device.balance)
+ }
+ },
methods: {
load () {
customerDeviceDetail({ deviceType: 0, deviceId: this.deviceId }).then(res => { this.device = res.data })
diff --git a/h5/pages/customer/index.vue b/h5/pages/customer/index.vue
index 6948e8a..125a7c5 100644
--- a/h5/pages/customer/index.vue
+++ b/h5/pages/customer/index.vue
@@ -1,11 +1,19 @@
<template>
<view class="cu-page cu-page--home">
<view class="cu-hero">
- <view class="cu-hero__greet">
- <view class="cu-avatar">{{ customerInitial }}</view>
- <view>
- <view class="cu-hero__hi">{{ greeting }}</view>
- <view class="cu-hero__name">{{ home.customerName || '鍟嗘埛鐢ㄦ埛' }}</view>
+ <view class="cu-profile-bar">
+ <view class="cu-profile-bar__info">
+ <view class="cu-avatar">{{ customerInitial }}</view>
+ <view>
+ <view class="cu-hero__hi">{{ greeting }}</view>
+ <view class="cu-hero__name">{{ displayName }}</view>
+ </view>
+ </view>
+ <view class="cu-profile-actions">
+ <view class="cu-profile-action cu-profile-action--pill" @click="logout">
+ <u-icon name="minus-circle-fill" color="#ffffff" size="18" />
+ <text class="cu-profile-action__text">閫�鍑�</text>
+ </view>
</view>
</view>
</view>
@@ -15,43 +23,61 @@
<u-swiper :list="banners" keyName="imageUrl" height="160" radius="12" indicator indicatorMode="dot" />
</view>
- <view class="cu-section-title">涓撳睘鏈嶅姟</view>
- <view class="cu-service-grid">
- <view class="cu-service-item cu-service-item--electric" @click="go('/pages/customer/electricity/list')">
- <view class="cu-service-item__icon">鈿�</view>
- <text class="cu-service-item__label">浜ょ數璐�</text>
- <text class="cu-service-item__desc">鐢佃〃 / 绌鸿皟鍏呭��</text>
+ <view class="cu-service-panel">
+ <view class="cu-section-head">
+ <view class="cu-section-head__bar" />
+ <text class="cu-section-head__title">涓撳睘鏈嶅姟</text>
</view>
- <view class="cu-service-item cu-service-item--contract" @click="go('/pages/customer/contract/list')">
- <view class="cu-service-item__icon">
- <u-icon name="file-text-fill" color="#40a9ff" size="24" />
+ <view class="cu-service-grid">
+ <view class="cu-service-card cu-service-card--electric" @click="go('/pages/customer/electricity/list')">
+ <view class="cu-service-card__top">
+ <view class="cu-service-card__icon">
+ <u-icon name="coupon-fill" color="#fa8c16" size="26" />
+ </view>
+ <text class="cu-service-card__arrow">鈥�</text>
+ </view>
+ <text class="cu-service-card__label">浜ょ數璐�</text>
+ <text class="cu-service-card__desc">鐢佃〃 / 绌鸿皟鍏呭��</text>
</view>
- <text class="cu-service-item__label">鏌ュ悎鍚�</text>
- <text class="cu-service-item__desc">绉熻祦鍚堝悓鏌ヨ</text>
+ <view class="cu-service-card cu-service-card--contract" @click="go('/pages/customer/contract/list')">
+ <view class="cu-service-card__top">
+ <view class="cu-service-card__icon">
+ <u-icon name="file-text-fill" color="#40a9ff" size="26" />
+ </view>
+ <text class="cu-service-card__arrow">鈥�</text>
+ </view>
+ <text class="cu-service-card__label">鏌ュ悎鍚�</text>
+ <text class="cu-service-card__desc">绉熻祦鍚堝悓鏌ヨ</text>
+ </view>
+ <view class="cu-service-card cu-service-card--bill" @click="go('/pages/customer/bill/list')">
+ <view class="cu-service-card__top">
+ <view class="cu-service-card__icon">
+ <u-icon name="red-packet-fill" color="#597ef7" size="26" />
+ </view>
+ <text class="cu-service-card__arrow">鈥�</text>
+ </view>
+ <text class="cu-service-card__label">鏌ヨ处鍗�</text>
+ <text class="cu-service-card__desc">鍦ㄧ嚎缂磋垂</text>
+ </view>
+ <view class="cu-service-card cu-service-card--record" @click="go('/pages/customer/recharge/record')">
+ <view class="cu-service-card__top">
+ <view class="cu-service-card__icon">
+ <u-icon name="list" color="#9254de" size="26" />
+ </view>
+ <text class="cu-service-card__arrow">鈥�</text>
+ </view>
+ <text class="cu-service-card__label">鍏呭�艰褰�</text>
+ <text class="cu-service-card__desc">鍘嗗彶鍏呭�兼槑缁�</text>
+ </view>
</view>
- <view class="cu-service-item cu-service-item--bill" @click="go('/pages/customer/bill/list')">
- <view class="cu-service-item__icon">馃挸</view>
- <text class="cu-service-item__label">鏌ヨ处鍗�</text>
- <text class="cu-service-item__desc">鍦ㄧ嚎缂磋垂</text>
- </view>
- <view class="cu-service-item cu-service-item--record" @click="go('/pages/customer/recharge/record')">
- <view class="cu-service-item__icon">馃搵</view>
- <text class="cu-service-item__label">鍏呭�艰褰�</text>
- <text class="cu-service-item__desc">鍘嗗彶鍏呭�兼槑缁�</text>
- </view>
- </view>
-
- <view class="cu-footer-bar">
- <view class="cu-footer-btn cu-footer-btn--primary" @click="onSwitchRole">鍒囨崲瑙掕壊</view>
- <view class="cu-footer-btn" @click="logout">閫�鍑虹櫥褰�</view>
</view>
</view>
</view>
</template>
<script>
-import { customerBanners, customerHome } from '@/api'
-import { switchRole, goRoleSelect } from '@/utils/roleSwitch.js'
+import { customerBanners, customerHome, customerLogout } from '@/api'
+import { switchRole } from '@/utils/roleSwitch.js'
export default {
data () {
return { banners: [], home: {} }
@@ -63,8 +89,11 @@
if (h < 18) return '涓嬪崍濂�'
return '鏅氫笂濂�'
},
+ displayName () {
+ return this.home.displayName || this.home.customerName || '鍟嗘埛鐢ㄦ埛'
+ },
customerInitial () {
- const name = (this.home.customerName || '鍟�').trim()
+ const name = (this.home.memberName || this.home.customerName || '鍟�').trim()
return name.charAt(0)
}
},
@@ -76,8 +105,7 @@
},
methods: {
go (url) { uni.navigateTo({ url }) },
- onSwitchRole () { switchRole() },
- logout () { goRoleSelect() }
+ logout () { switchRole(customerLogout) }
}
}
</script>
diff --git a/h5/pages/customer/login.vue b/h5/pages/customer/login.vue
index 492b5b7..f17c286 100644
--- a/h5/pages/customer/login.vue
+++ b/h5/pages/customer/login.vue
@@ -1,5 +1,11 @@
<template>
<view class="cu-login">
+ <view class="cu-auth-topbar">
+ <view class="cu-auth-topbar__btn" @click="goRoleSelect">
+ <u-icon name="reload" color="#2080f7" size="22" />
+ </view>
+ </view>
+
<view class="cu-login__brand">
<view class="cu-login__title">鍟嗘埛鐧诲綍</view>
<view class="cu-login__sub">闃滃畞鏂囦綋涓績 路 鍟嗘埛鏈嶅姟骞冲彴</view>
@@ -52,6 +58,9 @@
},
methods: {
...mapMutations(['setToken', 'setUserInfo', 'setOpenId', 'setUserType']),
+ goRoleSelect () {
+ uni.redirectTo({ url: '/pages/roleSelect?switch=1' })
+ },
onLogin () {
if (!this.form.phone || !this.form.code) {
return uni.showToast({ title: '璇峰~鍐欐墜鏈哄彿鍜岄獙璇佺爜', icon: 'none' })
diff --git a/h5/pages/customer/pay/result.vue b/h5/pages/customer/pay/result.vue
index fe628e4..a514108 100644
--- a/h5/pages/customer/pay/result.vue
+++ b/h5/pages/customer/pay/result.vue
@@ -1,5 +1,5 @@
<template>
- <view class="cu-result">
+ <view class="cu-result cu-page--with-fab">
<view :class="['cu-result__icon', success ? 'cu-result__icon--ok' : 'cu-result__icon--fail']">
{{ success ? '鉁�' : '鉁�' }}
</view>
@@ -9,6 +9,7 @@
</view>
<view class="cu-btn cu-btn--primary" @click="goRecord">{{ type === 'bill' ? '鏌ョ湅璐﹀崟鏄庣粏' : '鏌ョ湅鍏呭�艰褰�' }}</view>
<view class="cu-btn" @click="goHome">杩斿洖涓婚〉</view>
+ <cu-workbench-fab />
</view>
</template>
diff --git a/h5/pages/customer/recharge/record.vue b/h5/pages/customer/recharge/record.vue
index 7b4d931..0e13d36 100644
--- a/h5/pages/customer/recharge/record.vue
+++ b/h5/pages/customer/recharge/record.vue
@@ -1,83 +1,235 @@
<template>
- <view class="cu-page">
- <view class="cu-filters">
- <picker :range="statusOptions" range-key="label" @change="onStatusChange">
- <view class="cu-filter">{{ statusLabel }} 鈻�</view>
- </picker>
- <picker mode="date" fields="month" @change="onMonthChange">
- <view class="cu-filter">{{ month || '鍏呭�兼湀浠�' }} 鈻�</view>
- </picker>
- </view>
+ <view class="cu-page cu-page--with-fab cu-recharge-record-page">
+ <view class="cu-recharge-record-page__header">
+ <view class="cu-recharge-filter-panel">
+ <view class="cu-recharge-filter-panel__section">
+ <text class="cu-recharge-filter-panel__label">鍏呭�肩姸鎬�</text>
+ <scroll-view scroll-x class="cu-recharge-status-tabs" :show-scrollbar="false">
+ <view
+ v-for="(opt, i) in statusOptions"
+ :key="opt.value == null ? 'all' : opt.value"
+ :class="['cu-recharge-status-tab', statusIdx === i ? 'cu-recharge-status-tab--active' : '', opt.tabClass]"
+ @click="switchStatus(i)"
+ >{{ opt.label }}</view>
+ </scroll-view>
+ </view>
- <view class="cu-list-header">
- <text class="cu-list-header__count">鍏� {{ list.length }} 鏉¤褰�</text>
+ <view class="cu-recharge-filter-panel__divider" />
+
+ <view class="cu-recharge-filter-panel__section">
+ <view class="cu-recharge-filter-panel__row">
+ <text class="cu-recharge-filter-panel__label">鏃堕棿绛涢��</text>
+ <view class="cu-recharge-date-mode">
+ <view
+ :class="['cu-recharge-date-mode__item', dateMode === 'month' ? 'cu-recharge-date-mode__item--active' : '']"
+ @click="switchDateMode('month')"
+ >鎸夋湀</view>
+ <view
+ :class="['cu-recharge-date-mode__item', dateMode === 'range' ? 'cu-recharge-date-mode__item--active' : '']"
+ @click="switchDateMode('range')"
+ >鎸夋椂闂存</view>
+ </view>
+ </view>
+
+ <view class="cu-recharge-filter-panel__dates">
+ <template v-if="dateMode === 'month'">
+ <picker mode="date" fields="month" :value="monthPickerValue" @change="onMonthChange">
+ <view :class="['cu-recharge-date-field', month ? 'cu-recharge-date-field--active' : '']">
+ <text class="cu-recharge-date-field__text">{{ monthLabel }}</text>
+ <text class="cu-recharge-date-field__arrow">鈻�</text>
+ </view>
+ </picker>
+ <text v-if="month" class="cu-recharge-date-clear" @click="clearMonth">閲嶇疆</text>
+ </template>
+
+ <template v-else>
+ <view class="cu-recharge-date-range">
+ <picker mode="date" fields="day" :value="dateStart || today" @change="onDateStartChange">
+ <view :class="['cu-recharge-date-field', dateStart ? 'cu-recharge-date-field--active' : '']">
+ <text class="cu-recharge-date-field__text">{{ dateStart || '寮�濮嬫棩鏈�' }}</text>
+ <text class="cu-recharge-date-field__arrow">鈻�</text>
+ </view>
+ </picker>
+ <text class="cu-recharge-date-sep">鑷�</text>
+ <picker mode="date" fields="day" :value="dateEnd || today" @change="onDateEndChange">
+ <view :class="['cu-recharge-date-field', dateEnd ? 'cu-recharge-date-field--active' : '']">
+ <text class="cu-recharge-date-field__text">{{ dateEnd || '缁撴潫鏃ユ湡' }}</text>
+ <text class="cu-recharge-date-field__arrow">鈻�</text>
+ </view>
+ </picker>
+ </view>
+ <text v-if="dateStart || dateEnd" class="cu-recharge-date-clear" @click="clearRange">閲嶇疆</text>
+ </template>
+ </view>
+ </view>
+ </view>
+
+ <view class="cu-recharge-summary">
+ <text class="cu-recharge-summary__count">{{ list.length }}</text>
+ <text class="cu-recharge-summary__label">鏉″厖鍊艰褰�</text>
+ </view>
</view>
<view class="cu-list-wrap">
- <view v-for="item in list" :key="item.id" class="cu-list-card">
- <view class="cu-list-card__head">
- <view class="cu-list-card__icon cu-list-card__icon--record">馃搵</view>
- <view class="cu-list-card__main">
- <view class="cu-list-card__title-row">
- <text class="cu-list-card__title">{{ item.deviceInfo || item.name || '鍏呭�艰褰�' }}</text>
- <text :class="['cu-status', statusClass(item.status)]">{{ item.statusText }}</text>
+ <view v-for="item in list" :key="item.id" class="cu-recharge-card">
+ <view :class="['cu-recharge-card__accent', accentClass(item)]" />
+ <view class="cu-recharge-card__body">
+ <view class="cu-recharge-card__top">
+ <view :class="['cu-recharge-card__type', typeTagClass(item.type)]">
+ <text class="cu-recharge-card__type-icon">{{ item.type === 1 ? '鉂�' : '鈿�' }}</text>
+ <text class="cu-recharge-card__type-text">{{ typeText(item.type) }}</text>
</view>
- <text class="cu-list-card__sub" v-if="item.address">鎴峰彿 {{ item.address }}</text>
+ <text :class="['cu-status', statusClass(item.status)]">{{ item.statusText }}</text>
</view>
- </view>
- <view class="cu-info-grid">
- <view class="cu-info-cell">
- <text class="cu-info-cell__label">鍏呭�奸噾棰�</text>
- <text class="cu-info-cell__value cu-info-cell__value--primary">楼{{ item.money }}</text>
+ <text class="cu-recharge-card__title">{{ item.deviceInfo || item.name || '鍏呭�艰褰�' }}</text>
+ <text v-if="item.rechargeUserName" class="cu-recharge-card__operator">鍏呭�间汉 {{ item.rechargeUserName }}</text>
+
+ <view class="cu-recharge-card__amount-box">
+ <view class="cu-recharge-card__amount-main">
+ <text class="cu-recharge-card__amount-label">鍏呭�奸噾棰�</text>
+ <text class="cu-recharge-card__amount-value">楼{{ formatMoney(item.money) }}</text>
+ </view>
+ <view class="cu-recharge-card__amount-side">
+ <text class="cu-recharge-card__amount-label">鍏呭悗浣欓</text>
+ <text :class="['cu-recharge-card__amount-side-value', balanceToneClass(item.balanceAfter)]">{{ formatMoney(item.balanceAfter) }}</text>
+ </view>
</view>
- <view class="cu-info-cell">
- <text class="cu-info-cell__label">鍏呭悗浣欓</text>
- <text class="cu-info-cell__value">{{ item.balanceAfter }}</text>
- </view>
- <view class="cu-info-cell cu-info-cell--full">
- <text class="cu-info-cell__label">鍏呭�兼椂闂�</text>
- <text class="cu-info-cell__value">{{ item.createDate }}</text>
+
+ <view class="cu-recharge-card__foot">
+ <text class="cu-recharge-card__time">{{ formatTime(item.createDate) }}</text>
</view>
</view>
</view>
<u-empty v-if="!list.length" text="鏆傛棤璁板綍" margin-top="80" />
</view>
+ <cu-workbench-fab />
</view>
</template>
<script>
import { customerRechargeRecordPage } from '@/api'
+import { getBalanceTone } from '@/utils/utils.js'
+
+const STATUS_OPTIONS = [
+ { label: '鍏ㄩ儴', value: null, tabClass: '' },
+ { label: '鎴愬姛', value: 1, tabClass: 'cu-recharge-status-tab--ok' },
+ { label: '澶辫触', value: 2, tabClass: 'cu-recharge-status-tab--bad' },
+ { label: '鍏呭�间腑', value: 0, tabClass: 'cu-recharge-status-tab--pending' }
+]
+
export default {
data () {
+ const now = new Date()
+ const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
return {
list: [],
+ dateMode: 'month',
month: '',
+ dateStart: '',
+ dateEnd: '',
+ today,
statusIdx: 0,
- statusOptions: [
- { label: '鍏ㄩ儴鐘舵��', value: null },
- { label: '鍏呭�兼垚鍔�', value: 1 },
- { label: '鍏呭�煎け璐�', value: 2 },
- { label: '鍏呭�间腑', value: 0 }
- ]
+ statusOptions: STATUS_OPTIONS
}
},
- computed: { statusLabel () { return this.statusOptions[this.statusIdx].label } },
- onShow () { this.load() },
+ computed: {
+ monthPickerValue () {
+ return this.month || this.today.substring(0, 7)
+ },
+ monthLabel () {
+ if (!this.month) return '閫夋嫨鏈堜唤'
+ const parts = this.month.split('-')
+ if (parts.length >= 2) return `${parts[0]}骞�${parts[1]}鏈坄
+ return this.month
+ }
+ },
+ onShow () {
+ this.load()
+ },
methods: {
load () {
+ const model = { status: this.statusOptions[this.statusIdx].value }
+ if (this.dateMode === 'month') {
+ if (this.month) model.month = this.month
+ } else {
+ if (this.dateStart) model.createTimeBegin = `${this.dateStart} 00:00:00`
+ if (this.dateEnd) model.createTimeEnd = `${this.dateEnd} 23:59:59`
+ }
customerRechargeRecordPage({
page: 1,
capacity: 50,
- model: { status: this.statusOptions[this.statusIdx].value, month: this.month || null }
+ model
}).then(res => { this.list = (res.data && res.data.records) || [] })
},
- onStatusChange (e) { this.statusIdx = Number(e.detail.value); this.load() },
- onMonthChange (e) { this.month = e.detail.value; this.load() },
+ switchStatus (i) {
+ if (this.statusIdx === i) return
+ this.statusIdx = i
+ this.load()
+ },
+ switchDateMode (mode) {
+ if (this.dateMode === mode) return
+ this.dateMode = mode
+ if (mode === 'month') {
+ this.dateStart = ''
+ this.dateEnd = ''
+ } else {
+ this.month = ''
+ }
+ this.load()
+ },
+ onMonthChange (e) {
+ this.month = e.detail.value
+ this.load()
+ },
+ onDateStartChange (e) {
+ this.dateStart = e.detail.value
+ if (this.dateEnd && this.dateStart > this.dateEnd) {
+ this.dateEnd = this.dateStart
+ }
+ this.load()
+ },
+ onDateEndChange (e) {
+ this.dateEnd = e.detail.value
+ if (this.dateStart && this.dateEnd < this.dateStart) {
+ this.dateStart = this.dateEnd
+ }
+ this.load()
+ },
+ clearMonth () {
+ this.month = ''
+ this.load()
+ },
+ clearRange () {
+ this.dateStart = ''
+ this.dateEnd = ''
+ this.load()
+ },
+ typeText (type) {
+ return type === 1 ? '绌鸿皟鍏呭��' : '鐢佃〃鍏呭��'
+ },
+ typeTagClass (type) {
+ return type === 1 ? 'cu-recharge-card__type--conditioner' : 'cu-recharge-card__type--electric'
+ },
+ accentClass (item) {
+ return item.type === 1 ? 'cu-recharge-card__accent--conditioner' : 'cu-recharge-card__accent--electric'
+ },
statusClass (s) {
if (s === 1) return 'cu-status--ok'
if (s === 2) return 'cu-status--bad'
return 'cu-status--warn'
+ },
+ formatMoney (val) {
+ if (val === null || val === undefined || val === '') return '-'
+ const n = Number(val)
+ return Number.isNaN(n) ? val : n.toFixed(2)
+ },
+ formatTime (t) {
+ return t ? String(t).replace('T', ' ').substring(0, 19) : '-'
+ },
+ balanceToneClass (val) {
+ const tone = getBalanceTone(val)
+ return tone ? `cu-recharge-card__amount-side-value--${tone}` : ''
}
}
}
diff --git a/h5/pages/index.vue b/h5/pages/index.vue
index ba479e2..915a056 100644
--- a/h5/pages/index.vue
+++ b/h5/pages/index.vue
@@ -1,39 +1,51 @@
<template>
- <view class="main_app">
- <view class="hone_name title">{{ userInfo.realname }}锛屾杩庣櫥褰曪綖</view>
- <view class="home_con">
- <image class="bg" src="@/static/home/home_bg.jpg" mode=""></image>
- <view class="h1">闃滃畞鏂囦綋涓績</view>
- <view class="h2">娆㈣繋浣�</view>
- </view>
- <view class="title">涓氬姟鍔炵悊</view>
- <view class="list">
- <view v-for="item in list1" class="item" @click="itemClick(item)">
- <image :src="item.img"></image>
- <view class="name">{{item.name}}</view>
+ <view class="cu-page ops-home">
+ <view class="cu-hero">
+ <view class="cu-profile-bar">
+ <view class="cu-profile-bar__info">
+ <view class="cu-avatar">{{ userInitial }}</view>
+ <view>
+ <view class="cu-hero__hi">{{ greeting }}</view>
+ <view class="cu-hero__name">{{ userInfo.realname || '杩愮淮浜哄憳' }}</view>
+ </view>
+ </view>
+ <view class="cu-profile-actions">
+ <view class="cu-profile-action cu-profile-action--pill" @click="loginOut">
+ <u-icon name="minus-circle-fill" color="#ffffff" size="18" />
+ <text class="cu-profile-action__text">閫�鍑�</text>
+ </view>
+ </view>
</view>
</view>
- <view class="title">涓氬姟鏌ヨ</view>
- <view class="list">
- <view v-for="item in list2" class="item" @click="itemClick(item)">
- <image :src="item.img"></image>
- <view class="name">{{item.name}}</view>
- <view v-if="item.name == '寰呭姙涓績' && taskNum" class="superscript">{{taskNum}}</view>
+
+ <view class="ops-home__body">
+ <view class="home_con">
+ <image class="bg" src="@/static/home/home_bg.jpg" mode=""></image>
+ <view class="h1">闃滃畞鏂囦綋涓績</view>
+ <view class="h2">娆㈣繋浣�</view>
</view>
- </view>
- <view class="footer-actions">
- <view class="switch-role" @click="switchRole">鍒囨崲瑙掕壊</view>
- <view class="loginout" @click="loginOut">閫�鍑虹櫥褰�</view>
+ <view class="title">涓氬姟鍔炵悊</view>
+ <view class="list">
+ <view v-for="item in list1" :key="item.name" class="item" @click="itemClick(item)">
+ <image :src="item.img"></image>
+ <view class="name">{{ item.name }}</view>
+ </view>
+ </view>
+ <view class="title">涓氬姟鏌ヨ</view>
+ <view class="list list--last">
+ <view v-for="item in list2" :key="item.name" class="item" @click="itemClick(item)">
+ <image :src="item.img"></image>
+ <view class="name">{{ item.name }}</view>
+ <view v-if="item.name == '寰呭姙涓績' && taskNum" class="superscript">{{ taskNum }}</view>
+ </view>
+ </view>
</view>
</view>
</template>
<script>
- import {
- logoutPost,
- myNoticesH5
- } from '@/api'
- import { switchRole as doSwitchRole, goRoleSelect } from '@/utils/roleSwitch.js'
+ import { logoutPost, myNoticesH5 } from '@/api'
+ import { goRoleSelect } from '@/utils/roleSwitch.js'
export default {
data() {
return {
@@ -85,8 +97,21 @@
taskNum: 0
}
},
+ computed: {
+ greeting () {
+ const h = new Date().getHours()
+ if (h < 12) return '鏃╀笂濂�'
+ if (h < 18) return '涓嬪崍濂�'
+ return '鏅氫笂濂�'
+ },
+ userInitial () {
+ const name = (this.userInfo.realname || '杩�').trim()
+ return name.charAt(0)
+ }
+ },
onShow() {
- myNoticesH5({ page: 1, capacity: 1,model: {status: 0}}).then(res => {
+ this.userInfo = uni.getStorageSync('userInfo') || {}
+ myNoticesH5({ page: 1, capacity: 1, model: { status: 0 } }).then(res => {
this.taskNum = res.data.total
})
},
@@ -96,116 +121,98 @@
url: item.url
})
},
- switchRole () {
- doSwitchRole(logoutPost)
- },
loginOut() {
logoutPost().catch(() => {}).finally(() => goRoleSelect())
},
-
}
}
</script>
<style lang="scss" scoped>
- .main_app {
- padding: 0 30rpx;
+ .ops-home__body {
+ margin-top: -28rpx;
+ padding: 0 30rpx 48rpx;
+ }
- .hone_name {
+ .home_con {
+ width: 100%;
+ height: 270rpx;
+ border-radius: 16rpx;
+ margin-bottom: 40rpx;
+ padding: 36rpx 40rpx;
+ position: relative;
+ color: #fff;
+ box-sizing: border-box;
+ overflow: hidden;
+ box-shadow: 0 8rpx 32rpx rgba(15, 35, 95, 0.08);
- height: 90rpx;
- display: flex;
- align-items: center;
- }
-
- .home_con {
- width: 690rpx;
- height: 270rpx;
- border-radius: 8rpx;
- margin-bottom: 40rpx;
- padding: 36rpx 40rpx;
- position: relative;
- color: #fff;
-
- .h1 {
- font-weight: bold;
- font-size: 44rpx;
- margin-bottom: 14rpx;
- }
- }
-
- .title {
- font-weight: 500;
- font-size: 34rpx;
- }
-
- .list {
- margin-top: 30rpx;
- margin-bottom: 80rpx;
- display: flex;
-
- .item {
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 25%;
- position: relative;
- image {
- width: 88rpx;
- height: 88rpx;
- margin-bottom: 20rpx;
- }
-
- .name {
- font-size: 26rpx;
- }
- .superscript{
- height: 40rpx;
- width: 40rpx;
- position: absolute;
- top: -16rpx;
- right: 24rpx;
- background-color: red;
- color: #fff;
- font-size: 24rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 50%;
- }
- }
- }
-
- .footer-actions {
- position: fixed;
- bottom: 88rpx;
+ .bg {
+ position: absolute;
left: 0;
- right: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 24rpx;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 0;
}
- .switch-role,
- .loginout {
- height: 60rpx;
- padding: 0 32rpx;
- border-radius: 30rpx;
- font-size: 26rpx;
- display: flex;
- justify-content: center;
- align-items: center;
+ .h1,
+ .h2 {
+ position: relative;
+ z-index: 1;
}
- .switch-role {
- border: 1rpx solid $primaryColor;
- color: $primaryColor;
- }
-
- .loginout {
- border: 1rpx solid #ccc;
- color: #666;
+ .h1 {
+ font-weight: bold;
+ font-size: 44rpx;
+ margin-bottom: 14rpx;
}
}
-</style>
\ No newline at end of file
+
+ .title {
+ font-weight: 500;
+ font-size: 34rpx;
+ }
+
+ .list {
+ margin-top: 30rpx;
+ margin-bottom: 40rpx;
+ display: flex;
+
+ &--last {
+ margin-bottom: 48rpx;
+ }
+
+ .item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 25%;
+ position: relative;
+
+ image {
+ width: 88rpx;
+ height: 88rpx;
+ margin-bottom: 20rpx;
+ }
+
+ .name {
+ font-size: 26rpx;
+ }
+
+ .superscript {
+ height: 40rpx;
+ width: 40rpx;
+ position: absolute;
+ top: -16rpx;
+ right: 24rpx;
+ background-color: red;
+ color: #fff;
+ font-size: 24rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ }
+ }
+ }
+</style>
diff --git a/h5/pages/login.vue b/h5/pages/login.vue
index a7ce852..899344a 100644
--- a/h5/pages/login.vue
+++ b/h5/pages/login.vue
@@ -1,313 +1,138 @@
<template>
- <view class="login">
- <view class="login_title">娆㈣繋鐧诲綍</view>
- <view class="login_title login_title2">闃滃畞鏂囦綋涓績</view>
- <view v-if="devMockTip" class="dev-tip">{{ devMockTip }}</view>
- <view class="login_list">
- <view class="login_list_item">
- <image src="@/static/login_ic_phone@2x.png" mode="widthFix" />
- <input v-model="form.phone" maxlength="18" placeholder="璇疯緭鍏ユ墜鏈哄彿" />
- </view>
- <!-- <view class="login_list_item">
- <image src="@/static/login_ic_password@2x.png" mode="widthFix" />
- <input v-model="form.password" type="password" placeholder="瀵嗙爜" />
- </view> -->
- <view class="login_list_item">
- <image src="@/static/login_ic_password@2x.png" mode="widthFix"></image>
- <input v-model="form.code" placeholder="璇疯緭鍏ラ獙璇佺爜" type="text" />
- <view v-if="downTime == 0" class="btn" @click="sendSms">鑾峰彇楠岃瘉鐮�</view>
- <view v-else class="btn gray">{{ downTime }}</view>
- </view>
- </view>
- <view class="login_btn">
- <view class="login_btn_n" @click="onLogin">鐧诲綍</view>
- </view>
- </view>
+ <view class="cu-login">
+ <view class="cu-auth-topbar">
+ <view class="cu-auth-topbar__btn" @click="goRoleSelect">
+ <u-icon name="reload" color="#2080f7" size="22" />
+ </view>
+ </view>
+
+ <view class="cu-login__brand">
+ <view class="cu-login__title">娆㈣繋鐧诲綍</view>
+ <view class="cu-login__sub">闃滃畞鏂囦綋涓績 路 杩愮淮鏈嶅姟骞冲彴</view>
+ </view>
+
+ <view v-if="devMockTip" class="cu-login__tip">{{ devMockTip }}</view>
+
+ <view class="cu-input-wrap">
+ <input v-model="form.phone" maxlength="18" placeholder="璇疯緭鍏ユ墜鏈哄彿" />
+ </view>
+ <view class="cu-input-wrap">
+ <input v-model="form.code" placeholder="璇疯緭鍏ラ獙璇佺爜" />
+ <view v-if="downTime == 0" class="cu-sms-btn" @click="sendSms">鑾峰彇楠岃瘉鐮�</view>
+ <view v-else class="cu-sms-btn cu-sms-btn--disabled">{{ downTime }}s</view>
+ </view>
+
+ <view class="cu-btn cu-btn--primary" @click="onLogin">鐧诲綍</view>
+ </view>
</template>
<script>
- import {
- loginPost,
- getUserInfo,
- sendSMsPost,
- ywWxAuthorize,
+import {
+ loginPost,
+ getUserInfo,
+ sendSMsPost,
+ ywWxAuthorize,
+ getRecordByUserPoint
+} from '@/api'
+import { devWechatMock } from '@/utils/config.js'
+import { runWechatOAuthFlow } from '@/utils/wechatAuth.js'
+import { requestLoginSmsCode } from '@/utils/loginSms.js'
+import { mapMutations } from 'vuex'
- getRecordByUserPoint
- } from '@/api'
- import { devWechatMock } from '@/utils/config.js'
- import { runWechatOAuthFlow } from '@/utils/wechatAuth.js'
- import { requestLoginSmsCode } from '@/utils/loginSms.js'
- import {
- mapState,
- mapMutations
- } from 'vuex'
- export default {
- name: 'login',
+export default {
+ name: 'login',
+ data () {
+ return {
+ form: { phone: '', code: '' },
+ ywinfo: {},
+ downTime: 0,
+ code: '',
+ devMockTip: devWechatMock.enabled ? `寮�鍙戞ā寮忥細妯℃嫙 openid ${devWechatMock.openId}` : ''
+ }
+ },
+ onLoad (option) {
+ uni.setStorageSync('userType', 0)
+ const ywinfo = uni.getStorageSync('ywinfo') || {}
+ if (ywinfo.ywid && (ywinfo.type || ywinfo.type == 0)) {
+ this.ywinfo = ywinfo
+ uni.setStorageSync('ywinfo', {})
+ }
+ if (option.ywid || option.ywid == 0) {
+ uni.setStorageSync('ywinfo', {
+ type: option.type,
+ ywid: option.ywid
+ })
+ }
+ },
+ onShow () {
+ const that = this
+ runWechatOAuthFlow({
+ authorizeApi: ywWxAuthorize,
+ fallbackCode: this.code,
+ onSuccess: (res) => {
+ if (res.data.openid) {
+ that.$store.commit('setOpenId', res.data.openid)
+ }
+ if (res.data.token && res.data.token != '') {
+ that.$store.commit('setToken', res.data.token)
+ getUserInfo().then(ress => {
+ that.$store.commit('setUserInfo', ress.data)
+ })
+ const ywinfo = this.ywinfo
+ if (ywinfo.ywid && (ywinfo.type || ywinfo.type == 0)) {
+ getRecordByUserPoint({ pointCode: ywinfo.ywid }).then(res => {
+ if (res.data && res.data.id) {
+ uni.redirectTo({ url: '/pages/polling/point?id=' + res.data.id })
+ } else {
+ uni.redirectTo({ url: '/pages/polling/empty?message=' + res.message })
+ }
+ })
+ } else {
+ setTimeout(() => {
+ uni.redirectTo({ url: '/pages/index' })
+ }, 300)
+ }
+ }
+ }
+ })
+ },
+ methods: {
+ ...mapMutations(['setToken', 'setUserInfo']),
+ goRoleSelect () {
+ uni.redirectTo({ url: '/pages/roleSelect?switch=1' })
+ },
+ onLogin () {
+ const { form } = this
+ if (!form.phone) return uni.showToast({ title: '鎵嬫満鍙蜂笉鑳戒负绌�', icon: 'none' })
+ if (!form.code) return uni.showToast({ title: '楠岃瘉鐮佷笉鑳戒负绌�', icon: 'none' })
- data() {
- return {
- form: {
- phone: '',
- code: ''
- },
- ywinfo: {},
- downTime: 0,
- code: '',
- devMockTip: devWechatMock.enabled ? `寮�鍙戞ā寮忥細妯℃嫙 openid ${devWechatMock.openId}` : ''
- }
- },
- onLoad(option) {
- console.log('onLoad');
- // https://zhcg.fnwtzx.com/#/pages/login?type=0&ywid=ywid
- const ywinfo = uni.getStorageSync('ywinfo') || {}
- if (ywinfo.ywid && (ywinfo.type || ywinfo.type == 0)) {
- this.ywinfo = ywinfo
- uni.setStorageSync('ywinfo', {})
- }
- if (option.ywid || option.ywid == 0) {
- uni.setStorageSync('ywinfo', {
- type: option.type,
- ywid: option.ywid
- })
- }
- },
- onShow() {
- const that = this
- runWechatOAuthFlow({
- authorizeApi: ywWxAuthorize,
- fallbackCode: this.code,
- onSuccess: (res) => {
- if (res.data.openid) {
- that.$store.commit('setOpenId', res.data.openid)
- }
- if (res.data.token && res.data.token != '') {
- that.$store.commit('setToken', res.data.token)
- getUserInfo().then(ress => {
- that.$store.commit('setUserInfo', ress.data)
- })
- const ywinfo = this.ywinfo
- if (ywinfo.ywid && (ywinfo.type || ywinfo.type == 0)) {
- getRecordByUserPoint({
- pointCode: ywinfo.ywid
- }).then(res => {
- if (res.data && res.data.id) {
- uni.redirectTo({
- url: "/pages/polling/point?id=" + res.data.id
- })
- } else {
- uni.redirectTo({
- url: "/pages/polling/empty?message=" + res.message
- })
- }
- })
- } else {
- setTimeout(() => {
- uni.redirectTo({
- url: "/pages/index"
- })
- }, 300)
- }
- }
- }
- })
- },
- methods: {
- ...mapMutations(["setToken", "setUserInfo"]),
- onLogin() {
- const {
- form,
- ProtocolFlag
- } = this
- if (!form.phone) return uni.showToast({
- title: '鎵嬫満鍙蜂笉鑳戒负绌�',
- icon: 'none'
- })
- if (!form.code) return uni.showToast({
- title: '楠岃瘉鐮佷笉鑳戒负绌�',
- icon: 'none'
- })
-
- loginPost({
- ...form,
- openid: this.$store.state.openId
- }).then(res => {
- if (res.code === 200) {
- this.setToken(res.data)
- this.showToast('鐧诲綍鎴愬姛')
- getUserInfo().then(ress => {
- this.setUserInfo(ress.data)
- const ywinfo = this.ywinfo
- if (ywinfo.ywid && (ywinfo.type || ywinfo.type == 0)) {
- // getRecordByUserPoint({pointCode: ywinfo.ywid}).then(res => {
- getRecordByUserPoint({
- pointCode: ywinfo.ywid
- }).then(res => {
- if (res.data && res.data.id) {
- uni.redirectTo({
- url: "/pages/polling/point?id=" + res.data.id
- })
- } else {
- uni.redirectTo({
- url: "/pages/polling/empty?message=" + res.message
- })
- }
- })
- // })
- } else {
- uni.redirectTo({
- url: "/pages/index"
- })
- }
- })
- }
- })
-
-
-
- },
- sendSms() {
- requestLoginSmsCode(this, this.form.phone, sendSMsPost, { phone: this.form.phone, userType: 0 })
- },
- }
- }
+ loginPost({
+ ...form,
+ openid: this.$store.state.openId
+ }).then(res => {
+ if (res.code === 200) {
+ this.setToken(res.data)
+ getUserInfo().then(ress => {
+ this.setUserInfo(ress.data)
+ const ywinfo = this.ywinfo
+ if (ywinfo.ywid && (ywinfo.type || ywinfo.type == 0)) {
+ getRecordByUserPoint({ pointCode: ywinfo.ywid }).then(res => {
+ if (res.data && res.data.id) {
+ uni.redirectTo({ url: '/pages/polling/point?id=' + res.data.id })
+ } else {
+ uni.redirectTo({ url: '/pages/polling/empty?message=' + res.message })
+ }
+ })
+ } else {
+ uni.redirectTo({ url: '/pages/index' })
+ }
+ })
+ }
+ })
+ },
+ sendSms () {
+ requestLoginSmsCode(this, this.form.phone, sendSMsPost, { phone: this.form.phone, userType: 0 })
+ }
+ }
+}
</script>
-
-<style lang="scss" scoped>
- .login {
- width: 100%;
- height: 100vh;
- display: flex;
- padding-top: 130rpx;
- box-sizing: border-box;
- align-items: center;
- flex-direction: column;
- background: linear-gradient(180deg, #C5DDFF 0%, #FFFFFF 100%);
-
- .login_title {
- font-weight: 500;
- font-size: 52rpx;
- color: #222222;
- margin-top: 180rpx;
- width: 100%;
- padding-left: 60rpx;
- }
-
- .login_title2 {
- margin-top: 10rpx;
- margin-bottom: 40rpx;
- }
-
- .dev-tip {
- width: 100%;
- padding: 0 60rpx;
- box-sizing: border-box;
- font-size: 24rpx;
- color: #e6a23c;
- margin-bottom: 40rpx;
- line-height: 1.5;
- }
-
- .login_list {
- width: 100%;
- padding: 0 60rpx;
- box-sizing: border-box;
-
- .login_list_item {
- width: 100%;
- border-radius: 50rpx;
- height: 98rpx;
- padding: 0 40rpx;
- box-sizing: border-box;
- background: #ffffff;
- margin-bottom: 40rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- &:last-child {
- margin-bottom: 0 !important;
- }
-
- image {
- flex-shrink: 0;
- width: 40rpx;
- height: 40rpx;
- }
-
- .btn {
- width: 145rpx;
- color: $primaryColor;
- text-align: center;
- }
-
- .gray {
- color: #999999;
- }
-
- input {
- flex: 1;
- height: 100%;
- color: #666666;
- margin-left: 24rpx;
- border: none;
- }
- }
- }
-
- .login_btn {
- width: 100%;
- padding: 0 60rpx;
- box-sizing: border-box;
- margin-top: 60rpx;
-
- .for_psd {
- color: $uni-color-primary;
- width: 140rpx;
- text-align: center;
- margin: 40rpx auto;
- }
-
- .login_btn_n {
- width: 100%;
- height: 98rpx;
- background: $uni-color-primary;
- box-shadow: 0rpx 12rpx 24rpx 0rpx rgba(39, 155, 170, 0.2);
- display: flex;
- align-items: center;
- justify-content: center;
- color: #ffffff;
- border-radius: 50rpx;
- font-weight: 500;
- font-size: 32rpx;
- }
- }
-
- .deal_wrap {
- position: absolute;
- width: 100%;
- left: 0;
- text-align: center;
- bottom: 88rpx;
- display: flex;
- justify-content: center;
- align-items: center;
-
- .deal {
- color: $uni-color-primary;
- }
-
- .checked {
- width: 48rpx;
- margin-right: 12rpx;
- }
- }
- }
-
- .modal {
- width: 690rpx;
- min-height: 920rpx;
- max-height: 720px;
- border-radius: 24rpx;
- padding: 32rpx;
- }
-</style>
\ No newline at end of file
diff --git a/h5/pages/roleSelect.vue b/h5/pages/roleSelect.vue
index 68df7e0..503c40a 100644
--- a/h5/pages/roleSelect.vue
+++ b/h5/pages/roleSelect.vue
@@ -1,14 +1,28 @@
<template>
- <view class="page">
- <view class="title">璇烽�夋嫨鐧诲綍韬唤</view>
- <view class="sub-title">鍒囨崲瑙掕壊鍚庨渶浣跨敤瀵瑰簲韬唤閲嶆柊鐧诲綍</view>
- <view class="card" @click="goOps">
- <view class="name">杩愮淮浜哄憳</view>
- <view class="desc">宸ュ崟銆佸贰妫�銆佽澶囪繍缁�</view>
+ <view class="cu-auth-page">
+ <view class="cu-auth-page__title">閫夋嫨韬唤</view>
+ <view class="cu-auth-page__sub">鍒囨崲瑙掕壊鍚庨渶浣跨敤瀵瑰簲韬唤閲嶆柊鐧诲綍</view>
+
+ <view class="cu-role-card cu-role-card--ops" @click="goOps">
+ <view class="cu-role-card__icon">
+ <u-icon name="setting-fill" color="#40a9ff" size="28" />
+ </view>
+ <view class="cu-role-card__main">
+ <view class="cu-role-card__name">杩愮淮浜哄憳</view>
+ <view class="cu-role-card__desc">宸ュ崟銆佸贰妫�銆佽澶囪繍缁�</view>
+ </view>
+ <text class="cu-role-card__arrow">鈥�</text>
</view>
- <view class="card merchant" @click="goMerchant">
- <view class="name">鍟嗘埛</view>
- <view class="desc">浜ょ數璐广�佹煡鍚堝悓銆佹煡璐﹀崟</view>
+
+ <view class="cu-role-card cu-role-card--merchant" @click="goMerchant">
+ <view class="cu-role-card__icon">
+ <u-icon name="home-fill" color="#fa8c16" size="28" />
+ </view>
+ <view class="cu-role-card__main">
+ <view class="cu-role-card__name">鍟嗘埛</view>
+ <view class="cu-role-card__desc">浜ょ數璐广�佹煡鍚堝悓銆佹煡璐﹀崟</view>
+ </view>
+ <text class="cu-role-card__arrow">鈥�</text>
</view>
</view>
</template>
@@ -37,13 +51,3 @@
}
}
</script>
-
-<style lang="scss" scoped>
-.page { min-height: 100vh; padding: 120rpx 48rpx; background: linear-gradient(180deg, #e8f0ff 0%, #fff 100%); }
-.title { font-size: 44rpx; font-weight: 600; margin-bottom: 16rpx; color: #222; }
-.sub-title { font-size: 26rpx; color: #999; margin-bottom: 48rpx; }
-.card { background: #fff; border-radius: 24rpx; padding: 40rpx; margin-bottom: 32rpx; box-shadow: 0 8rpx 24rpx rgba(0,0,0,.06); }
-.card.merchant { border: 2rpx solid #3c7cff; }
-.name { font-size: 36rpx; font-weight: 600; color: #222; }
-.desc { margin-top: 12rpx; font-size: 26rpx; color: #888; }
-</style>
diff --git a/h5/styles/customer.scss b/h5/styles/customer.scss
index f6723d5..8b7223e 100644
--- a/h5/styles/customer.scss
+++ b/h5/styles/customer.scss
@@ -41,6 +41,87 @@
align-items: center;
}
+.cu-profile-bar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16rpx;
+}
+
+.cu-profile-bar__info {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ min-width: 0;
+}
+
+.cu-profile-actions {
+ display: flex;
+ align-items: center;
+ gap: 16rpx;
+ flex-shrink: 0;
+}
+
+.cu-profile-action {
+ width: 72rpx;
+ height: 72rpx;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.2);
+ border: 2rpx solid rgba(255, 255, 255, 0.35);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: opacity 0.2s ease;
+}
+
+.cu-profile-action--pill {
+ width: auto;
+ height: 56rpx;
+ padding: 0 20rpx;
+ border-radius: 999rpx;
+ gap: 6rpx;
+ flex-direction: row;
+ background: rgba(255, 255, 255, 0.18);
+ border: 2rpx solid rgba(255, 255, 255, 0.4);
+}
+
+.cu-profile-action__text {
+ font-size: 24rpx;
+ color: #fff;
+ line-height: 1;
+ font-weight: 500;
+}
+
+.cu-profile-action:active {
+ opacity: 0.75;
+}
+
+/* 鍟嗘埛瀛愰〉鍥為椤� */
+.cu-workbench-fab {
+ position: fixed;
+ top: 50%;
+ right: 24rpx;
+ z-index: 200;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 84rpx;
+ height: 84rpx;
+ padding: 0;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.96);
+ border: 1rpx solid rgba(15, 35, 95, 0.08);
+ box-shadow: 0 6rpx 24rpx rgba(15, 35, 95, 0.1);
+ transform: translateY(-50%);
+ transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
+}
+
+.cu-workbench-fab:active {
+ transform: translateY(-50%) scale(0.96);
+ opacity: 0.94;
+ box-shadow: 0 4rpx 16rpx rgba(15, 35, 95, 0.08);
+}
+
.cu-avatar {
width: 88rpx;
height: 88rpx;
@@ -89,12 +170,147 @@
padding-left: 8rpx;
}
+.cu-service-panel {
+ background: $cu-card-bg;
+ border-radius: $cu-radius;
+ padding: 28rpx 20rpx 20rpx;
+ box-shadow: $cu-shadow;
+ border: 1rpx solid rgba(15, 35, 95, 0.04);
+}
+
+.cu-section-head {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+ margin-bottom: 24rpx;
+ padding: 0 8rpx;
+}
+
+.cu-section-head__bar {
+ width: 6rpx;
+ height: 28rpx;
+ border-radius: 4rpx;
+ background: linear-gradient(180deg, #2080f7 0%, #4a9bff 100%);
+ flex-shrink: 0;
+}
+
+.cu-section-head__title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: $cu-text;
+ line-height: 1.2;
+}
+
.cu-service-grid {
display: flex;
flex-wrap: wrap;
- gap: 20rpx;
+ gap: 16rpx;
}
+.cu-service-card {
+ width: calc(50% - 8rpx);
+ background: linear-gradient(180deg, #fafbfd 0%, #fff 100%);
+ border-radius: 20rpx;
+ padding: 22rpx 20rpx 24rpx;
+ border: 1rpx solid rgba(15, 35, 95, 0.06);
+ box-sizing: border-box;
+ position: relative;
+ overflow: hidden;
+ transition: transform 0.2s ease, opacity 0.2s ease;
+}
+
+.cu-service-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 4rpx;
+}
+
+.cu-service-card:active {
+ transform: scale(0.98);
+ opacity: 0.92;
+}
+
+.cu-service-card__top {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 14rpx;
+}
+
+.cu-service-card__icon {
+ width: 76rpx;
+ height: 76rpx;
+ border-radius: 22rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.cu-service-card__arrow {
+ font-size: 36rpx;
+ color: rgba(154, 163, 178, 0.7);
+ line-height: 1;
+ font-weight: 300;
+ margin-top: 4rpx;
+}
+
+.cu-service-card__label {
+ display: block;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $cu-text;
+ line-height: 1.3;
+}
+
+.cu-service-card__desc {
+ display: block;
+ font-size: 22rpx;
+ color: $cu-text-muted;
+ margin-top: 8rpx;
+ line-height: 1.4;
+}
+
+.cu-service-card--electric::before {
+ background: linear-gradient(90deg, #fa8c16 0%, #ffc069 100%);
+}
+
+.cu-service-card--electric .cu-service-card__icon {
+ background: linear-gradient(135deg, #fff7e6 0%, #ffe7ba 100%);
+ box-shadow: 0 6rpx 16rpx rgba(250, 140, 22, 0.15);
+}
+
+.cu-service-card--contract::before {
+ background: linear-gradient(90deg, #40a9ff 0%, #69c0ff 100%);
+}
+
+.cu-service-card--contract .cu-service-card__icon {
+ background: linear-gradient(135deg, #e6f4ff 0%, #bae7ff 100%);
+ box-shadow: 0 6rpx 16rpx rgba(64, 169, 255, 0.18);
+}
+
+.cu-service-card--bill::before {
+ background: linear-gradient(90deg, #597ef7 0%, #85a5ff 100%);
+}
+
+.cu-service-card--bill .cu-service-card__icon {
+ background: linear-gradient(135deg, #eef2ff 0%, #d6e4ff 100%);
+ box-shadow: 0 6rpx 16rpx rgba(89, 126, 247, 0.15);
+}
+
+.cu-service-card--record::before {
+ background: linear-gradient(90deg, #9254de 0%, #b37feb 100%);
+}
+
+.cu-service-card--record .cu-service-card__icon {
+ background: linear-gradient(135deg, #f9f0ff 0%, #efdbff 100%);
+ box-shadow: 0 6rpx 16rpx rgba(146, 84, 222, 0.15);
+}
+
+/* 鍏煎鏃х被鍚� */
.cu-service-item {
width: calc(50% - 10rpx);
background: $cu-card-bg;
@@ -137,25 +353,112 @@
.cu-service-item--bill .cu-service-item__icon { background: #eef2ff; }
.cu-service-item--record .cu-service-item__icon { background: #fce8f3; }
-.cu-footer-bar {
- margin-top: 64rpx;
- display: flex;
- justify-content: center;
- gap: 24rpx;
+/* 璁よ瘉 / 瑙掕壊閫夋嫨 */
+.cu-auth-page {
+ min-height: 100vh;
+ padding: 120rpx 48rpx 48rpx;
+ background: linear-gradient(180deg, #dce9ff 0%, #f4f6fb 45%, #fff 100%);
+ box-sizing: border-box;
}
-.cu-footer-btn {
- font-size: 28rpx;
- padding: 18rpx 48rpx;
- border-radius: 999rpx;
- background: #fff;
- color: $cu-text-secondary;
+.cu-auth-page__title {
+ font-size: 44rpx;
+ font-weight: 700;
+ color: $cu-text;
+ margin-bottom: 12rpx;
+}
+
+.cu-auth-page__sub {
+ font-size: 26rpx;
+ color: $cu-text-muted;
+ margin-bottom: 48rpx;
+ line-height: 1.5;
+}
+
+.cu-role-card {
+ display: flex;
+ align-items: center;
+ background: $cu-card-bg;
+ border-radius: $cu-radius;
+ padding: 32rpx 28rpx;
+ margin-bottom: 24rpx;
box-shadow: $cu-shadow;
}
-.cu-footer-btn--primary {
- color: $cu-primary;
- border: 1rpx solid rgba(32, 128, 247, 0.35);
+.cu-role-card:active {
+ opacity: 0.92;
+}
+
+.cu-role-card__icon {
+ width: 88rpx;
+ height: 88rpx;
+ border-radius: 22rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 24rpx;
+ flex-shrink: 0;
+}
+
+.cu-role-card--ops .cu-role-card__icon {
+ background: linear-gradient(135deg, #e6f4ff 0%, #bae7ff 100%);
+ box-shadow: 0 6rpx 16rpx rgba(105, 192, 255, 0.28);
+}
+
+.cu-role-card--merchant .cu-role-card__icon {
+ background: linear-gradient(135deg, #fff7e6 0%, #ffe7ba 100%);
+ box-shadow: 0 6rpx 16rpx rgba(250, 173, 20, 0.22);
+}
+
+.cu-role-card__main {
+ flex: 1;
+ min-width: 0;
+}
+
+.cu-role-card__name {
+ font-size: 34rpx;
+ font-weight: 600;
+ color: $cu-text;
+ margin-bottom: 8rpx;
+}
+
+.cu-role-card__desc {
+ font-size: 24rpx;
+ color: $cu-text-muted;
+ line-height: 1.4;
+}
+
+.cu-role-card__arrow {
+ font-size: 32rpx;
+ color: $cu-text-muted;
+ flex-shrink: 0;
+ margin-left: 12rpx;
+}
+
+.cu-auth-topbar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 10;
+ display: flex;
+ justify-content: flex-end;
+ padding: 24rpx 32rpx;
+}
+
+.cu-auth-topbar__btn {
+ width: 72rpx;
+ height: 72rpx;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.9);
+ box-shadow: $cu-shadow;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.cu-auth-topbar__btn:active {
+ opacity: 0.8;
}
/* 鍗$墖 */
@@ -229,10 +532,13 @@
/* 鐘舵�� */
.cu-status {
+ flex-shrink: 0;
+ white-space: nowrap;
font-size: 24rpx;
padding: 4rpx 16rpx;
border-radius: 999rpx;
font-weight: 500;
+ line-height: 1.4;
}
.cu-status--ok {
@@ -402,7 +708,9 @@
.cu-login {
min-height: 100vh;
padding: 120rpx 48rpx 48rpx;
+ padding-top: 160rpx;
background: linear-gradient(180deg, #dce9ff 0%, #f4f6fb 45%, #fff 100%);
+ box-sizing: border-box;
}
.cu-login__brand {
@@ -509,6 +817,200 @@
padding: 8rpx 24rpx 16rpx;
}
+.cu-list-header--inline {
+ padding: 0 8rpx 12rpx;
+}
+
+.cu-device-page .cu-list-wrap {
+ padding-top: 0;
+}
+
+.cu-device-page__header {
+ padding-bottom: 8rpx;
+}
+
+.cu-device-type-tabs {
+ display: flex;
+ gap: 20rpx;
+ margin: 24rpx 24rpx 20rpx;
+}
+
+.cu-device-type-card {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 16rpx;
+ padding: 22rpx 20rpx;
+ background: $cu-card-bg;
+ border-radius: $cu-radius;
+ box-shadow: $cu-shadow;
+ border: 2rpx solid transparent;
+ transition: all 0.22s ease;
+ box-sizing: border-box;
+}
+
+.cu-device-type-card:active {
+ transform: scale(0.97);
+}
+
+.cu-device-type-card__icon-wrap {
+ width: 72rpx;
+ height: 72rpx;
+ border-radius: 20rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ transition: background 0.22s ease;
+}
+
+.cu-device-type-card__icon {
+ font-size: 34rpx;
+ line-height: 1;
+}
+
+.cu-device-type-card__text {
+ flex: 1;
+ min-width: 0;
+}
+
+.cu-device-type-card__label {
+ display: block;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $cu-text;
+ line-height: 1.3;
+ transition: color 0.22s ease;
+}
+
+.cu-device-type-card__hint {
+ display: block;
+ font-size: 22rpx;
+ color: $cu-text-muted;
+ margin-top: 4rpx;
+ line-height: 1.3;
+ transition: color 0.22s ease;
+}
+
+.cu-device-type-card--electric .cu-device-type-card__icon-wrap {
+ background: linear-gradient(135deg, #fff7e6, #ffe8b3);
+}
+
+.cu-device-type-card--conditioner .cu-device-type-card__icon-wrap {
+ background: linear-gradient(135deg, #e8f7ef, #c8f0dc);
+}
+
+.cu-device-type-card--electric:not(.cu-device-type-card--active) {
+ border-color: rgba(250, 140, 22, 0.18);
+}
+
+.cu-device-type-card--conditioner:not(.cu-device-type-card--active) {
+ border-color: rgba(25, 190, 107, 0.18);
+}
+
+.cu-device-type-card--electric.cu-device-type-card--active {
+ background: linear-gradient(135deg, #ffb347 0%, #fa8c16 100%);
+ border-color: transparent;
+ box-shadow: 0 10rpx 28rpx rgba(250, 140, 22, 0.32);
+}
+
+.cu-device-type-card--electric.cu-device-type-card--active .cu-device-type-card__icon-wrap {
+ background: rgba(255, 255, 255, 0.28);
+}
+
+.cu-device-type-card--electric.cu-device-type-card--active .cu-device-type-card__label,
+.cu-device-type-card--electric.cu-device-type-card--active .cu-device-type-card__hint {
+ color: #fff;
+}
+
+.cu-device-type-card--electric.cu-device-type-card--active .cu-device-type-card__hint {
+ opacity: 0.88;
+}
+
+.cu-device-type-card--conditioner.cu-device-type-card--active {
+ background: linear-gradient(135deg, #5cdbd3 0%, #13c2c2 100%);
+ border-color: transparent;
+ box-shadow: 0 10rpx 28rpx rgba(19, 194, 194, 0.32);
+}
+
+.cu-device-type-card--conditioner.cu-device-type-card--active .cu-device-type-card__icon-wrap {
+ background: rgba(255, 255, 255, 0.28);
+}
+
+.cu-device-type-card--conditioner.cu-device-type-card--active .cu-device-type-card__label,
+.cu-device-type-card--conditioner.cu-device-type-card--active .cu-device-type-card__hint {
+ color: #fff;
+}
+
+.cu-device-type-card--conditioner.cu-device-type-card--active .cu-device-type-card__hint {
+ opacity: 0.88;
+}
+
+.cu-ac-balance-card {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 20rpx;
+ margin: 0 24rpx 16rpx;
+ padding: 28rpx 24rpx;
+ background: linear-gradient(135deg, #f0f7ff 0%, #fff 100%);
+ border-radius: $cu-radius;
+ border: 1rpx solid rgba(32, 128, 247, 0.12);
+ box-shadow: $cu-shadow;
+}
+
+.cu-ac-balance-card__main {
+ flex: 1;
+ min-width: 0;
+}
+
+.cu-ac-balance-card__label {
+ display: block;
+ font-size: 24rpx;
+ color: $cu-text-muted;
+ margin-bottom: 8rpx;
+}
+
+.cu-ac-balance-card__value {
+ display: block;
+ font-size: 44rpx;
+ font-weight: 700;
+ color: $cu-primary;
+ line-height: 1.2;
+}
+
+.cu-ac-balance-card__value--danger {
+ color: $cu-danger;
+}
+
+.cu-ac-balance-card__value--success {
+ color: $cu-success;
+}
+
+.cu-ac-balance-card__btn {
+ flex-shrink: 0;
+ padding: 16rpx 32rpx;
+ background: linear-gradient(135deg, #2080f7 0%, #4a9bff 100%);
+ border-radius: 999rpx;
+ font-size: 26rpx;
+ font-weight: 600;
+ color: #fff;
+ box-shadow: 0 6rpx 18rpx rgba(32, 128, 247, 0.28);
+}
+
+.cu-ac-balance-card__btn:active {
+ opacity: 0.9;
+}
+
+.cu-list-card--readonly .cu-list-card__meta-row {
+ padding: 16rpx 24rpx 24rpx;
+ border-top: 1rpx solid #f0f2f6;
+}
+
+.cu-list-card__meta-row {
+ padding: 0 24rpx 24rpx;
+}
+
.cu-list-header__count {
font-size: 24rpx;
color: $cu-text-muted;
@@ -570,13 +1072,21 @@
justify-content: space-between;
gap: 12rpx;
margin-bottom: 8rpx;
+ min-width: 0;
+}
+
+.cu-list-card__title-wrap {
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
}
.cu-list-card__title {
+ display: block;
font-size: 30rpx;
font-weight: 600;
color: $cu-text;
- flex: 1;
+ width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -645,6 +1155,10 @@
.cu-info-cell__value--danger {
color: $cu-danger;
+}
+
+.cu-info-cell__value--success {
+ color: $cu-success;
}
.cu-info-cell__value--primary {
@@ -1067,6 +1581,163 @@
}
/* 鍏呭��/缂磋垂椤� */
+.cu-recharge-hero {
+ margin: 24rpx 24rpx 0;
+ border-radius: $cu-radius;
+ overflow: hidden;
+ box-shadow: $cu-shadow;
+}
+
+.cu-recharge-hero__gradient {
+ padding: 40rpx 32rpx 56rpx;
+ background: linear-gradient(145deg, #2080f7 0%, #4a9bff 52%, #6eb3ff 100%);
+}
+
+.cu-recharge-hero--electric .cu-recharge-hero__gradient {
+ background: linear-gradient(145deg, #1a6fe0 0%, #2080f7 45%, #ffb84d 165%);
+}
+
+.cu-recharge-hero__balance-block {
+ text-align: center;
+}
+
+.cu-recharge-hero__balance-label {
+ display: block;
+ font-size: 24rpx;
+ color: rgba(255, 255, 255, 0.88);
+ margin-bottom: 12rpx;
+ letter-spacing: 1rpx;
+}
+
+.cu-recharge-hero__balance-value {
+ display: block;
+ font-size: 64rpx;
+ font-weight: 700;
+ color: #fff;
+ line-height: 1.15;
+ font-variant-numeric: tabular-nums;
+}
+
+.cu-recharge-hero__balance-value--danger {
+ color: #ffb4b0;
+ text-shadow: 0 2rpx 8rpx rgba(250, 53, 52, 0.35);
+}
+
+.cu-recharge-hero__balance-value--success {
+ color: #b8f5d0;
+ text-shadow: 0 2rpx 8rpx rgba(25, 190, 107, 0.35);
+}
+
+.cu-recharge-hero__panel {
+ margin-top: -28rpx;
+ padding: 28rpx 24rpx 24rpx;
+ background: $cu-card-bg;
+ border-radius: $cu-radius $cu-radius 0 0;
+}
+
+.cu-recharge-hero__device {
+ display: flex;
+ align-items: flex-start;
+ gap: 20rpx;
+ padding-bottom: 20rpx;
+ border-bottom: 1rpx solid #f0f2f6;
+}
+
+.cu-recharge-hero__icon {
+ width: 80rpx;
+ height: 80rpx;
+ border-radius: 20rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 38rpx;
+ flex-shrink: 0;
+}
+
+.cu-recharge-hero__icon--electric {
+ background: linear-gradient(135deg, #fff7e6, #ffe8b3);
+}
+
+.cu-recharge-hero__device-main {
+ flex: 1;
+ min-width: 0;
+}
+
+.cu-recharge-hero__title-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12rpx;
+ margin-bottom: 8rpx;
+ min-width: 0;
+}
+
+.cu-recharge-hero__title-wrap {
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+}
+
+.cu-recharge-hero__title {
+ display: block;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $cu-text;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ width: 100%;
+}
+
+.cu-recharge-hero__status {
+ flex-shrink: 0;
+ font-size: 22rpx;
+ padding: 4rpx 14rpx;
+ border-radius: 999rpx;
+}
+
+.cu-recharge-hero__status--ok {
+ color: $cu-success;
+ background: rgba(25, 190, 107, 0.12);
+}
+
+.cu-recharge-hero__status--bad {
+ color: $cu-danger;
+ background: rgba(250, 53, 52, 0.1);
+}
+
+.cu-recharge-hero__sub {
+ display: block;
+ font-size: 24rpx;
+ color: $cu-text-secondary;
+ line-height: 1.5;
+}
+
+.cu-recharge-hero__meta {
+ padding-top: 20rpx;
+}
+
+.cu-recharge-hero__meta-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16rpx;
+}
+
+.cu-recharge-hero__meta-label {
+ font-size: 24rpx;
+ color: $cu-text-muted;
+ flex-shrink: 0;
+}
+
+.cu-recharge-hero__meta-value {
+ font-size: 26rpx;
+ color: $cu-text;
+ font-weight: 500;
+ text-align: right;
+ word-break: break-all;
+}
+
.cu-device-summary {
margin: 24rpx 24rpx 0;
padding: 28rpx;
@@ -1075,12 +1746,19 @@
box-shadow: $cu-shadow;
}
+.cu-device-summary__title {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: $cu-text;
+ margin-bottom: 4rpx;
+}
+
.cu-device-summary__balance {
margin-top: 20rpx;
padding: 24rpx;
- background: linear-gradient(135deg, #fff5f5, #fff);
+ background: linear-gradient(135deg, #f0f7ff 0%, #fff 100%);
border-radius: 16rpx;
- border: 1rpx solid rgba(250, 53, 52, 0.12);
+ border: 1rpx solid rgba(32, 128, 247, 0.12);
text-align: center;
}
@@ -1093,7 +1771,15 @@
.cu-device-summary__balance-value {
font-size: 48rpx;
font-weight: 700;
+ color: $cu-primary;
+}
+
+.cu-device-summary__balance-value--danger {
color: $cu-danger;
+}
+
+.cu-device-summary__balance-value--success {
+ color: $cu-success;
}
.cu-pay-amount-box {
@@ -1277,6 +1963,399 @@
transform: translateY(2rpx);
}
+.cu-bill-cost-picker--status-ok {
+ border-color: #52c41a;
+ background: linear-gradient(135deg, #f0faf4 0%, #fff 100%);
+}
+
+.cu-bill-cost-picker--status-ok .cu-bill-cost-picker__label {
+ color: $cu-text;
+ font-weight: 600;
+}
+
+.cu-bill-cost-picker--status-bad {
+ border-color: $cu-danger;
+ background: linear-gradient(135deg, #fff1f0 0%, #fff 100%);
+}
+
+.cu-bill-cost-picker--status-bad .cu-bill-cost-picker__label {
+ color: $cu-danger;
+ font-weight: 600;
+}
+
+.cu-bill-cost-picker--status-pending {
+ border-color: $cu-warning;
+ background: linear-gradient(135deg, #fff7e6 0%, #fff 100%);
+}
+
+.cu-bill-cost-picker--status-pending .cu-bill-cost-picker__label {
+ color: $cu-warning;
+ font-weight: 600;
+}
+
+.cu-recharge-record-page__header {
+ padding: 24rpx 24rpx 8rpx;
+}
+
+.cu-recharge-filter-panel {
+ background: $cu-card-bg;
+ border-radius: $cu-radius;
+ box-shadow: $cu-shadow;
+ padding: 24rpx;
+ margin-bottom: 16rpx;
+}
+
+.cu-recharge-filter-panel__section + .cu-recharge-filter-panel__section {
+ margin-top: 0;
+}
+
+.cu-recharge-filter-panel__label {
+ display: block;
+ font-size: 24rpx;
+ color: $cu-text-muted;
+ margin-bottom: 16rpx;
+ line-height: 1.2;
+}
+
+.cu-recharge-filter-panel__row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16rpx;
+ margin-bottom: 16rpx;
+}
+
+.cu-recharge-filter-panel__row .cu-recharge-filter-panel__label {
+ margin-bottom: 0;
+ flex-shrink: 0;
+}
+
+.cu-recharge-filter-panel__divider {
+ height: 1rpx;
+ background: #f0f2f6;
+ margin: 24rpx 0;
+}
+
+.cu-recharge-filter-panel__dates {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 12rpx;
+}
+
+.cu-recharge-status-tabs {
+ white-space: nowrap;
+ width: 100%;
+}
+
+.cu-recharge-status-tab {
+ display: inline-block;
+ padding: 14rpx 28rpx;
+ margin-right: 12rpx;
+ font-size: 26rpx;
+ color: $cu-text-secondary;
+ background: #f4f6fb;
+ border-radius: 999rpx;
+ line-height: 1.2;
+ border: 2rpx solid transparent;
+ transition: all 0.2s ease;
+}
+
+.cu-recharge-status-tab:last-child {
+ margin-right: 0;
+}
+
+.cu-recharge-status-tab--active {
+ color: $cu-primary;
+ background: linear-gradient(135deg, #e8f2ff 0%, #f0f7ff 100%);
+ border-color: rgba(32, 128, 247, 0.25);
+ font-weight: 600;
+}
+
+.cu-recharge-status-tab--active.cu-recharge-status-tab--ok {
+ color: $cu-success;
+ background: linear-gradient(135deg, #f0faf4 0%, #fff 100%);
+ border-color: rgba(25, 190, 107, 0.25);
+}
+
+.cu-recharge-status-tab--active.cu-recharge-status-tab--bad {
+ color: $cu-danger;
+ background: linear-gradient(135deg, #fff1f0 0%, #fff 100%);
+ border-color: rgba(250, 53, 52, 0.25);
+}
+
+.cu-recharge-status-tab--active.cu-recharge-status-tab--pending {
+ color: $cu-warning;
+ background: linear-gradient(135deg, #fff7e6 0%, #fff 100%);
+ border-color: rgba(255, 153, 0, 0.25);
+}
+
+.cu-recharge-date-mode {
+ display: flex;
+ padding: 4rpx;
+ background: #eef1f6;
+ border-radius: 999rpx;
+ flex-shrink: 0;
+}
+
+.cu-recharge-date-mode__item {
+ padding: 10rpx 22rpx;
+ font-size: 22rpx;
+ color: $cu-text-secondary;
+ border-radius: 999rpx;
+ line-height: 1.2;
+}
+
+.cu-recharge-date-mode__item--active {
+ background: #fff;
+ color: $cu-primary;
+ font-weight: 600;
+ box-shadow: 0 4rpx 12rpx rgba(15, 35, 95, 0.08);
+}
+
+.cu-recharge-date-range {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+ flex: 1;
+ min-width: 0;
+}
+
+.cu-recharge-date-field {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8rpx;
+ padding: 16rpx 20rpx;
+ background: #f8fafc;
+ border-radius: 16rpx;
+ border: 2rpx solid #eef1f6;
+}
+
+.cu-recharge-date-field--active {
+ background: #f0f7ff;
+ border-color: rgba(32, 128, 247, 0.2);
+}
+
+.cu-recharge-date-field__text {
+ flex: 1;
+ min-width: 0;
+ font-size: 26rpx;
+ color: $cu-text-secondary;
+ line-height: 1.3;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.cu-recharge-date-field--active .cu-recharge-date-field__text {
+ color: $cu-primary;
+ font-weight: 600;
+}
+
+.cu-recharge-date-field__arrow {
+ flex-shrink: 0;
+ font-size: 22rpx;
+ color: $cu-text-muted;
+ line-height: 1;
+}
+
+.cu-recharge-date-sep {
+ flex-shrink: 0;
+ font-size: 24rpx;
+ color: $cu-text-muted;
+}
+
+.cu-recharge-date-clear {
+ flex-shrink: 0;
+ font-size: 24rpx;
+ color: $cu-primary;
+ padding: 8rpx 0;
+}
+
+.cu-recharge-summary {
+ display: flex;
+ align-items: baseline;
+ gap: 8rpx;
+ padding: 0 8rpx 16rpx;
+}
+
+.cu-recharge-summary__count {
+ font-size: 44rpx;
+ font-weight: 700;
+ color: $cu-text;
+ line-height: 1;
+}
+
+.cu-recharge-summary__label {
+ font-size: 26rpx;
+ color: $cu-text-muted;
+}
+
+.cu-recharge-record-page .cu-list-wrap {
+ padding-top: 0;
+}
+
+.cu-recharge-card {
+ display: flex;
+ background: $cu-card-bg;
+ border-radius: $cu-radius;
+ box-shadow: $cu-shadow;
+ overflow: hidden;
+ margin-bottom: 20rpx;
+}
+
+.cu-recharge-card__accent {
+ width: 8rpx;
+ flex-shrink: 0;
+}
+
+.cu-recharge-card__accent--electric {
+ background: linear-gradient(180deg, #ffc069 0%, #fa8c16 100%);
+}
+
+.cu-recharge-card__accent--conditioner {
+ background: linear-gradient(180deg, #5cdbd3 0%, #13c2c2 100%);
+}
+
+.cu-recharge-card__body {
+ flex: 1;
+ min-width: 0;
+ padding: 24rpx 24rpx 20rpx;
+}
+
+.cu-recharge-card__top {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16rpx;
+ margin-bottom: 16rpx;
+}
+
+.cu-recharge-card__type {
+ display: flex;
+ align-items: center;
+ gap: 8rpx;
+ padding: 8rpx 16rpx;
+ border-radius: 999rpx;
+ flex-shrink: 0;
+}
+
+.cu-recharge-card__type--electric {
+ background: #fff7e6;
+}
+
+.cu-recharge-card__type--conditioner {
+ background: #e6fffb;
+}
+
+.cu-recharge-card__type-icon {
+ font-size: 22rpx;
+ line-height: 1;
+}
+
+.cu-recharge-card__type-text {
+ font-size: 22rpx;
+ font-weight: 600;
+ line-height: 1.2;
+}
+
+.cu-recharge-card__type--electric .cu-recharge-card__type-text {
+ color: #d46b08;
+}
+
+.cu-recharge-card__type--conditioner .cu-recharge-card__type-text {
+ color: #08979c;
+}
+
+.cu-recharge-card__title {
+ display: block;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $cu-text;
+ line-height: 1.4;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.cu-recharge-card__operator {
+ display: block;
+ font-size: 24rpx;
+ color: $cu-text-muted;
+ margin-top: 8rpx;
+ line-height: 1.4;
+}
+
+.cu-recharge-card__amount-box {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 24rpx;
+ margin-top: 20rpx;
+ padding: 20rpx;
+ background: #f8fafc;
+ border-radius: 16rpx;
+}
+
+.cu-recharge-card__amount-main {
+ flex: 1;
+ min-width: 0;
+}
+
+.cu-recharge-card__amount-side {
+ flex-shrink: 0;
+ text-align: right;
+}
+
+.cu-recharge-card__amount-label {
+ display: block;
+ font-size: 22rpx;
+ color: $cu-text-muted;
+ margin-bottom: 8rpx;
+ line-height: 1.2;
+}
+
+.cu-recharge-card__amount-value {
+ display: block;
+ font-size: 36rpx;
+ font-weight: 700;
+ color: $cu-primary;
+ line-height: 1.2;
+ font-variant-numeric: tabular-nums;
+}
+
+.cu-recharge-card__amount-side-value {
+ display: block;
+ font-size: 28rpx;
+ font-weight: 600;
+ color: $cu-text;
+ line-height: 1.2;
+ font-variant-numeric: tabular-nums;
+}
+
+.cu-recharge-card__amount-side-value--success {
+ color: $cu-success;
+}
+
+.cu-recharge-card__amount-side-value--danger {
+ color: $cu-danger;
+}
+
+.cu-recharge-card__foot {
+ margin-top: 16rpx;
+ padding-top: 16rpx;
+ border-top: 1rpx solid #f0f2f6;
+}
+
+.cu-recharge-card__time {
+ font-size: 24rpx;
+ color: $cu-text-muted;
+ line-height: 1.4;
+}
+
.cu-bill-tabs {
margin-bottom: 16rpx;
}
diff --git a/h5/utils/utils.js b/h5/utils/utils.js
index 08378e8..b0423f4 100644
--- a/h5/utils/utils.js
+++ b/h5/utils/utils.js
@@ -161,3 +161,11 @@
return false;
}
}
+
+/** 璐︽埛浣欓鑹茶皟锛�>0 缁胯壊锛�<=0 绾㈣壊锛屾棤鏁堝�兼棤鑹茶皟 */
+export function getBalanceTone (val) {
+ if (val === null || val === undefined || val === '') return ''
+ const n = Number(val)
+ if (Number.isNaN(n)) return ''
+ return n > 0 ? 'success' : 'danger'
+}
diff --git a/server/db/business.yw_conditioner_device.menu.sql b/server/db/business.yw_conditioner_device.menu.sql
new file mode 100644
index 0000000..e9c90fe
--- /dev/null
+++ b/server/db/business.yw_conditioner_device.menu.sql
@@ -0,0 +1,20 @@
+-- 绌鸿皟澶氳仈鏈猴細鏂板瀛愯彍鍗曘�岀┖璋冭澶囩鐞嗐��+ 瓒呯骇绠$悊鍛樻巿鏉冿紙鍙噸澶嶆墽琛岋級
+
+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/ywconditionerdevice', '绌鸿皟鍐呮満鍒楄〃涓庢埧婧愬叧鑱旂淮鎶�', NULL, 0,
+ IFNULL((SELECT MAX(sm.`SORT`) FROM `SYSTEM_MENU` sm WHERE sm.`PARENT_ID` = p.`ID` AND sm.`DELETED` = 0), 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/ywconditionerdevice')
+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.`PATH` = '/business/ywconditionerdevice' AND menu.`DELETED` = 0
+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_customer_member_openid.sql b/server/db/business.yw_customer_member_openid.sql
new file mode 100644
index 0000000..dd57603
--- /dev/null
+++ b/server/db/business.yw_customer_member_openid.sql
@@ -0,0 +1,22 @@
+-- 鍟嗘埛 H5 浜哄憳璐﹀彿 openid锛坢ember.type=3锛夛紝鏀寔鍚屼竴瀹㈡埛澶氫汉鍛樼嫭绔嬬櫥褰�
+SET @db = DATABASE();
+
+SET @sql = IF(
+ (SELECT COUNT(*) FROM information_schema.statistics
+ WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'member' AND INDEX_NAME = 'idx_member_openid') = 0,
+ 'ALTER TABLE `member` ADD INDEX `idx_member_openid` (`openid`)',
+ 'SELECT 1'
+);
+PREPARE stmt FROM @sql;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- 灏嗗巻鍙� yw_customer.openid 杩佺Щ鍒伴粯璁よ仈绯讳汉 member.openid
+UPDATE `member` m
+INNER JOIN `yw_customer` c ON c.member_id = m.id AND c.isdeleted = 0
+SET m.openid = c.openid, m.edit_date = NOW()
+WHERE m.isdeleted = 0
+ AND m.type = 3
+ AND c.openid IS NOT NULL
+ AND c.openid != ''
+ AND (m.openid IS NULL OR m.openid = '');
diff --git a/server/db/business.yw_electrical_charge.member.sql b/server/db/business.yw_electrical_charge.member.sql
new file mode 100644
index 0000000..98e3723
--- /dev/null
+++ b/server/db/business.yw_electrical_charge.member.sql
@@ -0,0 +1,32 @@
+-- 鍏呭�艰褰曪細鍏宠仈鎿嶄綔浜哄憳锛坢ember锛夊強灞曠ず濮撳悕
+SET @db = DATABASE();
+
+SET @sql = IF(
+ (SELECT COUNT(*) FROM information_schema.columns
+ WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'yw_electrical_charge' AND COLUMN_NAME = 'member_id') = 0,
+ 'ALTER TABLE `yw_electrical_charge` ADD COLUMN `member_id` int DEFAULT NULL COMMENT ''鍏呭�兼搷浣滀汉鍛�(member.id)'' AFTER `customer_id`',
+ 'SELECT 1'
+);
+PREPARE stmt FROM @sql;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @sql = IF(
+ (SELECT COUNT(*) FROM information_schema.columns
+ WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'yw_electrical_charge' AND COLUMN_NAME = 'recharge_user_name') = 0,
+ 'ALTER TABLE `yw_electrical_charge` ADD COLUMN `recharge_user_name` varchar(64) DEFAULT NULL COMMENT ''鍏呭�间汉濮撳悕'' AFTER `member_id`',
+ 'SELECT 1'
+);
+PREPARE stmt FROM @sql;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @sql = IF(
+ (SELECT COUNT(*) FROM information_schema.columns
+ WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'yw_wx_pay_order' AND COLUMN_NAME = 'member_id') = 0,
+ 'ALTER TABLE `yw_wx_pay_order` ADD COLUMN `member_id` int DEFAULT NULL COMMENT ''涓嬪崟鎿嶄綔浜哄憳(member.id)'' AFTER `customer_id`',
+ 'SELECT 1'
+);
+PREPARE stmt FROM @sql;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
diff --git a/server/system_service/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java b/server/system_service/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java
index b5a1acc..e277949 100644
--- a/server/system_service/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java
+++ b/server/system_service/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java
@@ -132,21 +132,29 @@
try {
//鐧诲嚭娴峰悍绯荤粺鏁版嵁
LoginUserInfo loginUserInfo = this.getUserInfoByToken(token);
- String url = systemDictDataBiz.queryByCode(Constants.HK_PARAM,Constants.HK_HTTPS).getCode() +
- systemDictDataBiz.queryByCode(Constants.HK_PARAM,Constants.HK_HOST).getCode() +
- systemDictDataBiz.queryByCode(Constants.HK_PARAM,Constants.LOGIN_OUT_URL).getCode();
- if(StringUtils.isNotBlank(loginUserInfo.getHkMenuToken())){
+ if (loginUserInfo != null && StringUtils.isNotBlank(loginUserInfo.getHkMenuToken())) {
+ String url = systemDictDataBiz.queryByCode(Constants.HK_PARAM,Constants.HK_HTTPS).getCode() +
+ systemDictDataBiz.queryByCode(Constants.HK_PARAM,Constants.HK_HOST).getCode() +
+ systemDictDataBiz.queryByCode(Constants.HK_PARAM,Constants.LOGIN_OUT_URL).getCode();
log.info("璋冭捣娴峰悍閫�鍑虹櫥褰�=======================>"+url+"?token="+loginUserInfo.getHkMenuToken());
-// this.hkLoginOut(url+"?token="+loginUserInfo.getHkMenuToken());
HttpsUtil.get(url+"?token="+loginUserInfo.getHkMenuToken(),true);
}
- redisTemplate.delete(Constants.REDIS_TOKEN_KEY+token);//鍒犻櫎鑰佺殑token
- systemLoginService.cleanOpenid(loginUserInfo.getId());
+ invalidateToken(token);
+ if (loginUserInfo != null && !Constants.equalsInteger(loginUserInfo.getH5UserType(), LoginUserInfo.H5_USER_CUSTOMER)) {
+ systemLoginService.cleanOpenid(loginUserInfo.getId());
+ }
} catch (Exception e) {
e.printStackTrace();
}
}
+ /** 浠呭け鏁� token锛屼笉娓呯悊 openid锛堝晢鎴� H5 閫�鍑虹敱涓氬姟灞傚崟鐙В缁� member锛� */
+ public void invalidateToken(String token) {
+ if (StringUtils.isNotBlank(token)) {
+ redisTemplate.delete(Constants.REDIS_TOKEN_KEY + token);
+ }
+ }
+
public void hkLoginOut(String url){
try {
diff --git a/server/system_service/src/main/java/com/doumee/core/model/LoginUserInfo.java b/server/system_service/src/main/java/com/doumee/core/model/LoginUserInfo.java
index fc97fe1..8bf289f 100644
--- a/server/system_service/src/main/java/com/doumee/core/model/LoginUserInfo.java
+++ b/server/system_service/src/main/java/com/doumee/core/model/LoginUserInfo.java
@@ -59,6 +59,15 @@
@ApiModelProperty("鍟嗘埛ID锛坔5UserType=1鏃舵湁鍊硷級")
private Integer customerId;
+ @ApiModelProperty("鍟嗘埛鍚嶇О锛坔5UserType=1鏃舵湁鍊硷級")
+ private String customerName;
+
+ @ApiModelProperty("鍟嗘埛浜哄憳濮撳悕锛坔5UserType=1鏃舵湁鍊硷級")
+ private String memberName;
+
+ @ApiModelProperty("灞曠ず鍚嶇О锛氬鎴峰悕绉�-浜哄憳鍚嶇О")
+ private String displayName;
+
public static final int H5_USER_OPS = 0;
public static final int H5_USER_CUSTOMER = 1;
public static final int SOURCE_H5_CUSTOMER = 10;
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 9f79a7a..35e33c4 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,6 +7,7 @@
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.dto.YwConditionerEditDTO;
import com.doumee.dao.business.dto.YwConditionerLockDTO;
import com.doumee.dao.business.dto.YwConditionerOperateDTO;
import com.doumee.dao.business.model.YwConditioner;
@@ -158,4 +159,30 @@
public ApiResponse<List<Map<String, Object>>> gatewayOptions(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
return ApiResponse.success(ywConditionerService.gatewayOptions());
}
+
+ @ApiOperation("绌鸿皟璁惧绠$悊鍒嗛〉")
+ @PostMapping("/deviceManagePage")
+ @CloudRequiredPermission("business:ywconditioner:query")
+ public ApiResponse<PageData<YwConditioner>> deviceManagePage(@RequestBody PageWrap<YwConditioner> pageWrap,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(ywConditionerService.findDeviceManagePage(pageWrap));
+ }
+
+ @ApiOperation("绌鸿皟璁惧绠$悊璇︽儏")
+ @GetMapping("/manageDetail/{id}")
+ @CloudRequiredPermission("business:ywconditioner:update")
+ public ApiResponse<YwConditionerEditDTO> manageDetail(@PathVariable Integer id,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ return ApiResponse.success(ywConditionerService.getManageDetail(id));
+ }
+
+ @PreventRepeat
+ @ApiOperation("淇濆瓨绌鸿皟鎴挎簮鍏宠仈")
+ @PostMapping("/saveManageDetail")
+ @CloudRequiredPermission("business:ywconditioner:update")
+ public ApiResponse<Void> saveManageDetail(@RequestBody YwConditionerEditDTO dto,
+ @RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ ywConditionerService.updateManageDetail(dto, this.getLoginUser(token));
+ return ApiResponse.success(null);
+ }
}
diff --git a/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/web/YwCustomerH5Controller.java b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/web/YwCustomerH5Controller.java
index 0cf420e..6de51ca 100644
--- a/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/web/YwCustomerH5Controller.java
+++ b/server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/web/YwCustomerH5Controller.java
@@ -95,7 +95,20 @@
if (user == null || !Constants.equalsInteger(user.getH5UserType(), LoginUserInfo.H5_USER_CUSTOMER)) {
return ApiResponse.failed("鐧诲綍宸插け鏁�");
}
- return ApiResponse.success(ywCustomerH5AuthService.buildLoginUserInfo(user.getCustomerId()));
+ return ApiResponse.success(ywCustomerH5AuthService.buildLoginUserInfo(user.getCustomerId(), user.getMemberId()));
+ }
+
+ @ApiOperation("鍟嗘埛閫�鍑虹櫥褰�")
+ @PostMapping("/logout")
+ public ApiResponse<String> logout(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
+ try {
+ LoginUserInfo user = requireCustomerUser(token);
+ ywCustomerH5AuthService.logout(user, token);
+ return ApiResponse.success("閫�鍑烘垚鍔�");
+ } catch (Exception e) {
+ log.error("customer logout failed", e);
+ return ApiResponse.failed(ResponseStatus.SERVER_ERROR.getCode(), "閫�鍑哄け璐�");
+ }
}
@ApiOperation("宸ヤ綔鍙拌疆鎾浘")
@@ -109,7 +122,7 @@
@GetMapping("/home")
public ApiResponse<Map<String, Object>> home(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) {
LoginUserInfo user = requireCustomerUser(token);
- return ApiResponse.success(ywCustomerH5BizService.home(user.getCustomerId()));
+ return ApiResponse.success(ywCustomerH5BizService.home(user.getCustomerId(), user.getMemberId()));
}
@ApiOperation("浜ょ數璐硅澶囧垪琛�")
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerEditDTO.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerEditDTO.java
new file mode 100644
index 0000000..f739b59
--- /dev/null
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwConditionerEditDTO.java
@@ -0,0 +1,14 @@
+package com.doumee.dao.business.dto;
+
+import com.doumee.dao.business.model.YwConditioner;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class YwConditionerEditDTO extends YwConditioner {
+
+ private List<Integer> roomIds;
+}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwCustomerRechargeRecordVO.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwCustomerRechargeRecordVO.java
index 8ff517e..8eec451 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwCustomerRechargeRecordVO.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwCustomerRechargeRecordVO.java
@@ -58,4 +58,6 @@
private Integer objId;
private String address;
private String name;
+ private Integer memberId;
+ private String rechargeUserName;
}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/h5/CustomerDeviceH5VO.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/h5/CustomerDeviceH5VO.java
index 9c3e768..378d07c 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/h5/CustomerDeviceH5VO.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/h5/CustomerDeviceH5VO.java
@@ -17,6 +17,7 @@
private String roomInfo;
private List<String> roomList;
private String meterAccountNo;
+ private String meterAddress;
private BigDecimal totalUsage;
private BigDecimal balance;
private Boolean balanceLow;
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 da9272c..4e5e117 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
@@ -139,4 +139,16 @@
@TableField(exist = false)
@ApiModelProperty("鍟嗘埛鍏宠仈-鐢佃垂鍗犳瘮%")
private Integer devRatio;
+
+ @TableField(exist = false)
+ @ApiModelProperty("缁戝畾鎴块棿灞曠ず鍚嶏紙澶氭埧婧愶級")
+ private String roomNames;
+
+ @TableField(exist = false)
+ @ApiModelProperty("璁惧绠$悊鍒楄〃-鍚嶇О/缂栧彿鍏抽敭璇�")
+ private String manageKeyword;
+
+ @TableField(exist = false)
+ @ApiModelProperty("璁惧绠$悊鍒楄〃-鍦ㄧ嚎绛涢�夛細鍦ㄧ嚎/绂荤嚎")
+ private String manageOnlineFilter;
}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwElectricalCharge.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwElectricalCharge.java
index 7970889..631a406 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwElectricalCharge.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwElectricalCharge.java
@@ -65,6 +65,10 @@
@ApiModelProperty("瀹㈡埛涓婚敭锛堝叧鑱攜w_customer)")
@ExcelColumn(name="瀹㈡埛涓婚敭锛堝叧鑱攜w_customer)",index=14 ,width=10)
private Integer customerId;
+ @ApiModelProperty("鍏呭�兼搷浣滀汉鍛�(member.id)")
+ private Integer memberId;
+ @ApiModelProperty("鍏呭�间汉濮撳悕")
+ private String rechargeUserName;
@ApiModelProperty("寰俊鏀粯璁㈠崟鍙�")
private String wxOrderNo;
@ApiModelProperty("鍏ヨ处鏃ユ湡")
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwWxPayOrder.java b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwWxPayOrder.java
index 21687ad..b2cc867 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwWxPayOrder.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/YwWxPayOrder.java
@@ -29,6 +29,8 @@
private String orderNo;
@ApiModelProperty("浠樻鍟嗘埛")
private Integer customerId;
+ @ApiModelProperty("涓嬪崟鎿嶄綔浜哄憳(member.id)")
+ private Integer memberId;
@ApiModelProperty("0鐢佃〃 1绌鸿皟 2璐﹀崟")
private Integer orderType;
@ApiModelProperty("涓氬姟寮曠敤ID")
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 682e7e0..27ba7ff 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,6 +3,7 @@
import com.doumee.core.model.LoginUserInfo;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.dto.YwConditionerEditDTO;
import com.doumee.dao.business.dto.YwConditionerLockDTO;
import com.doumee.dao.business.dto.YwConditionerOperateDTO;
import com.doumee.dao.business.model.YwConditioner;
@@ -37,4 +38,10 @@
PageData<YwConditionerActions> historyPage(PageWrap<YwConditionerActions> pageWrap);
List<Map<String, Object>> gatewayOptions();
+
+ PageData<YwConditioner> findDeviceManagePage(PageWrap<YwConditioner> pageWrap);
+
+ YwConditionerEditDTO getManageDetail(Integer id);
+
+ void updateManageDetail(YwConditionerEditDTO dto, LoginUserInfo user);
}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerDeviceAutoBindService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerDeviceAutoBindService.java
index 9b2dbfa..a1c34f1 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerDeviceAutoBindService.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerDeviceAutoBindService.java
@@ -2,6 +2,9 @@
import com.doumee.core.model.LoginUserInfo;
+import java.util.List;
+import java.util.Map;
+
/**
* 鏍规嵁鍟嗘埛绉熻祦鍚堝悓鑷姩鍏宠仈鐢佃〃/绌鸿皟璁惧
*/
@@ -15,4 +18,20 @@
/** 鍚堝悓閫�绉�/鍒版湡鏃惰В闄よ嚜鍔ㄥ叧鑱� */
void unbindByContractId(Integer contractId, LoginUserInfo user);
+
+ /** 鍒锋柊鍟嗘埛璁惧锛氭竻鐞嗗け鏁堝悎鍚岀粦瀹氬苟鎸夋湁鏁堝悎鍚岄噸鏂板叧鑱� */
+ void refreshCustomerDevices(Integer customerId, LoginUserInfo user);
+
+ /** 鏈夋晥鍚堝悓绉熻祦鎴挎簮 ID */
+ List<Integer> listActiveContractRoomIds(Integer customerId);
+
+ /** 鏈夋晥鍚堝悓鍏宠仈鐨勭數琛� ID */
+ List<Integer> listElectricalIdsByActiveContracts(Integer customerId);
+
+ /** 鏈夋晥鍚堝悓鍏宠仈鐨勭┖璋冨唴鏈� ID */
+ List<Integer> listConditionerIdsByActiveContracts(Integer customerId);
+
+ Map<Integer, List<Integer>> batchListElectricalIdsByActiveContracts(List<Integer> customerIds);
+
+ Map<Integer, List<Integer>> batchListConditionerIdsByActiveContracts(List<Integer> customerIds);
}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5AuthService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5AuthService.java
index ca0cae6..962b68f 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5AuthService.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5AuthService.java
@@ -11,8 +11,11 @@
String loginByOpenId(String openId);
- LoginUserInfo buildLoginUserInfo(Integer customerId);
+ LoginUserInfo buildLoginUserInfo(Integer customerId, Integer memberId);
/** 鍙戦獙璇佺爜鍓嶆牎楠岋細鍟嗘埛鎵嬫満鍙烽』瀵瑰簲鏈夋晥 yw_customer锛堝惈鑱旂郴浜� member.phone锛� */
void assertActiveCustomerByPhone(String phone);
+
+ /** 閫�鍑虹櫥褰曪細瑙g粦 member openid 骞跺け鏁� token */
+ void logout(LoginUserInfo user, String token);
}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5BizService.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5BizService.java
index 30760de..0576e25 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5BizService.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwCustomerH5BizService.java
@@ -16,7 +16,7 @@
List<YwH5Banner> listBanners();
- Map<String, Object> home(Integer customerId);
+ Map<String, Object> home(Integer customerId, Integer memberId);
PageData<CustomerDeviceH5VO> devicePage(PageWrap<CustomerDeviceQueryDTO> pageWrap, Integer customerId);
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/SmsEmailServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/SmsEmailServiceImpl.java
index 4129a20..05bea91 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/SmsEmailServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/SmsEmailServiceImpl.java
@@ -176,13 +176,6 @@
}
private YwCustomer findMerchantByPhone(String phone) {
- YwCustomer byCustomerPhone = ywCustomerMapper.selectOne(new QueryWrapper<YwCustomer>().lambda()
- .eq(YwCustomer::getIsdeleted, Constants.ZERO)
- .eq(YwCustomer::getPhone, phone)
- .last(" limit 1 "));
- if (byCustomerPhone != null) {
- return byCustomerPhone;
- }
Member member = memberMapper.selectOne(new QueryWrapper<Member>().lambda()
.eq(Member::getIsdeleted, Constants.ZERO)
.eq(Member::getType, Constants.memberType.customer)
@@ -190,19 +183,22 @@
.isNotNull(Member::getCustomerId)
.orderByDesc(Member::getId)
.last(" limit 1 "));
- if (member == null || member.getCustomerId() == null) {
- return null;
- }
- YwCustomer customer = ywCustomerMapper.selectOne(new QueryWrapper<YwCustomer>().lambda()
- .eq(YwCustomer::getId, member.getCustomerId())
- .eq(YwCustomer::getIsdeleted, Constants.ZERO)
- .last(" limit 1 "));
- if (customer != null) {
- return customer;
+ if (member != null && member.getCustomerId() != null) {
+ YwCustomer customer = ywCustomerMapper.selectOne(new QueryWrapper<YwCustomer>().lambda()
+ .eq(YwCustomer::getId, member.getCustomerId())
+ .eq(YwCustomer::getIsdeleted, Constants.ZERO)
+ .last(" limit 1 "));
+ if (customer != null) {
+ return customer;
+ }
+ return ywCustomerMapper.selectOne(new QueryWrapper<YwCustomer>().lambda()
+ .eq(YwCustomer::getIsdeleted, Constants.ZERO)
+ .eq(YwCustomer::getMemberId, member.getId())
+ .last(" limit 1 "));
}
return ywCustomerMapper.selectOne(new QueryWrapper<YwCustomer>().lambda()
.eq(YwCustomer::getIsdeleted, Constants.ZERO)
- .eq(YwCustomer::getMemberId, member.getId())
+ .eq(YwCustomer::getPhone, phone)
.last(" limit 1 "));
}
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 8c74504..ed66477 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,13 +8,19 @@
import com.doumee.core.utils.Constants;
import com.doumee.core.utils.DateUtil;
import com.doumee.core.utils.Utils;
+import com.doumee.dao.business.dto.YwConditionerEditDTO;
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.YwElectricalRoomMapper;
import com.doumee.dao.business.model.YwConditioner;
import com.doumee.dao.business.model.YwConditionerActions;
import com.doumee.dao.business.model.YwConditionerGateway;
+import com.doumee.dao.business.model.YwElectricalRoom;
+import com.doumee.dao.business.model.YwBuilding;
+import com.doumee.dao.business.model.YwFloor;
+import com.doumee.dao.business.model.YwRoom;
import com.doumee.service.business.ConditionerBizService;
import com.doumee.service.business.YwConditionerActionsService;
import com.doumee.service.business.YwConditionerService;
@@ -25,9 +31,13 @@
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -45,6 +55,8 @@
@Autowired
private YwConditionerMapper ywConditionerMapper;
+ @Autowired
+ private YwElectricalRoomMapper ywElectricalRoomMapper;
@Autowired
private YwConditionerGatewayMapper gatewayMapper;
@Autowired
@@ -252,4 +264,140 @@
public List<Map<String, Object>> gatewayOptions() {
return conditionerBizService.gatewayOptions();
}
+
+ @Override
+ public PageData<YwConditioner> findDeviceManagePage(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)
+ .and(StringUtils.isNotBlank(model.getManageKeyword()), w -> w
+ .like(YwConditioner::getName, model.getManageKeyword())
+ .or().like(YwConditioner::getCode, model.getManageKeyword())
+ .or().like(YwConditioner::getRoomName, model.getManageKeyword())
+ .or().like(YwConditioner::getWgMac, model.getManageKeyword()))
+ .eq(StringUtils.isNotBlank(model.getManageOnlineFilter()), YwConditioner::getOnline, model.getManageOnlineFilter())
+ .eq(StringUtils.isNotBlank(model.getWgMacFilter()), YwConditioner::getWgMac, model.getWgMacFilter())
+ .orderByDesc(YwConditioner::getCreateDate)
+ .orderByDesc(YwConditioner::getId);
+ PageData<YwConditioner> pageData = PageData.from(
+ ywConditionerMapper.selectJoinPage(page, YwConditioner.class, queryWrapper));
+ fillRoomNames(pageData.getRecords());
+ fillGatewayBzFromGateway(pageData.getRecords());
+ return pageData;
+ }
+
+ @Override
+ public YwConditionerEditDTO getManageDetail(Integer id) {
+ YwConditioner conditioner = findById(id);
+ if (conditioner == null || Objects.equals(conditioner.getIsdeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+ YwConditionerEditDTO dto = new YwConditionerEditDTO();
+ BeanUtils.copyProperties(conditioner, dto);
+ List<YwElectricalRoom> rooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
+ .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO)
+ .eq(YwElectricalRoom::getType, Constants.ONE)
+ .eq(YwElectricalRoom::getObjId, id));
+ dto.setRoomIds(rooms.stream().map(YwElectricalRoom::getRoomId).filter(Objects::nonNull).collect(Collectors.toList()));
+ fillRoomNames(Collections.singletonList(dto));
+ return dto;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateManageDetail(YwConditionerEditDTO dto, LoginUserInfo user) {
+ if (dto == null || dto.getId() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST);
+ }
+ YwConditioner exist = findById(dto.getId());
+ if (exist == null || Objects.equals(exist.getIsdeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+ saveRooms(dto.getId(), dto.getRoomIds(), user);
+ Integer primaryRoomId = CollectionUtils.isEmpty(dto.getRoomIds()) ? null : dto.getRoomIds().get(0);
+ ywConditionerMapper.update(null, new UpdateWrapper<YwConditioner>().lambda()
+ .eq(YwConditioner::getId, dto.getId())
+ .set(YwConditioner::getRoomId, primaryRoomId)
+ .set(YwConditioner::getRemark, dto.getRemark())
+ .set(YwConditioner::getEditor, user.getId())
+ .set(YwConditioner::getEditDate, new Date()));
+ }
+
+ private void saveRooms(Integer conditionerId, List<Integer> roomIds, LoginUserInfo user) {
+ ywElectricalRoomMapper.update(null, new UpdateWrapper<YwElectricalRoom>().lambda()
+ .set(YwElectricalRoom::getIsdeleted, Constants.ONE)
+ .set(YwElectricalRoom::getEditDate, new Date())
+ .set(YwElectricalRoom::getEditor, user.getId())
+ .eq(YwElectricalRoom::getObjId, conditionerId)
+ .eq(YwElectricalRoom::getType, Constants.ONE));
+ if (CollectionUtils.isEmpty(roomIds)) {
+ return;
+ }
+ int sort = 0;
+ for (Integer roomId : roomIds) {
+ if (roomId == null) {
+ continue;
+ }
+ YwElectricalRoom rel = new YwElectricalRoom();
+ rel.setCreator(user.getId());
+ rel.setCreateDate(new Date());
+ rel.setEditor(user.getId());
+ rel.setEditDate(new Date());
+ rel.setIsdeleted(Constants.ZERO);
+ rel.setType(Constants.ONE);
+ rel.setObjId(conditionerId);
+ rel.setRoomId(roomId);
+ rel.setSortnum(++sort);
+ ywElectricalRoomMapper.insert(rel);
+ }
+ }
+
+ private void fillRoomNames(List<? extends YwConditioner> list) {
+ if (CollectionUtils.isEmpty(list)) {
+ return;
+ }
+ List<Integer> ids = list.stream().map(YwConditioner::getId).filter(Objects::nonNull).collect(Collectors.toList());
+ if (ids.isEmpty()) {
+ return;
+ }
+ MPJLambdaWrapper<YwElectricalRoom> wrapper = new MPJLambdaWrapper<>();
+ wrapper.selectAll(YwElectricalRoom.class)
+ .selectAs(YwRoom::getRoomNum, YwElectricalRoom::getRoomName)
+ .selectAs(YwBuilding::getName, YwElectricalRoom::getBuildingName)
+ .selectAs(YwFloor::getName, YwElectricalRoom::getFloorName)
+ .leftJoin(YwRoom.class, YwRoom::getId, YwElectricalRoom::getRoomId)
+ .leftJoin(YwFloor.class, YwFloor::getId, YwRoom::getFloor)
+ .leftJoin(YwBuilding.class, YwBuilding::getId, YwRoom::getBuildingId)
+ .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO)
+ .eq(YwElectricalRoom::getType, Constants.ONE)
+ .in(YwElectricalRoom::getObjId, ids);
+ List<YwElectricalRoom> rooms = ywElectricalRoomMapper.selectJoinList(YwElectricalRoom.class, wrapper);
+ Map<Integer, List<YwElectricalRoom>> grouped = rooms.stream()
+ .collect(Collectors.groupingBy(YwElectricalRoom::getObjId));
+ for (YwConditioner row : list) {
+ List<YwElectricalRoom> rs = grouped.get(row.getId());
+ if (CollectionUtils.isEmpty(rs)) {
+ continue;
+ }
+ row.setRoomNames(rs.stream().map(this::formatRoomPath).filter(StringUtils::isNotBlank)
+ .collect(Collectors.joining("銆�")));
+ }
+ }
+
+ private String formatRoomPath(YwElectricalRoom room) {
+ List<String> parts = new ArrayList<>();
+ if (StringUtils.isNotBlank(room.getBuildingName())) {
+ parts.add(room.getBuildingName());
+ }
+ if (StringUtils.isNotBlank(room.getFloorName())) {
+ parts.add(room.getFloorName());
+ }
+ if (StringUtils.isNotBlank(room.getRoomName())) {
+ parts.add(room.getRoomName());
+ }
+ return String.join("/", parts);
+ }
}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwContractServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwContractServiceImpl.java
index 20689e4..b1d2cb3 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwContractServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwContractServiceImpl.java
@@ -383,6 +383,9 @@
update.setBtRemark(getbackRentRemarkByParam(param));
ywContractMapper.updateById(update);
dealLogBiz(param,Constants.YwLogType.CONTRACT_BACK, param.getLoginUserInfo().getRealname(),getbackRentLogByParam(param));
+ if (model.getRenterId() != null) {
+ ywCustomerDeviceAutoBindService.refreshCustomerDevices(model.getRenterId(), param.getLoginUserInfo());
+ }
//濡傛灉閫�绉熸棩鏈熷皬浜庡綋鍓嶆棩鏈� 鍒欑洿鎺ラ噴鏀炬埧婧愪俊鎭� 鏈璧�
if(Utils.Date.getEnd(param.getBtDate()).getTime()<System.currentTimeMillis()){
List<YwContractRoom> contractRoomList = ywContractRoomMapper.selectList(new QueryWrapper<YwContractRoom>().lambda()
@@ -1645,7 +1648,7 @@
queryWrapper.eq(YwContract::getType, pageWrap.getModel().getType());
}
if (pageWrap.getModel().getCode() != null) {
- queryWrapper.eq(YwContract::getCode, pageWrap.getModel().getCode());
+ queryWrapper.like(YwContract::getCode, pageWrap.getModel().getCode());
}
if (pageWrap.getModel().getUserId() != null) {
queryWrapper.eq(YwContract::getUserId, pageWrap.getModel().getUserId());
@@ -1716,6 +1719,9 @@
if (pageWrap.getModel().getCompanyName() != null) {
queryWrapper.like(Company::getName, pageWrap.getModel().getCompanyName());
}
+ if (pageWrap.getModel().getRenterName() != null) {
+ queryWrapper.like(YwCustomer::getName, pageWrap.getModel().getRenterName());
+ }
if (pageWrap.getModel().getRoomId() != null) {
queryWrapper.apply(" t.id in ( select ycr.CONTRACT_ID from yw_contract_room ycr where ycr.type = 0 and ycr.ROOM_ID = "+pageWrap.getModel().getRoomId()+" ) ");
}
@@ -1785,6 +1791,14 @@
.in(YwContract::getId,ywContractList.stream().map(i->i.getId()).collect(Collectors.toList()))
);
+ LoginUserInfo timerUser = new LoginUserInfo();
+ timerUser.setId(1);
+ timerUser.setRealname("timer");
+ for (YwContract c : ywContractList) {
+ if (c.getRenterId() != null) {
+ ywCustomerDeviceAutoBindService.refreshCustomerDevices(c.getRenterId(), timerUser);
+ }
+ }
List<YwContractRoom> contractRoomList = ywContractRoomMapper.selectList(new QueryWrapper<YwContractRoom>().lambda().in(YwContractRoom::getContractId,
ywContractList.stream().map(i->i.getId()).collect(Collectors.toList())));
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java
index 07a3d71..3fdaadc 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java
@@ -2,10 +2,10 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
-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.core.utils.Utils;
import com.doumee.dao.business.*;
import com.doumee.dao.business.dto.YwCustomerGsConfigDTO;
import com.doumee.dao.business.model.*;
@@ -13,6 +13,7 @@
import com.doumee.service.business.YwCustomerRechargeBizService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
@@ -40,6 +41,7 @@
@Autowired
private YwConditionerMapper ywConditionerMapper;
@Autowired
+ @Lazy
private YwCustomerRechargeBizService ywCustomerRechargeBizService;
@Override
@@ -49,38 +51,52 @@
return;
}
YwContract contract = ywContractMapper.selectById(contractId);
- if (contract == null || Objects.equals(contract.getIsdeleted(), Constants.ONE)) {
+ if (contract == null || Objects.equals(contract.getIsdeleted(), Constants.ONE) || contract.getRenterId() == null) {
return;
}
- if (contract.getRenterId() == null) {
- return;
- }
- if (!isActiveContract(contract)) {
- return;
- }
- List<Integer> roomIds = listContractRoomIds(contractId);
- if (roomIds.isEmpty()) {
- return;
- }
- bindElectricals(contract, roomIds, user);
- bindConditioners(contract, roomIds, user);
+ refreshCustomerDevices(contract.getRenterId(), user);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void syncByCustomerId(Integer customerId, LoginUserInfo user) {
+ refreshCustomerDevices(customerId, user);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void refreshCustomerDevices(Integer customerId, LoginUserInfo user) {
if (customerId == null) {
return;
}
+ LoginUserInfo opUser = user != null ? user : systemUser();
List<YwContract> contracts = ywContractMapper.selectList(new QueryWrapper<YwContract>().lambda()
.eq(YwContract::getRenterId, customerId)
- .eq(YwContract::getIsdeleted, Constants.ZERO)
- .in(YwContract::getStatus, Arrays.asList(Constants.ZERO, Constants.ONE, Constants.THREE)));
- for (YwContract c : contracts) {
- if (isActiveContract(c)) {
- syncByContractId(c.getId(), user);
+ .eq(YwContract::getIsdeleted, Constants.ZERO));
+ List<YwContract> activeContracts = contracts.stream().filter(this::isActiveContract).collect(Collectors.toList());
+ Set<Integer> activeContractIds = activeContracts.stream().map(YwContract::getId).collect(Collectors.toSet());
+
+ for (YwContract contract : contracts) {
+ if (!activeContractIds.contains(contract.getId())) {
+ unbindByContractId(contract.getId(), opUser);
}
}
+
+ Set<Integer> roomIds = new LinkedHashSet<>();
+ for (YwContract contract : activeContracts) {
+ roomIds.addAll(listContractRoomIds(contract.getId()));
+ }
+ if (roomIds.isEmpty()) {
+ clearContractElectricalBindings(customerId, opUser);
+ softDeleteAllConditionerRels(customerId, opUser);
+ return;
+ }
+
+ List<Integer> roomIdList = new ArrayList<>(roomIds);
+ for (YwContract contract : activeContracts) {
+ bindElectricals(contract, listContractRoomIds(contract.getId()), opUser);
+ }
+ bindConditionersMerged(customerId, roomIdList, opUser);
}
@Override
@@ -100,14 +116,153 @@
.eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO));
}
+ @Override
+ public List<Integer> listActiveContractRoomIds(Integer customerId) {
+ if (customerId == null) {
+ return Collections.emptyList();
+ }
+ List<YwContract> active = listActiveContracts(customerId);
+ Set<Integer> roomIds = new LinkedHashSet<>();
+ for (YwContract contract : active) {
+ roomIds.addAll(listContractRoomIds(contract.getId()));
+ }
+ return new ArrayList<>(roomIds);
+ }
+
+ @Override
+ public List<Integer> listElectricalIdsByActiveContracts(Integer customerId) {
+ List<Integer> roomIds = listActiveContractRoomIds(customerId);
+ if (roomIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<>(resolveElectricalIdsFromRooms(roomIds));
+ }
+
+ @Override
+ public List<Integer> listConditionerIdsByActiveContracts(Integer customerId) {
+ List<Integer> roomIds = listActiveContractRoomIds(customerId);
+ if (roomIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<>(resolveConditionerIdsFromRooms(roomIds));
+ }
+
+ @Override
+ public Map<Integer, List<Integer>> batchListElectricalIdsByActiveContracts(List<Integer> customerIds) {
+ return batchResolveDeviceIds(customerIds, Constants.ZERO);
+ }
+
+ @Override
+ public Map<Integer, List<Integer>> batchListConditionerIdsByActiveContracts(List<Integer> customerIds) {
+ return batchResolveDeviceIds(customerIds, Constants.ONE);
+ }
+
+ private Map<Integer, List<Integer>> batchResolveDeviceIds(List<Integer> customerIds, int deviceType) {
+ if (CollectionUtils.isEmpty(customerIds)) {
+ return Collections.emptyMap();
+ }
+ Map<Integer, Set<Integer>> roomsByCustomer = batchActiveContractRoomIds(customerIds);
+ Set<Integer> allRoomIds = roomsByCustomer.values().stream()
+ .flatMap(Set::stream).collect(Collectors.toCollection(LinkedHashSet::new));
+ Map<Integer, Set<Integer>> devicesByRoom = mapDevicesByRoom(allRoomIds, deviceType);
+
+ Map<Integer, List<Integer>> result = new LinkedHashMap<>();
+ for (Integer customerId : customerIds) {
+ Set<Integer> roomIds = roomsByCustomer.getOrDefault(customerId, Collections.emptySet());
+ Set<Integer> deviceIds = new LinkedHashSet<>();
+ for (Integer roomId : roomIds) {
+ deviceIds.addAll(devicesByRoom.getOrDefault(roomId, Collections.emptySet()));
+ }
+ result.put(customerId, new ArrayList<>(deviceIds));
+ }
+ return result;
+ }
+
+ private Map<Integer, Set<Integer>> batchActiveContractRoomIds(List<Integer> customerIds) {
+ List<YwContract> contracts = ywContractMapper.selectList(new QueryWrapper<YwContract>().lambda()
+ .in(YwContract::getRenterId, customerIds)
+ .eq(YwContract::getIsdeleted, Constants.ZERO));
+ Map<Integer, List<YwContract>> activeByCustomer = contracts.stream()
+ .filter(this::isActiveContract)
+ .collect(Collectors.groupingBy(YwContract::getRenterId));
+
+ List<Integer> contractIds = activeByCustomer.values().stream()
+ .flatMap(List::stream).map(YwContract::getId).distinct().collect(Collectors.toList());
+ Map<Integer, List<Integer>> roomsByContract = loadContractRoomMap(contractIds);
+
+ Map<Integer, Set<Integer>> roomsByCustomer = new LinkedHashMap<>();
+ for (Integer customerId : customerIds) {
+ Set<Integer> roomIds = new LinkedHashSet<>();
+ for (YwContract contract : activeByCustomer.getOrDefault(customerId, Collections.emptyList())) {
+ roomIds.addAll(roomsByContract.getOrDefault(contract.getId(), Collections.emptyList()));
+ }
+ roomsByCustomer.put(customerId, roomIds);
+ }
+ return roomsByCustomer;
+ }
+
+ private Map<Integer, List<Integer>> loadContractRoomMap(List<Integer> contractIds) {
+ if (CollectionUtils.isEmpty(contractIds)) {
+ return Collections.emptyMap();
+ }
+ return ywContractRoomMapper.selectList(new QueryWrapper<YwContractRoom>().lambda()
+ .in(YwContractRoom::getContractId, contractIds)
+ .eq(YwContractRoom::getType, Constants.ZERO)
+ .eq(YwContractRoom::getIsdeleted, Constants.ZERO))
+ .stream()
+ .collect(Collectors.groupingBy(YwContractRoom::getContractId,
+ Collectors.mapping(YwContractRoom::getRoomId,
+ Collectors.collectingAndThen(Collectors.toList(),
+ list -> list.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList())))));
+ }
+
+ private Map<Integer, Set<Integer>> mapDevicesByRoom(Set<Integer> roomIds, int deviceType) {
+ if (CollectionUtils.isEmpty(roomIds)) {
+ return Collections.emptyMap();
+ }
+ List<Integer> roomIdList = new ArrayList<>(roomIds);
+ Map<Integer, Set<Integer>> result = new HashMap<>();
+ List<YwElectricalRoom> relRooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
+ .in(YwElectricalRoom::getRoomId, roomIdList)
+ .eq(YwElectricalRoom::getType, deviceType)
+ .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO));
+ for (YwElectricalRoom rel : relRooms) {
+ if (rel.getRoomId() == null || rel.getObjId() == null) {
+ continue;
+ }
+ result.computeIfAbsent(rel.getRoomId(), k -> new LinkedHashSet<>()).add(rel.getObjId());
+ }
+ if (deviceType == Constants.ONE) {
+ List<YwConditioner> byRoom = ywConditionerMapper.selectList(new QueryWrapper<YwConditioner>().lambda()
+ .in(YwConditioner::getRoomId, roomIdList)
+ .eq(YwConditioner::getIsdeleted, Constants.ZERO));
+ for (YwConditioner conditioner : byRoom) {
+ if (conditioner.getRoomId() == null || conditioner.getId() == null) {
+ continue;
+ }
+ result.computeIfAbsent(conditioner.getRoomId(), k -> new LinkedHashSet<>()).add(conditioner.getId());
+ }
+ }
+ return result;
+ }
+
+ private List<YwContract> listActiveContracts(Integer customerId) {
+ return ywContractMapper.selectList(new QueryWrapper<YwContract>().lambda()
+ .eq(YwContract::getRenterId, customerId)
+ .eq(YwContract::getIsdeleted, Constants.ZERO))
+ .stream().filter(this::isActiveContract).collect(Collectors.toList());
+ }
+
private boolean isActiveContract(YwContract contract) {
- if (contract.getStartDate() == null || contract.getEndDate() == null) {
+ if (contract == null || contract.getStartDate() == null || contract.getEndDate() == null) {
+ return false;
+ }
+ if (Objects.equals(contract.getStatus(), Constants.FOUR)) {
return false;
}
long now = System.currentTimeMillis();
return contract.getStartDate().getTime() <= now
- && contract.getEndDate().getTime() >= now
- && !Objects.equals(contract.getStatus(), Constants.FOUR);
+ && Utils.Date.getEnd(contract.getEndDate()).getTime() >= now;
}
private List<Integer> listContractRoomIds(Integer contractId) {
@@ -119,13 +274,39 @@
.collect(Collectors.toList());
}
- private void bindElectricals(YwContract contract, List<Integer> roomIds, LoginUserInfo user) {
- List<YwElectricalRoom> relRooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
+ private Set<Integer> resolveElectricalIdsFromRooms(List<Integer> roomIds) {
+ if (CollectionUtils.isEmpty(roomIds)) {
+ return Collections.emptySet();
+ }
+ return ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
+ .in(YwElectricalRoom::getRoomId, roomIds)
+ .eq(YwElectricalRoom::getType, Constants.ZERO)
+ .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO))
+ .stream().map(YwElectricalRoom::getObjId).filter(Objects::nonNull)
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ }
+
+ private Set<Integer> resolveConditionerIdsFromRooms(List<Integer> roomIds) {
+ if (CollectionUtils.isEmpty(roomIds)) {
+ return Collections.emptySet();
+ }
+ Set<Integer> conditionerIds = new LinkedHashSet<>();
+ List<YwElectricalRoom> acRooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
.in(YwElectricalRoom::getRoomId, roomIds)
- .eq(YwElectricalRoom::getType, Constants.ZERO)
+ .eq(YwElectricalRoom::getType, Constants.ONE)
.eq(YwElectricalRoom::getIsdeleted, Constants.ZERO));
- Set<Integer> electricalIds = relRooms.stream().map(YwElectricalRoom::getObjId)
- .filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new));
+ acRooms.stream().map(YwElectricalRoom::getObjId).filter(Objects::nonNull).forEach(conditionerIds::add);
+ if (conditionerIds.isEmpty()) {
+ ywConditionerMapper.selectList(new QueryWrapper<YwConditioner>().lambda()
+ .in(YwConditioner::getRoomId, roomIds)
+ .eq(YwConditioner::getIsdeleted, Constants.ZERO))
+ .stream().map(YwConditioner::getId).filter(Objects::nonNull).forEach(conditionerIds::add);
+ }
+ return conditionerIds;
+ }
+
+ private void bindElectricals(YwContract contract, List<Integer> roomIds, LoginUserInfo user) {
+ Set<Integer> electricalIds = resolveElectricalIdsFromRooms(roomIds);
if (electricalIds.isEmpty()) {
return;
}
@@ -142,7 +323,8 @@
.eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO)
.last("limit 1"));
if (exist != null) {
- if (exist.getContractId() == null) {
+ if (!Objects.equals(exist.getContractId(), contract.getId())
+ || !Objects.equals(exist.getBindSource(), BIND_SOURCE_CONTRACT)) {
exist.setContractId(contract.getId());
exist.setBindSource(BIND_SOURCE_CONTRACT);
exist.setEditDate(now);
@@ -165,24 +347,14 @@
}
}
- private void bindConditioners(YwContract contract, List<Integer> roomIds, LoginUserInfo user) {
- Set<Integer> conditionerIds = new LinkedHashSet<>();
- List<YwElectricalRoom> acRooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
- .in(YwElectricalRoom::getRoomId, roomIds)
- .eq(YwElectricalRoom::getType, Constants.ONE)
- .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO));
- acRooms.stream().map(YwElectricalRoom::getObjId).filter(Objects::nonNull).forEach(conditionerIds::add);
+ private void bindConditionersMerged(Integer customerId, List<Integer> roomIds, LoginUserInfo user) {
+ Set<Integer> conditionerIds = resolveConditionerIdsFromRooms(roomIds);
if (conditionerIds.isEmpty()) {
- List<YwConditioner> byRoom = ywConditionerMapper.selectList(new QueryWrapper<YwConditioner>().lambda()
- .in(YwConditioner::getRoomId, roomIds)
- .eq(YwConditioner::getIsdeleted, Constants.ZERO));
- byRoom.stream().map(YwConditioner::getId).forEach(conditionerIds::add);
- }
- if (conditionerIds.isEmpty()) {
+ softDeleteAllConditionerRels(customerId, user);
return;
}
YwCustomerGsConfigDTO dto = new YwCustomerGsConfigDTO();
- dto.setCustomerId(contract.getRenterId());
+ dto.setCustomerId(customerId);
dto.setIsPwr(Constants.ONE);
dto.setIsRestStop(Constants.ZERO);
dto.setGsBz("鍚堝悓鑷姩鍏宠仈");
@@ -196,12 +368,33 @@
}
dto.setConditioners(items);
try {
- ywCustomerRechargeBizService.saveCustomerGsConfig(dto, user != null ? user : systemUser());
+ ywCustomerRechargeBizService.saveCustomerGsConfig(dto, user);
} catch (BusinessException e) {
- log.warn("auto bind conditioner GS failed contractId={}: {}", contract.getId(), e.getMessage());
+ log.warn("auto bind conditioner GS failed customerId={}: {}", customerId, e.getMessage());
}
}
+ private void clearContractElectricalBindings(Integer customerId, LoginUserInfo user) {
+ Date now = new Date();
+ ywCustomerElectricalMapper.update(null, new UpdateWrapper<YwCustomerElectrical>().lambda()
+ .set(YwCustomerElectrical::getIsdeleted, Constants.ONE)
+ .set(YwCustomerElectrical::getEditDate, now)
+ .set(YwCustomerElectrical::getEditor, user != null ? user.getId() : null)
+ .eq(YwCustomerElectrical::getCustomerId, customerId)
+ .eq(YwCustomerElectrical::getBindSource, BIND_SOURCE_CONTRACT)
+ .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO));
+ }
+
+ private void softDeleteAllConditionerRels(Integer customerId, LoginUserInfo user) {
+ Date now = new Date();
+ ywCustomerConditionerMapper.update(null, new UpdateWrapper<YwCustomerConditioner>().lambda()
+ .set(YwCustomerConditioner::getIsdeleted, Constants.ONE)
+ .set(YwCustomerConditioner::getEditDate, now)
+ .set(YwCustomerConditioner::getEditor, user != null ? user.getId() : null)
+ .eq(YwCustomerConditioner::getCustomerId, customerId)
+ .eq(YwCustomerConditioner::getIsdeleted, Constants.ZERO));
+ }
+
private Set<Integer> listBoundElectricalIdsExcept(Integer customerId) {
List<YwCustomerElectrical> list = ywCustomerElectricalMapper.selectList(new QueryWrapper<YwCustomerElectrical>().lambda()
.eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO)
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5AuthServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5AuthServiceImpl.java
index 59d02f7..3d0b5fd 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5AuthServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5AuthServiceImpl.java
@@ -12,6 +12,7 @@
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.YwCustomer;
import com.doumee.dao.system.dto.LoginPhoneDTO;
+import com.doumee.service.business.SmsEmailService;
import com.doumee.service.business.YwCustomerH5AuthService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -30,22 +31,37 @@
private MemberMapper memberMapper;
@Autowired
private JwtTokenUtil jwtTokenUtil;
+ @Autowired
+ private SmsEmailService smsEmailService;
@Override
@Transactional(rollbackFor = Exception.class)
public String loginByPhone(LoginPhoneDTO dto) {
- YwCustomer customer = findActiveByPhone(dto.getPhone());
- bindOpenId(customer, dto.getOpenid());
- touchLogin(customer);
- return jwtTokenUtil.generateToken(toLoginUserInfo(customer));
+ smsEmailService.validateCode(dto.getCode(), dto.getPhone());
+ CustomerMemberContext ctx = resolveByPhone(dto.getPhone());
+ if (ctx == null || ctx.customer == null) {
+ throw new BusinessException(ResponseStatus.ACCOUNT_INCORRECT.getCode(), "鍟嗘埛涓嶅瓨鍦ㄦ垨鏈敞鍐�");
+ }
+ assertCustomerEnabled(ctx.customer);
+ if (ctx.member != null) {
+ assertMemberEnabled(ctx.member);
+ bindMemberOpenId(ctx.member, dto.getOpenid());
+ touchMemberLogin(ctx.member);
+ }
+ touchLogin(ctx.customer);
+ return jwtTokenUtil.generateToken(toLoginUserInfo(ctx.customer, ctx.member));
}
@Override
@Transactional(rollbackFor = Exception.class)
public String loginByCustomerId(Integer customerId) {
YwCustomer customer = requireActiveCustomer(customerId);
+ Member member = resolveDefaultMember(customer);
touchLogin(customer);
- return jwtTokenUtil.generateToken(toLoginUserInfo(customer));
+ if (member != null) {
+ touchMemberLogin(member);
+ }
+ return jwtTokenUtil.generateToken(toLoginUserInfo(customer, member));
}
@Override
@@ -54,58 +70,97 @@
if (StringUtils.isBlank(openId)) {
return null;
}
+ String trimmed = openId.trim();
+ Member member = findActiveCustomerMemberByOpenId(trimmed);
+ if (member != null) {
+ YwCustomer customer = loadCustomerByMember(member);
+ if (customer != null) {
+ assertCustomerEnabled(customer);
+ assertMemberEnabled(member);
+ touchMemberLogin(member);
+ touchLogin(customer);
+ return jwtTokenUtil.generateToken(toLoginUserInfo(customer, member));
+ }
+ }
YwCustomer customer = ywCustomerMapper.selectOne(new QueryWrapper<YwCustomer>().lambda()
.eq(YwCustomer::getIsdeleted, Constants.ZERO)
- .eq(YwCustomer::getOpenid, openId.trim())
+ .eq(YwCustomer::getOpenid, trimmed)
.last(" limit 1 "));
if (customer == null) {
return null;
}
assertCustomerEnabled(customer);
+ Member defaultMember = resolveDefaultMember(customer);
+ if (defaultMember != null && StringUtils.isBlank(defaultMember.getOpenid())) {
+ bindMemberOpenId(defaultMember, trimmed);
+ touchMemberLogin(defaultMember);
+ }
touchLogin(customer);
- return jwtTokenUtil.generateToken(toLoginUserInfo(customer));
+ return jwtTokenUtil.generateToken(toLoginUserInfo(customer, defaultMember));
}
@Override
- public LoginUserInfo buildLoginUserInfo(Integer customerId) {
- return toLoginUserInfo(requireActiveCustomer(customerId));
+ public LoginUserInfo buildLoginUserInfo(Integer customerId, Integer memberId) {
+ YwCustomer customer = requireActiveCustomer(customerId);
+ Member member = resolveMemberForCustomer(customer, memberId);
+ return toLoginUserInfo(customer, member);
}
@Override
public void assertActiveCustomerByPhone(String phone) {
- findActiveByPhone(phone);
+ CustomerMemberContext ctx = resolveByPhone(phone);
+ if (ctx == null || ctx.customer == null) {
+ throw new BusinessException(ResponseStatus.ACCOUNT_INCORRECT.getCode(), "鍟嗘埛涓嶅瓨鍦ㄦ垨鏈敞鍐�");
+ }
+ assertCustomerEnabled(ctx.customer);
+ if (ctx.member != null) {
+ assertMemberEnabled(ctx.member);
+ }
}
- private YwCustomer findActiveByPhone(String phone) {
+ private CustomerMemberContext resolveByPhone(String phone) {
if (StringUtils.isBlank(phone)) {
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鎵嬫満鍙蜂笉鑳戒负绌�");
}
- YwCustomer customer = findCustomerByPhone(phone.trim());
- if (customer == null) {
- throw new BusinessException(ResponseStatus.ACCOUNT_INCORRECT.getCode(), "鍟嗘埛涓嶅瓨鍦ㄦ垨鏈敞鍐�");
+ String trimmed = phone.trim();
+ Member member = findActiveCustomerMemberByPhone(trimmed);
+ if (member != null) {
+ YwCustomer customer = loadCustomerByMember(member);
+ if (customer != null) {
+ return new CustomerMemberContext(customer, member);
+ }
}
- assertCustomerEnabled(customer);
- return customer;
- }
-
- /**
- * 鍟嗘埛鎵嬫満鍙凤細浼樺厛 yw_customer.phone锛屽惁鍒欏尮閰嶈仈绯讳汉 member.phone
- */
- private YwCustomer findCustomerByPhone(String phone) {
YwCustomer byCustomerPhone = ywCustomerMapper.selectOne(new QueryWrapper<YwCustomer>().lambda()
.eq(YwCustomer::getIsdeleted, Constants.ZERO)
- .eq(YwCustomer::getPhone, phone)
+ .eq(YwCustomer::getPhone, trimmed)
.last(" limit 1 "));
if (byCustomerPhone != null) {
- return byCustomerPhone;
+ return new CustomerMemberContext(byCustomerPhone, resolveDefaultMember(byCustomerPhone));
}
- Member member = memberMapper.selectOne(new QueryWrapper<Member>().lambda()
+ return null;
+ }
+
+ private Member findActiveCustomerMemberByPhone(String phone) {
+ return memberMapper.selectOne(new QueryWrapper<Member>().lambda()
.eq(Member::getIsdeleted, Constants.ZERO)
.eq(Member::getType, Constants.memberType.customer)
.eq(Member::getPhone, phone)
.isNotNull(Member::getCustomerId)
.orderByDesc(Member::getId)
.last(" limit 1 "));
+ }
+
+ private Member findActiveCustomerMemberByOpenId(String openId) {
+ return memberMapper.selectOne(new QueryWrapper<Member>().lambda()
+ .eq(Member::getIsdeleted, Constants.ZERO)
+ .eq(Member::getType, Constants.memberType.customer)
+ .eq(Member::getOpenid, openId)
+ .isNotNull(Member::getCustomerId)
+ .orderByDesc(Member::getId)
+ .last(" limit 1 "));
+ }
+
+ private YwCustomer loadCustomerByMember(Member member) {
if (member == null || member.getCustomerId() == null) {
return null;
}
@@ -120,6 +175,48 @@
.eq(YwCustomer::getIsdeleted, Constants.ZERO)
.eq(YwCustomer::getMemberId, member.getId())
.last(" limit 1 "));
+ }
+
+ private Member resolveDefaultMember(YwCustomer customer) {
+ if (customer.getMemberId() != null) {
+ Member member = memberMapper.selectById(customer.getMemberId());
+ if (isActiveCustomerMember(member)) {
+ return member;
+ }
+ }
+ return memberMapper.selectOne(new QueryWrapper<Member>().lambda()
+ .eq(Member::getIsdeleted, Constants.ZERO)
+ .eq(Member::getType, Constants.memberType.customer)
+ .eq(Member::getCustomerId, customer.getId())
+ .orderByAsc(Member::getId)
+ .last(" limit 1 "));
+ }
+
+ private Member resolveMemberForCustomer(YwCustomer customer, Integer memberId) {
+ if (memberId != null) {
+ Member member = memberMapper.selectById(memberId);
+ if (member != null && isMemberBelongsToCustomer(member, customer)) {
+ assertMemberEnabled(member);
+ return member;
+ }
+ }
+ return resolveDefaultMember(customer);
+ }
+
+ private boolean isMemberBelongsToCustomer(Member member, YwCustomer customer) {
+ if (!isActiveCustomerMember(member)) {
+ return false;
+ }
+ if (member.getCustomerId() != null && Constants.equalsInteger(member.getCustomerId(), customer.getId())) {
+ return true;
+ }
+ return customer.getMemberId() != null && Constants.equalsInteger(customer.getMemberId(), member.getId());
+ }
+
+ private boolean isActiveCustomerMember(Member member) {
+ return member != null
+ && Constants.equalsInteger(member.getIsdeleted(), Constants.ZERO)
+ && Constants.equalsInteger(member.getType(), Constants.memberType.customer);
}
private YwCustomer requireActiveCustomer(Integer customerId) {
@@ -140,15 +237,32 @@
}
}
- private void bindOpenId(YwCustomer customer, String openid) {
- if (StringUtils.isBlank(openid)) {
+ private void assertMemberEnabled(Member member) {
+ if (member.getStatus() != null && Constants.equalsInteger(member.getStatus(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.NO_ALLOW_LOGIN.getCode(), "浜哄憳璐﹀彿宸茬鐢�");
+ }
+ }
+
+ private void bindMemberOpenId(Member member, String openid) {
+ if (member == null || StringUtils.isBlank(openid)) {
return;
}
- ywCustomerMapper.update(null, new UpdateWrapper<YwCustomer>().lambda()
- .set(YwCustomer::getOpenid, null)
- .eq(YwCustomer::getOpenid, openid.trim())
- .ne(YwCustomer::getId, customer.getId()));
- customer.setOpenid(openid.trim());
+ String trimmed = openid.trim();
+ memberMapper.update(null, new UpdateWrapper<Member>().lambda()
+ .set(Member::getOpenid, null)
+ .eq(Member::getOpenid, trimmed)
+ .ne(Member::getId, member.getId()));
+ member.setOpenid(trimmed);
+ member.setEditDate(new Date());
+ memberMapper.updateById(member);
+ }
+
+ private void touchMemberLogin(Member member) {
+ if (member == null) {
+ return;
+ }
+ member.setEditDate(new Date());
+ memberMapper.updateById(member);
}
private void touchLogin(YwCustomer customer) {
@@ -159,18 +273,43 @@
ywCustomerMapper.updateById(customer);
}
- private LoginUserInfo toLoginUserInfo(YwCustomer customer) {
+ private LoginUserInfo toLoginUserInfo(YwCustomer customer, Member member) {
LoginUserInfo loginUserInfo = new LoginUserInfo();
loginUserInfo.setCustomerId(customer.getId());
loginUserInfo.setId(customer.getId());
loginUserInfo.setH5UserType(LoginUserInfo.H5_USER_CUSTOMER);
- loginUserInfo.setRealname(customer.getName());
- loginUserInfo.setMobile(resolveLoginMobile(customer));
- loginUserInfo.setUsername("customer_" + customer.getId());
+ loginUserInfo.setCustomerName(customer.getName());
+ if (member != null) {
+ loginUserInfo.setMemberId(member.getId());
+ loginUserInfo.setMemberName(member.getName());
+ loginUserInfo.setMobile(member.getPhone());
+ } else {
+ loginUserInfo.setMobile(resolveLoginMobile(customer));
+ }
+ String displayName = buildDisplayName(customer.getName(), member != null ? member.getName() : null);
+ loginUserInfo.setDisplayName(displayName);
+ loginUserInfo.setRealname(displayName);
+ loginUserInfo.setUsername("customer_" + customer.getId()
+ + "_member_" + (member != null ? member.getId() : 0));
loginUserInfo.setSource(LoginUserInfo.SOURCE_H5_CUSTOMER);
loginUserInfo.setRoles(Collections.singletonList("h5_customer"));
loginUserInfo.setPermissions(Collections.emptyList());
return loginUserInfo;
+ }
+
+ private String buildDisplayName(String customerName, String memberName) {
+ String customer = StringUtils.trimToEmpty(customerName);
+ String member = StringUtils.trimToEmpty(memberName);
+ if (StringUtils.isBlank(customer) && StringUtils.isBlank(member)) {
+ return "";
+ }
+ if (StringUtils.isBlank(member)) {
+ return customer;
+ }
+ if (StringUtils.isBlank(customer)) {
+ return member;
+ }
+ return customer + "-" + member;
}
private String resolveLoginMobile(YwCustomer customer) {
@@ -185,4 +324,45 @@
}
return customer.getPhone();
}
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void logout(LoginUserInfo user, String token) {
+ if (user == null) {
+ jwtTokenUtil.invalidateToken(token);
+ return;
+ }
+ String memberOpenId = null;
+ if (user.getMemberId() != null) {
+ Member member = memberMapper.selectById(user.getMemberId());
+ if (member != null) {
+ memberOpenId = member.getOpenid();
+ if (StringUtils.isNotBlank(memberOpenId)) {
+ memberMapper.update(null, new UpdateWrapper<Member>().lambda()
+ .set(Member::getOpenid, null)
+ .eq(Member::getId, member.getId()));
+ }
+ }
+ }
+ if (user.getCustomerId() != null) {
+ YwCustomer customer = ywCustomerMapper.selectById(user.getCustomerId());
+ if (customer != null && StringUtils.isNotBlank(customer.getOpenid())
+ && (memberOpenId == null || StringUtils.equals(customer.getOpenid(), memberOpenId))) {
+ ywCustomerMapper.update(null, new UpdateWrapper<YwCustomer>().lambda()
+ .set(YwCustomer::getOpenid, null)
+ .eq(YwCustomer::getId, customer.getId()));
+ }
+ }
+ jwtTokenUtil.invalidateToken(token);
+ }
+
+ private static class CustomerMemberContext {
+ private final YwCustomer customer;
+ private final Member member;
+
+ private CustomerMemberContext(YwCustomer customer, Member member) {
+ this.customer = customer;
+ this.member = member;
+ }
+ }
}
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5BizServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5BizServiceImpl.java
index 5e55186..d9494ab 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5BizServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerH5BizServiceImpl.java
@@ -29,6 +29,8 @@
import com.doumee.dao.business.model.*;
import com.doumee.dao.system.MultifileMapper;
import com.doumee.dao.system.model.Multifile;
+import com.doumee.service.business.YwCustomerDeviceAutoBindService;
+import com.doumee.service.business.YwCustomerH5AuthService;
import com.doumee.service.business.YwCustomerH5BizService;
import com.doumee.service.business.YwCustomerRechargeBizService;
import com.doumee.service.business.YwElectricalBizService;
@@ -57,6 +59,8 @@
@Autowired
private YwCustomerRechargeBizService ywCustomerRechargeBizService;
@Autowired
+ private YwCustomerDeviceAutoBindService ywCustomerDeviceAutoBindService;
+ @Autowired
private YwCustomerElectricalMapper ywCustomerElectricalMapper;
@Autowired
private YwElectricalMapper ywElectricalMapper;
@@ -78,6 +82,8 @@
private MultifileMapper multifileMapper;
@Autowired
private SystemDictDataBiz systemDictDataBiz;
+ @Autowired
+ private YwCustomerH5AuthService ywCustomerH5AuthService;
@Override
public List<YwH5Banner> listBanners() {
@@ -85,11 +91,14 @@
}
@Override
- public Map<String, Object> home(Integer customerId) {
+ public Map<String, Object> home(Integer customerId, Integer memberId) {
YwCustomer customer = requireCustomer(customerId);
+ LoginUserInfo loginUser = ywCustomerH5AuthService.buildLoginUserInfo(customerId, memberId);
YwCustomerRechargeDetailVO detail = ywCustomerRechargeBizService.getDetail(customerId);
Map<String, Object> map = new LinkedHashMap<>();
- map.put("customerName", customer.getName());
+ map.put("customerName", loginUser.getCustomerName() != null ? loginUser.getCustomerName() : customer.getName());
+ map.put("memberName", loginUser.getMemberName());
+ map.put("displayName", loginUser.getDisplayName());
map.put("electricalCount", detail.getElectricalList() != null ? detail.getElectricalList().size() : 0);
map.put("conditionerCount", detail.getConditionerList() != null ? detail.getConditionerList().size() : 0);
map.put("gsConfig", detail.getGsConfig());
@@ -100,6 +109,7 @@
@Override
public PageData<CustomerDeviceH5VO> devicePage(PageWrap<CustomerDeviceQueryDTO> pageWrap, Integer customerId) {
requireCustomer(customerId);
+ ywCustomerDeviceAutoBindService.refreshCustomerDevices(customerId, systemUser());
CustomerDeviceQueryDTO q = pageWrap.getModel() != null ? pageWrap.getModel() : new CustomerDeviceQueryDTO();
List<CustomerDeviceH5VO> all = new ArrayList<>();
if (q.getDeviceType() == null || q.getDeviceType() == 0) {
@@ -254,10 +264,8 @@
if (Objects.equals(customer.getFirstRechargeDone(), Constants.ONE)) {
return;
}
- List<Integer> electricalIds = ywCustomerElectricalMapper.selectList(new QueryWrapper<YwCustomerElectrical>().lambda()
- .eq(YwCustomerElectrical::getCustomerId, customerId)
- .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO))
- .stream().map(YwCustomerElectrical::getElectricalId).collect(Collectors.toList());
+ ywCustomerDeviceAutoBindService.refreshCustomerDevices(customerId, user != null ? user : systemUser());
+ List<Integer> electricalIds = ywCustomerDeviceAutoBindService.listElectricalIdsByActiveContracts(customerId);
for (Integer eid : electricalIds) {
YwCustomerRechargeElectricalDTO dto = new YwCustomerRechargeElectricalDTO();
dto.setCustomerId(customerId);
@@ -280,10 +288,7 @@
}
private List<CustomerDeviceH5VO> buildElectricalDevices(Integer customerId) {
- List<Integer> ids = ywCustomerElectricalMapper.selectList(new QueryWrapper<YwCustomerElectrical>().lambda()
- .eq(YwCustomerElectrical::getCustomerId, customerId)
- .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO))
- .stream().map(YwCustomerElectrical::getElectricalId).collect(Collectors.toList());
+ List<Integer> ids = ywCustomerDeviceAutoBindService.listElectricalIdsByActiveContracts(customerId);
if (ids.isEmpty()) {
return Collections.emptyList();
}
@@ -297,6 +302,7 @@
vo.setDeviceId(e.getId());
vo.setDeviceName(e.getName());
vo.setMeterAccountNo(e.getParamId());
+ vo.setMeterAddress(e.getAddress());
vo.setRoomInfo(e.getRoomNames());
vo.setBalance(e.getBalance());
vo.setBalanceLow(e.getBalance() != null && e.getBalance().compareTo(new BigDecimal("50")) < 0);
@@ -345,6 +351,13 @@
return true;
}
+ private LoginUserInfo systemUser() {
+ LoginUserInfo user = new LoginUserInfo();
+ user.setId(1);
+ user.setRealname("system");
+ return user;
+ }
+
private YwCustomer requireCustomer(Integer customerId) {
YwCustomer c = ywCustomerMapper.selectById(customerId);
if (c == null || Objects.equals(c.getIsdeleted(), Constants.ONE)) {
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerRechargeBizServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerRechargeBizServiceImpl.java
index c1136e4..2e5321f 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerRechargeBizServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerRechargeBizServiceImpl.java
@@ -24,6 +24,7 @@
import com.doumee.dao.business.dto.*;
import com.doumee.dao.business.model.*;
import com.doumee.service.business.ConditionerBizService;
+import com.doumee.service.business.YwCustomerDeviceAutoBindService;
import com.doumee.service.business.YwCustomerRechargeBizService;
import com.doumee.service.business.YwElectricalBizService;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
@@ -57,6 +58,10 @@
@Autowired
private YwElectricalMapper ywElectricalMapper;
@Autowired
+ private YwElectricalRoomMapper ywElectricalRoomMapper;
+ @Autowired
+ private YwRoomMapper ywRoomMapper;
+ @Autowired
private YwConditionerMapper ywConditionerMapper;
@Autowired
private YwElectricalChargeMapper ywElectricalChargeMapper;
@@ -66,6 +71,10 @@
private YwElectricalBizService ywElectricalBizService;
@Autowired
private ConditionerBizService conditionerBizService;
+ @Autowired
+ private YwCustomerDeviceAutoBindService ywCustomerDeviceAutoBindService;
+ @Autowired
+ private MemberMapper memberMapper;
@Override
public PageData<YwCustomerRechargeMerchantVO> findMerchantPage(PageWrap<YwCustomerRechargeQueryDTO> pageWrap) {
@@ -156,20 +165,17 @@
Map<Integer, YwCustomerGs> gsMap = loadGsMap(customerIds);
- List<YwCustomerElectrical> relE = loadCustomerElectricalRels(customerIds);
- Map<Integer, List<Integer>> customerElectricalIds = relE.stream()
- .collect(Collectors.groupingBy(YwCustomerElectrical::getCustomerId,
- Collectors.mapping(YwCustomerElectrical::getElectricalId, Collectors.toList())));
+ Map<Integer, List<Integer>> customerElectricalIds = ywCustomerDeviceAutoBindService
+ .batchListElectricalIdsByActiveContracts(customerIds);
+ Map<Integer, List<Integer>> customerConditionerIds = ywCustomerDeviceAutoBindService
+ .batchListConditionerIdsByActiveContracts(customerIds);
- List<YwCustomerConditioner> relC = loadCustomerConditionerRels(customerIds);
- Map<Integer, List<Integer>> customerConditionerIds = relC.stream()
- .collect(Collectors.groupingBy(YwCustomerConditioner::getCustomerId,
- Collectors.mapping(YwCustomerConditioner::getConditionerId, Collectors.toList())));
-
- Set<Integer> allElectricalIds = relE.stream().map(YwCustomerElectrical::getElectricalId).collect(Collectors.toSet());
+ Set<Integer> allElectricalIds = customerElectricalIds.values().stream()
+ .flatMap(List::stream).collect(Collectors.toSet());
Map<Integer, YwElectrical> electricalMap = loadElectricalMap(allElectricalIds);
- Set<Integer> allConditionerIds = relC.stream().map(YwCustomerConditioner::getConditionerId).collect(Collectors.toSet());
+ Set<Integer> allConditionerIds = customerConditionerIds.values().stream()
+ .flatMap(List::stream).collect(Collectors.toSet());
Map<Integer, YwConditioner> conditionerMap = loadConditionerMap(allConditionerIds);
List<YwCustomerRechargeMerchantVO> list = new ArrayList<>();
@@ -260,36 +266,6 @@
}
}
- private List<YwCustomerElectrical> loadCustomerElectricalRels(List<Integer> customerIds) {
- if (CollectionUtils.isEmpty(customerIds)) {
- return Collections.emptyList();
- }
- try {
- return ywCustomerElectricalMapper.selectList(new QueryWrapper<YwCustomerElectrical>().lambda()
- .select(YwCustomerElectrical::getCustomerId, YwCustomerElectrical::getElectricalId)
- .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO)
- .in(YwCustomerElectrical::getCustomerId, customerIds));
- } catch (Exception e) {
- log.warn("load yw_customer_electrical failed, skip electrical stats: {}", e.getMessage());
- return Collections.emptyList();
- }
- }
-
- private List<YwCustomerConditioner> loadCustomerConditionerRels(List<Integer> customerIds) {
- if (CollectionUtils.isEmpty(customerIds)) {
- return Collections.emptyList();
- }
- try {
- return ywCustomerConditionerMapper.selectList(new QueryWrapper<YwCustomerConditioner>().lambda()
- .select(YwCustomerConditioner::getCustomerId, YwCustomerConditioner::getConditionerId)
- .eq(YwCustomerConditioner::getIsdeleted, Constants.ZERO)
- .in(YwCustomerConditioner::getCustomerId, customerIds));
- } catch (Exception e) {
- log.warn("load yw_customer_conditioner failed, skip conditioner stats: {}", e.getMessage());
- return Collections.emptyList();
- }
- }
-
private Map<Integer, YwElectrical> loadElectricalMap(Set<Integer> electricalIds) {
if (CollectionUtils.isEmpty(electricalIds)) {
return Collections.emptyMap();
@@ -326,6 +302,7 @@
@Override
public YwCustomerRechargeDetailVO getDetail(Integer customerId) {
YwCustomer customer = requireCustomer(customerId);
+ ywCustomerDeviceAutoBindService.refreshCustomerDevices(customerId, systemUser());
YwCustomerRechargeDetailVO vo = new YwCustomerRechargeDetailVO();
vo.setCustomerId(customer.getId());
vo.setCustomerName(customer.getName());
@@ -339,6 +316,7 @@
@Override
public PageData<YwElectrical> listCustomerElectrical(PageWrap<YwElectrical> pageWrap, Integer customerId) {
requireCustomer(customerId);
+ ywCustomerDeviceAutoBindService.refreshCustomerDevices(customerId, systemUser());
List<Integer> ids = listBoundElectricalIds(customerId);
if (ids.isEmpty()) {
return emptyPage(pageWrap);
@@ -412,6 +390,7 @@
@Override
public PageData<YwConditioner> listCustomerConditioner(PageWrap<YwConditioner> pageWrap, Integer customerId) {
requireCustomer(customerId);
+ ywCustomerDeviceAutoBindService.refreshCustomerDevices(customerId, systemUser());
List<YwConditioner> list = loadCustomerConditionerList(customerId);
if (CollectionUtils.isEmpty(list)) {
return emptyPage(pageWrap);
@@ -898,7 +877,38 @@
charge.setStatusTime(new Date());
charge.setStatusInfo("鍏呭�间腑");
charge.setDeviceInfo("GS-" + gs.getPlatformGsId() + " " + customer.getName());
+ applyRechargeOperator(charge, user);
return charge;
+ }
+
+ private void applyRechargeOperator(YwElectricalCharge charge, LoginUserInfo user) {
+ if (charge == null || user == null) {
+ return;
+ }
+ if (user.getMemberId() != null) {
+ charge.setMemberId(user.getMemberId());
+ }
+ String operatorName = resolveRechargeUserName(user);
+ if (StringUtils.isNotBlank(operatorName)) {
+ charge.setRechargeUserName(operatorName);
+ }
+ }
+
+ private String resolveRechargeUserName(LoginUserInfo user) {
+ if (user == null) {
+ return null;
+ }
+ if (StringUtils.isNotBlank(user.getMemberName())) {
+ return user.getMemberName();
+ }
+ if (Constants.equalsInteger(user.getH5UserType(), LoginUserInfo.H5_USER_CUSTOMER)
+ && StringUtils.isNotBlank(user.getDisplayName())) {
+ int idx = user.getDisplayName().lastIndexOf('-');
+ if (idx >= 0 && idx < user.getDisplayName().length() - 1) {
+ return user.getDisplayName().substring(idx + 1).trim();
+ }
+ }
+ return StringUtils.trimToNull(user.getRealname());
}
private void fillRecordText(YwCustomerRechargeRecordVO vo) {
@@ -907,7 +917,13 @@
vo.setDeviceInfo(StringUtils.defaultString(vo.getAddress()) + " " + StringUtils.defaultString(vo.getName()));
}
}
- vo.setTypeText(Objects.equals(vo.getType(), Constants.ONE) ? "绌鸿皟" : "鐢佃〃");
+ vo.setTypeText(Objects.equals(vo.getType(), Constants.ONE) ? "绌鸿皟鍏呭��" : "鐢佃〃鍏呭��");
+ if (StringUtils.isBlank(vo.getRechargeUserName()) && vo.getMemberId() != null) {
+ Member member = memberMapper.selectById(vo.getMemberId());
+ if (member != null && StringUtils.isNotBlank(member.getName())) {
+ vo.setRechargeUserName(member.getName());
+ }
+ }
if (Objects.equals(vo.getStatus(), Constants.ZERO)) {
vo.setStatusText("鍏呭�间腑");
} else if (Objects.equals(vo.getStatus(), Constants.ONE)) {
@@ -1013,20 +1029,14 @@
}
private void assertElectricalBound(Integer customerId, Integer electricalId) {
- Long cnt = ywCustomerElectricalMapper.selectCount(new QueryWrapper<YwCustomerElectrical>().lambda()
- .eq(YwCustomerElectrical::getCustomerId, customerId)
- .eq(YwCustomerElectrical::getElectricalId, electricalId)
- .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO));
- if (cnt == null || cnt == 0) {
- throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢佃〃鏈叧鑱旇鍟嗘埛");
+ List<Integer> boundIds = ywCustomerDeviceAutoBindService.listElectricalIdsByActiveContracts(customerId);
+ if (!boundIds.contains(electricalId)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢佃〃鏈叧鑱旇鍟嗘埛鏈夋晥鍚堝悓鎴挎簮");
}
}
private List<Integer> listBoundElectricalIds(Integer customerId) {
- return ywCustomerElectricalMapper.selectList(new QueryWrapper<YwCustomerElectrical>().lambda()
- .eq(YwCustomerElectrical::getCustomerId, customerId)
- .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO))
- .stream().map(YwCustomerElectrical::getElectricalId).collect(Collectors.toList());
+ return ywCustomerDeviceAutoBindService.listElectricalIdsByActiveContracts(customerId);
}
private Set<Integer> listAllBoundElectricalIds() {
@@ -1054,22 +1064,73 @@
}
private List<YwConditioner> loadCustomerConditionerList(Integer customerId) {
- List<YwCustomerConditioner> rels = ywCustomerConditionerMapper.selectList(new QueryWrapper<YwCustomerConditioner>().lambda()
- .eq(YwCustomerConditioner::getCustomerId, customerId)
- .eq(YwCustomerConditioner::getIsdeleted, Constants.ZERO));
- if (rels.isEmpty()) {
+ List<Integer> conditionerIds = ywCustomerDeviceAutoBindService.listConditionerIdsByActiveContracts(customerId);
+ if (conditionerIds.isEmpty()) {
return Collections.emptyList();
}
- Map<Integer, Integer> ratioMap = rels.stream()
+ Map<Integer, Integer> ratioMap = ywCustomerConditionerMapper.selectList(new QueryWrapper<YwCustomerConditioner>().lambda()
+ .eq(YwCustomerConditioner::getCustomerId, customerId)
+ .eq(YwCustomerConditioner::getIsdeleted, Constants.ZERO))
+ .stream()
.collect(Collectors.toMap(YwCustomerConditioner::getConditionerId, YwCustomerConditioner::getDevRatio, (a, b) -> a));
- List<YwConditioner> list = ywConditionerMapper.selectBatchIds(ratioMap.keySet());
+ List<YwConditioner> list = ywConditionerMapper.selectBatchIds(conditionerIds);
list = list.stream().filter(c -> !Objects.equals(c.getIsdeleted(), Constants.ONE)).collect(Collectors.toList());
+ enrichConditionerRoomNames(list, customerId);
for (YwConditioner c : list) {
- c.setDevRatio(ratioMap.get(c.getId()));
+ c.setDevRatio(ratioMap.getOrDefault(c.getId(), 100));
}
return list;
}
+ private void enrichConditionerRoomNames(List<YwConditioner> list, Integer customerId) {
+ if (CollectionUtils.isEmpty(list)) {
+ return;
+ }
+ List<Integer> contractRoomIds = ywCustomerDeviceAutoBindService.listActiveContractRoomIds(customerId);
+ Map<Integer, Integer> conditionerRoomMap = new HashMap<>();
+ if (!CollectionUtils.isEmpty(contractRoomIds)) {
+ ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
+ .in(YwElectricalRoom::getRoomId, contractRoomIds)
+ .eq(YwElectricalRoom::getType, Constants.ONE)
+ .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO))
+ .forEach(rel -> {
+ if (rel.getObjId() != null && rel.getRoomId() != null) {
+ conditionerRoomMap.putIfAbsent(rel.getObjId(), rel.getRoomId());
+ }
+ });
+ }
+ Set<Integer> roomIds = new HashSet<>(contractRoomIds);
+ for (YwConditioner conditioner : list) {
+ Integer roomId = conditionerRoomMap.getOrDefault(conditioner.getId(), conditioner.getRoomId());
+ if (roomId != null) {
+ roomIds.add(roomId);
+ }
+ }
+ if (roomIds.isEmpty()) {
+ return;
+ }
+ Map<Integer, YwRoom> roomMap = ywRoomMapper.selectBatchIds(roomIds).stream()
+ .filter(r -> !Objects.equals(r.getIsdeleted(), Constants.ONE))
+ .collect(Collectors.toMap(YwRoom::getId, r -> r, (a, b) -> a));
+ for (YwConditioner conditioner : list) {
+ if (StringUtils.isNotBlank(conditioner.getRoomName())) {
+ continue;
+ }
+ Integer roomId = conditionerRoomMap.getOrDefault(conditioner.getId(), conditioner.getRoomId());
+ YwRoom room = roomId != null ? roomMap.get(roomId) : null;
+ if (room != null) {
+ conditioner.setRoomName(StringUtils.defaultString(room.getRoomNum()));
+ }
+ }
+ }
+
+ private LoginUserInfo systemUser() {
+ LoginUserInfo user = new LoginUserInfo();
+ user.setId(1);
+ user.setRealname("system");
+ return user;
+ }
+
private PageData<YwElectrical> pageElectricalByIds(PageWrap<YwElectrical> pageWrap, List<Integer> ids) {
IPage<YwElectrical> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
YwElectrical model = pageWrap.getModel();
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerWxPayServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerWxPayServiceImpl.java
index 35e1b09..5b94c6c 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerWxPayServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerWxPayServiceImpl.java
@@ -15,6 +15,7 @@
import com.doumee.dao.business.model.*;
import com.doumee.service.business.YwContractRevenueService;
import com.doumee.service.business.YwCustomerH5BizService;
+import com.doumee.service.business.YwCustomerH5AuthService;
import com.doumee.service.business.YwCustomerRechargeBizService;
import com.doumee.service.business.YwCustomerWxPayService;
import lombok.extern.slf4j.Slf4j;
@@ -48,6 +49,8 @@
private YwContractRevenueService ywContractRevenueService;
@Autowired
private YwCustomerH5BizService ywCustomerH5BizService;
+ @Autowired
+ private YwCustomerH5AuthService ywCustomerH5AuthService;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -74,6 +77,7 @@
order.setIsdeleted(Constants.ZERO);
order.setOrderNo(orderNo);
order.setCustomerId(user.getCustomerId());
+ order.setMemberId(user.getMemberId());
order.setOrderType(orderType);
order.setBizRefId(bizRefId);
order.setAmount(dto.getAmount());
@@ -120,7 +124,7 @@
if (Objects.equals(order.getStatus(), YwWxPayOrder.STATUS_SUCCESS)) {
return successXml();
}
- LoginUserInfo user = buildCustomerUser(order.getCustomerId());
+ LoginUserInfo user = buildCustomerUser(order);
try {
Integer bizRecordId = fulfillBiz(order, user);
order.setStatus(YwWxPayOrder.STATUS_SUCCESS);
@@ -238,13 +242,11 @@
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟绫诲瀷鏃犳晥");
}
- private LoginUserInfo buildCustomerUser(Integer customerId) {
- LoginUserInfo u = new LoginUserInfo();
- u.setId(customerId);
- u.setCustomerId(customerId);
- u.setH5UserType(LoginUserInfo.H5_USER_CUSTOMER);
- u.setRealname("鍟嗘埛H5");
- return u;
+ private LoginUserInfo buildCustomerUser(YwWxPayOrder order) {
+ if (order == null || order.getCustomerId() == null) {
+ return null;
+ }
+ return ywCustomerH5AuthService.buildLoginUserInfo(order.getCustomerId(), order.getMemberId());
}
private String successXml() {
diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwElectricalBizServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwElectricalBizServiceImpl.java
index 2a76435..e39073e 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwElectricalBizServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwElectricalBizServiceImpl.java
@@ -625,6 +625,7 @@
charge.setStatusInfo("鍏呭�间腑");
charge.setBanlance(e.getBalance());
charge.setRoomNames(e.getRoomNames());
+ applyRechargeOperator(charge, user);
if (dto.getCustomerId() != null) {
charge.setCustomerId(dto.getCustomerId());
}
@@ -1401,4 +1402,34 @@
private String newOprId() {
return UUID.randomUUID().toString().replace("-", "");
}
+
+ private void applyRechargeOperator(YwElectricalCharge charge, LoginUserInfo user) {
+ if (charge == null || user == null) {
+ return;
+ }
+ if (user.getMemberId() != null) {
+ charge.setMemberId(user.getMemberId());
+ }
+ String operatorName = resolveRechargeUserName(user);
+ if (StringUtils.isNotBlank(operatorName)) {
+ charge.setRechargeUserName(operatorName);
+ }
+ }
+
+ private String resolveRechargeUserName(LoginUserInfo user) {
+ if (user == null) {
+ return null;
+ }
+ if (StringUtils.isNotBlank(user.getMemberName())) {
+ return user.getMemberName();
+ }
+ if (Constants.equalsInteger(user.getH5UserType(), LoginUserInfo.H5_USER_CUSTOMER)
+ && StringUtils.isNotBlank(user.getDisplayName())) {
+ int idx = user.getDisplayName().lastIndexOf('-');
+ if (idx >= 0 && idx < user.getDisplayName().length() - 1) {
+ return user.getDisplayName().substring(idx + 1).trim();
+ }
+ }
+ return StringUtils.trimToNull(user.getRealname());
+ }
}
--
Gitblit v1.9.3