This commit is contained in:
tangzh 2025-05-26 22:39:56 +08:00
parent b64e9a4e0d
commit 1576a9417b
57 changed files with 2710 additions and 52 deletions

View File

@ -5,7 +5,7 @@
"author": "Zheng Jie",
"license": "Apache-2.0",
"scripts": {
"dev": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
"dev": "vue-cli-service serve",
"build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview",

View File

@ -0,0 +1,48 @@
import request from '@/utils/request'
export function add(data) {
return request({
url: 'api/busDevice',
method: 'post',
data
})
}
export function del(ids) {
return request({
url: 'api/busDevice/',
method: 'delete',
data: ids
})
}
export function edit(data) {
return request({
url: 'api/busDevice',
method: 'put',
data
})
}
export function getQrCode(id) {
return request({
url: 'api/busDevice/getQrCode?id=' + id,
method: 'get'
})
}
export function getDeviceTree(pid) {
return request({
url: 'api/busDevice/getDeviceTree?pid=' + pid,
method: 'get'
})
}
export function getChild(id) {
return request({
url: 'api/busDevice/child?id=' + id,
method: 'get'
})
}
export default { add, edit, del, getDeviceTree }

View File

@ -0,0 +1,27 @@
import request from '@/utils/request'
export function add(data) {
return request({
url: 'api/busDeviceCommand',
method: 'post',
data
})
}
export function del(ids) {
return request({
url: 'api/busDeviceCommand/',
method: 'delete',
data: ids
})
}
export function edit(data) {
return request({
url: 'api/busDeviceCommand',
method: 'put',
data
})
}
export default { add, edit, del }

View File

@ -0,0 +1,42 @@
import request from '@/utils/request'
export function add(data) {
return request({
url: 'api/busUser',
method: 'post',
data
})
}
export function del(ids) {
return request({
url: 'api/busUser/',
method: 'delete',
data: ids
})
}
export function edit(data) {
return request({
url: 'api/busUser',
method: 'put',
data
})
}
export function updUserCommands(data) {
return request({
url: 'api/busUser/updUserCommands',
method: 'put',
data
})
}
export function get(id) {
return request({
url: 'api/busUser/' + id,
method: 'get'
})
}
export default { add, edit, del, get }

View File

@ -1,13 +1,13 @@
<template>
<div style="display: inline-block">
<el-button v-permission="permission.edit" :loading="crud.status.cu === 2" :disabled="disabledEdit" size="mini" type="primary" icon="el-icon-edit" @click.stop="crud.toEdit(data)" />
<el-button v-permission="permission.edit" :loading="crud.status.cu === 2" :disabled="disabledEdit" size="mini" type="primary" icon="el-icon-edit" title="修改" @click.stop="crud.toEdit(data)" />
<el-popover v-model="pop" v-permission="permission.del" placement="top" width="180" trigger="manual" @show="onPopoverShow" @hide="onPopoverHide">
<p>{{ msg }}</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="doCancel">取消</el-button>
<el-button :loading="crud.dataStatus[crud.getDataId(data)].delete === 2" type="primary" size="mini" @click="crud.doDelete(data)">确定</el-button>
</div>
<el-button slot="reference" :disabled="disabledDle" type="danger" icon="el-icon-delete" size="mini" @click.stop="toDelete" />
<el-button slot="reference" :disabled="disabledDle" type="danger" icon="el-icon-delete" size="mini" @click.stop="toDelete" title="删除"/>
</el-popover>
</div>
</template>

View File

@ -77,7 +77,7 @@ export default {
calculable: true,
series: [
{
name: '案件类型统计',
name: '设备指令统计',
type: 'pie',
roseType: 'radius',
radius: [15, 95],

View File

@ -0,0 +1,178 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.deviceSn" size="small" placeholder="输入设备号" style="width: 180px;" class="filter-item" @keyup.enter.native="crud.toQuery"/>
<el-input v-model="query.model" size="small" placeholder="输入型号" style="width: 180px;" class="filter-item" @keyup.enter.native="crud.toQuery"/>
<el-input v-model="query.brand" size="small" placeholder="输入品牌" style="width: 180px;" class="filter-item" @keyup.enter.native="crud.toQuery"/>
<el-select v-model="query.type" size="small" placeholder="选择类型" class="filter-item" style="width: 180px" @change="crud.toQuery">
<el-option key="" label="全部" value=""/>
<el-option v-for="item in typeList" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
<rrOperation/>
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="设备号" prop="deviceSn">
<el-input v-model="form.deviceSn" style="width: 370px;" />
</el-form-item>
<el-form-item label="型号" prop="model">
<el-input v-model="form.model" style="width: 370px;" />
</el-form-item>
<el-form-item label="品牌">
<el-input v-model="form.brand" style="width: 370px;" />
</el-form-item>
<el-form-item label="固件版本">
<el-input v-model="form.firmwareVersion" style="width: 370px;" />
</el-form-item>
<el-form-item label="放置位置">
<el-input v-model="form.location" style="width: 370px;" />
</el-form-item>
<el-form-item label="类型">
<el-select v-model="form.type" size="small" placeholder="设备类型" class="filter-item" style="width: 370px">
<el-option v-for="item in typeList" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" style="width: 370px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<el-dialog :visible.sync="qrCodeVisible" width="500px">
<div style="text-align: center"><img :src="baseApi + '/file/image/' + qrCodeUrl" /></div>
<div style="text-align: center;"><h4 style="text-align: center;">可以截图保存有效期只有一小时</h4></div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="deviceSn" label="设备序列号" />
<el-table-column prop="model" label="型号" />
<el-table-column prop="brand" label="品牌" />
<el-table-column prop="firmwareVersion" label="固件版本" />
<el-table-column prop="location" label="放置位置" />
<el-table-column prop="status" label="设备状态">
<template slot-scope="scope">
<span v-if="scope.row.status==1">在线</span>
<span v-else-if="scope.row.status==2">离线</span>
<span v-else>故障</span>
</template>
</el-table-column>
<el-table-column prop="type" label="类型">
<template slot-scope="scope">
<span v-if="scope.row.type==1">打印机</span>
<span v-else>摄像头</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" />
<el-table-column v-if="checkPer(['admin','busDevice:edit','busDevice:del'])" label="操作" width="250px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
<el-button type="success" size="mini" title="设备指令" @click="openCommandData(scope.row.id)"><svg-icon icon-class="chain" /></el-button>
<el-button type="warning" size="mini" title="生成二维码" @click="createQrCode(scope.row.id)"><svg-icon icon-class="exit-fullscreen" /></el-button>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
<!-- 子窗口模态框组件 -->
<commandPage :visible.sync="showCommandData" :data="deviceId" @close="closeCommandPage" />
</div>
</div>
</template>
<script>
import crudBusDevice, { getQrCode } from '@/api/bus/busDevice/busDevice'
import commandPage from '@/views/bus/busDeviceCommand/index'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
import {mapGetters} from "vuex";
const defaultForm = { id: null, deviceSn: null, model: null, brand: null, firmwareVersion: null, location: null, status: null, onlineTime: null, offlineTime: null, totalPrintTime: null, remark: null, type: null, createBy: null, updateBy: null, createTime: null, updateTime: null }
export default {
name: 'BusDevice',
components: { pagination, crudOperation, rrOperation, udOperation, commandPage },
mixins: [presenter(), header(), form(defaultForm), crud()],
cruds() {
return CRUD({ title: '', url: 'api/busDevice', idField: 'id', sort: 'id,desc', crudMethod: { ...crudBusDevice }})
},
data() {
return {
qrCodeUrl: '',
qrCodeVisible: false,
permission: {
add: ['admin', 'busDevice:add'],
edit: ['admin', 'busDevice:edit'],
del: ['admin', 'busDevice:del']
},
typeList: [
{ 'label': '打印机', 'value': 1 },
{ 'label': '摄像头', 'value': 2 }
],
rules: {
deviceSn: [
{ required: true, message: '设备序列号不能为空', trigger: 'blur' }
],
model: [
{ required: true, message: '型号不能为空', trigger: 'blur' }
]
},
showCommandData: false,
commandData: [],
commandPage: { page: 1, size: 8, total: 0 },
deviceId: 0
}
},
created() {
this.crud.optShow.download = false
},
computed: {
...mapGetters([
'baseApi'
])
},
methods: {
async createQrCode(id) {
await getQrCode(id).then(res => {
this.qrCodeVisible = true
this.qrCodeUrl = res
})
},
openCommandData(id) {
this.showCommandData = true
this.deviceId = id
},
closeCommandPage() {
this.showCommandData = false
},
// false
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,133 @@
<template>
<el-dialog :visible.sync="visible" title="设备命令" @close="updCommandData">
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" append-to-body style="width: 1000px; margin-left: 25%">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="设备ID" prop="deviceId" hidden="hidden">
<el-input v-model="form.deviceId" />
</el-form-item>
<el-form-item label="指令名称" prop="commandName">
<el-input v-model="form.commandName" style="width: 370px;" />
</el-form-item>
<el-form-item label="指令类型" prop="commandType">
<el-select v-model="form.commandType" size="small" placeholder="类型" class="filter-item" style="width: 370px">
<el-option v-for="item in commandTypes" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
</el-form-item>
<el-form-item label="指令参数">
<el-input v-model="form.commandParams" style="width: 370px;" rows="3" type="textarea"/>
</el-form-item>
<el-form-item label="指令描述">
<el-input v-model="form.description" style="width: 370px;" rows="3" type="textarea"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%; height: 400px" @selection-change="crud.selectionChangeHandler" >
<el-table-column type="selection" width="55" />
<el-table-column prop="commandName" label="指令名称" />
<el-table-column prop="commandType" label="指令类型" >
<template slot-scope="scope">
<span v-if="scope.row.commandType==1">控制</span>
<span v-else-if="scope.row.commandType==2">查询</span>
<span v-else>校准</span>
</template>
</el-table-column>
<el-table-column prop="commandParams" label="指令参数" />
<el-table-column prop="description" label="指令描述" />
<el-table-column v-if="checkPer(['admin','busDeviceCommand:edit','busDeviceCommand:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</el-dialog>
</template>
<script>
import crudBusDeviceCommand from '@/api/bus/busDeviceCommand/busDeviceCommand'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { id: null, deviceId: null, commandName: null, commandType: null, description: null, isSystem: null, commandParams: null, createBy: null, updateBy: null, createTime: null, updateTime: null }
export default {
name: 'BusDeviceCommand',
components: { pagination, crudOperation, udOperation },
props: {
visible: { type: Boolean, default: false },
data: { type: Number, required: true }
},
mixins: [presenter(), header(), form(defaultForm), crud()],
cruds() {
return CRUD({ title: '指令', url: 'api/busDeviceCommand', idField: 'id', sort: 'id,desc', crudMethod: { ...crudBusDeviceCommand }})
},
data() {
return {
commandTypes: [
{ 'label': '控制', 'value': 1 },
{ 'label': '查询', 'value': 2 },
{ 'label': '校准', 'value': 3 }
],
permission: {
add: ['admin', 'busDeviceCommand:add'],
edit: ['admin', 'busDeviceCommand:edit'],
del: ['admin', 'busDeviceCommand:del']
},
rules: {
commandName: [
{required: true, message: '指令名称不能为空', trigger: 'blur'}
],
commandType: [
{required: true, message: '指令类型不能为空', trigger: 'blur'}
],
commandParams: [
{required: true, message: '指令参数不能为空', trigger: 'blur'}
],
}
}
},
watch: {
data(newVal) {
defaultForm.deviceId = newVal
}
},
created() {
this.crud.optShow.download = false
},
methods: {
updCommandData() {
this.visible = false
this.$emit('update:visible', false)
},
// false
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,244 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.nickname" size="small" placeholder="输入用户昵称" style="width: 180px;" class="filter-item" @keyup.enter.native="crud.toQuery"/>
<el-input v-model="query.phone" size="small" placeholder="输入手机号" style="width: 180px;" class="filter-item" @keyup.enter.native="crud.toQuery"/>
<el-select v-model="query.status" size="small" placeholder="选择状态" class="filter-item" style="width: 180px" @change="crud.toQuery">
<el-option key="" label="全部" value=""/>
<el-option v-for="item in statusList" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
<rrOperation />
</div>
<!--如果想在工具栏加入更多按钮可以使用插槽方式 slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
</div>
<el-row :gutter="15">
<!--用户管理-->
<el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="17" style="margin-bottom: 10px">
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<span class="role-span">用户列表</span>
</div>
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="nickname" label="用户昵称" />
<el-table-column prop="avatar" label="头像" >
<template slot-scope="{row}">
<el-image
:src=" baseApi + row.avatar"
:preview-src-list="[baseApi + row.avatar]"
fit="contain"
lazy
class="el-avatar"
>
<div slot="error">
<i class="el-icon-document" />
</div>
</el-image>
</template>
</el-table-column>
<el-table-column prop="phone" label="手机号码" />
<el-table-column prop="status" label="状态">
<template slot-scope="scope">
<span v-if="scope.row.status==1" style="color: #13ce66">正常</span>
<span v-else style="color: #ff6d6d">禁止</span>
</template>
</el-table-column>
<el-table-column prop="sex" label="性别">
<template slot-scope="scope">
<span v-if="scope.row.sex==0">未知</span>
<span v-else-if="scope.row.sex==1"></span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" />
<el-table-column v-if="checkPer(['admin','busUser:edit','busUser:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<el-button v-if="scope.row.status==1" type="danger" size="mini" title="禁用" @click="setStatus(scope.row.id, 0)"><svg-icon icon-class="lock"/></el-button>
<el-button v-else type="success" size="mini" title="启用" @click="setStatus(scope.row.id, 1)"><svg-icon icon-class="unlock"/></el-button>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</el-card>
</el-col>
<!-- 设备 -->
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="7">
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<el-tooltip class="item" effect="dark" content="选择指定用户分配指令" placement="top">
<span class="role-span">设备分配</span>
</el-tooltip>
<el-button
:disabled="!showButton"
:loading="deviceLoading"
icon="el-icon-check"
size="mini"
style="float: right; padding: 6px 9px"
type="primary"
@click="saveUserDevice"
>保存</el-button>
</div>
<el-tree
ref="device"
lazy
:data="deviceData"
:default-checked-keys="deviceCommandIds"
:load="getDeviceDatas"
:props="defaultProps"
check-strictly
accordion
show-checkbox
node-key="id"
@check="deviceChange"
/>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import crudBusUser, { edit, updUserCommands } from '@/api/bus/busUser/busUser'
import { getDeviceTree, getChild } from '@/api/bus/busDevice/busDevice'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation.vue'
import crudOperation from '@crud/CRUD.operation.vue'
import udOperation from '@crud/UD.operation.vue'
import pagination from '@crud/Pagination.vue'
import { mapGetters } from 'vuex'
const defaultForm = { id: null, realName: null, nickname: null, avatar: null, phone: null, status: null, token: null, sex: null, createBy: null, updateBy: null, createTime: null, updateTime: null }
export default {
name: 'BusUser',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
cruds() {
return CRUD({ title: 'busUser', url: 'api/busUser', idField: 'id', sort: 'id,desc', crudMethod: { ...crudBusUser }})
},
data() {
return {
thisUserId: 0,
defaultProps: { children: 'children', label: 'label', isLeaf: 'leaf' },
showButton: false,
deviceLoading: false,
deviceData: [],
deviceCommandIds: [],
permission: {
del: ['admin', 'busUser:del']
},
statusList: [
{ 'label': '正常', 'value': 1 },
{ 'label': '禁用', 'value': 0 }
]
}
},
computed: {
...mapGetters([
'baseApi'
])
},
created() {
this.crud.optShow = { add: false, edit: false, del: true, download: false, reset: true }
},
methods: {
getDeviceDatas(node, resolve) {
setTimeout(() => {
getDeviceTree(node.data.id ? node.data.id : 0).then(res => {
resolve(res)
})
}, 100)
},
//
handleCurrentChange(val) {
if (val) {
const _this = this
//
this.$refs.device.setCheckedKeys([])
// id
this.thisUserId = val.id
// key
this.deviceCommandIds = []
val.commands.forEach(function(data) {
_this.deviceCommandIds.push(data.id)
})
this.showButton = true
}
},
//
saveUserDevice() {
this.deviceLoading = true
const userCommand = { id: this.thisUserId, commands: [] }
// key
this.deviceCommandIds.forEach(function(id) {
const command = { id: id }
userCommand.commands.push(command)
})
updUserCommands(userCommand).then(() => {
this.crud.notify('保存成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
this.deviceLoading = false
this.refreshUser()
}).catch(err => {
this.deviceLoading = false
console.log(err.response.data.message)
})
},
//
refreshUser() {
//
crudBusUser.get(this.thisUserId).then(res => {
for (let i = 0; i < this.crud.data.length; i++) {
if (res.id === this.crud.data[i].id) {
this.crud.data[i] = res
break
}
}
})
},
deviceChange(device) {
// id
getChild(device.id).then(childIds => {
// menuIds
if (this.deviceCommandIds.indexOf(device.id) !== -1) {
for (let i = 0; i < childIds.length; i++) {
const index = this.deviceCommandIds.indexOf(childIds[i])
if (index !== -1) {
this.deviceCommandIds.splice(index, 1)
}
}
} else {
for (let i = 0; i < childIds.length; i++) {
const index = this.deviceCommandIds.indexOf(childIds[i])
if (index === -1) {
this.deviceCommandIds.push(childIds[i])
}
}
}
this.$refs.device.setCheckedKeys(this.deviceCommandIds)
})
},
async setStatus(id, status) {
const params = { id: id, status: status }
await edit(params).then(res => {
this.crud.notify('操作成功', 'success')
})
this.crud.toQuery()
},
// false
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

View File

@ -46,7 +46,7 @@
</div>
<div class="card-panel-description">
<div class="card-panel-text">
执法数据
指令数据
</div>
<count-to :start-val="0" :end-val="groupData.law" :duration="3600" class="card-panel-num" />
</div>

View File

@ -20,5 +20,22 @@
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- 生成二维码 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,28 @@
package me.zhengjie.utils;
import java.util.zip.CRC32;
public class CRC32Utils {
public static String getUnsignedCRC32(String input) {
CRC32 crc32 = new CRC32();
crc32.update(input.getBytes());
int value = (int) crc32.getValue();
// 将int转换为long以避免潜在的符号扩展问题
int unsignedValue = value & 0xFFFFFFFF;
String hexString = formatCrcToHex(unsignedValue);
return "0x" + hexString; // 0x8abd91cc
}
private static String formatCrcToHex(int crc) {
// 方法一采用String.format
String format = String.format("%08x", crc);
return format.toUpperCase();
}
public static void main(String[] args) {
String text = "{\"Name\":\"4KL200URH300.4K\",\"MachineName\":\"3DPLUS\",\"MachineType\":\"Spade\",\"MainboardID\":\"12345678\",\"ProtocolVersion\":\"V3.0.0\",\"FirmwareVersion\":\"V1.0.0\"}";
String crc32Value = getUnsignedCRC32(text);
System.out.println("CRC-32 (unsigned): " + crc32Value);
}
}

View File

@ -63,4 +63,9 @@ public interface CacheKey {
* 数据字典
*/
String DICT_NAME = "dict::name:";
/**
* 二维码
*/
String QR_CODE = "qrCode:";
}

View File

@ -75,11 +75,11 @@ public class FileUtil extends cn.hutool.core.io.FileUtil {
*/
private static final DecimalFormat DF = new DecimalFormat("0.00");
public static final String IMAGE = "图片";
public static final String TXT = "文档";
public static final String MUSIC = "音乐";
public static final String VIDEO = "视频";
public static final String OTHER = "其他";
public static final String IMAGE = "image";
public static final String TXT = "txt";
public static final String MUSIC = "music";
public static final String VIDEO = "video";
public static final String OTHER = "others";
/**

View File

@ -0,0 +1,82 @@
package me.zhengjie.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
public class QrCodeUtil {
//CODE_WIDTH二维码宽度单位像素
private static final int CODE_WIDTH = 400;
//CODE_HEIGHT二维码高度单位像素
private static final int CODE_HEIGHT = 400;
//FRONT_COLOR二维码前景色0x000000 表示黑色
private static final int FRONT_COLOR = 0x000000;
//BACKGROUND_COLOR二维码背景色0xFFFFFF 表示白色
//演示用 16 进制表示和前端页面 CSS 的取色是一样的注意前后景颜色应该对比明显如常见的黑白
private static final int BACKGROUND_COLOR = 0xFFFFFF;
//文件类型
private static final String FILE_FORMAT = "png";
public MultipartFile getQrCode(String content, String orderCode) {
try {
if (StringUtils.isBlank(content)) { return null; }
content = content.trim();
// 核心代码-生成二维码
BufferedImage image = getBufferedImage(content);
MultipartFile multipartFile = toMultipartFile(image, orderCode);
return multipartFile;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static MultipartFile toMultipartFile(BufferedImage image, String orderCode) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(image, FILE_FORMAT, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream.toByteArray();
// 使用MockMultipartFile来创建MultipartFile
return new MockMultipartFile(orderCode, orderCode + "." + FILE_FORMAT , "image/png", byteArray);
}
//核心代码-生成二维码
private static BufferedImage getBufferedImage(String content) throws WriterException {
//com.google.zxing.EncodeHintType编码提示类型,枚举类型
Map<EncodeHintType, Object> hints = new HashMap<>();
//EncodeHintType.CHARACTER_SET设置字符编码类型
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
//EncodeHintType.ERROR_CORRECTION设置误差校正
//ErrorCorrectionLevel误差校正等级L = ~7% correctionM = ~15% correctionQ = ~25% correctionH = ~30% correction
//不设置时默认为 L 等级等级不一样生成的图案不同但扫描的结果是一样的
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
//EncodeHintType.MARGIN设置二维码边距单位像素值越小二维码离四周越近
hints.put(EncodeHintType.MARGIN, 1);
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, CODE_WIDTH, CODE_HEIGHT, hints);
BufferedImage bufferedImage = new BufferedImage(CODE_WIDTH, CODE_HEIGHT, BufferedImage.TYPE_INT_BGR);
for (int x = 0; x < CODE_WIDTH; x++) {
for (int y = 0; y < CODE_HEIGHT; y++) {
bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR);
}
}
return bufferedImage;
}
}

View File

@ -0,0 +1,43 @@
package me.zhengjie.modules.front.rest;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.annotation.Log;
import me.zhengjie.annotation.rest.AnonymousPostMapping;
import me.zhengjie.modules.front.domain.dto.LoginVo;
import me.zhengjie.modules.front.service.WeChatService;
import me.zhengjie.modules.security.service.dto.JwtUserDto;
import me.zhengjie.modules.system.service.BusDeviceService;
import me.zhengjie.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@RestController("DeviceController")
@RequestMapping("api/front/device")
@Api(tags = "微信:设备管理")
public class DeviceController {
@Autowired private BusDeviceService busDeviceService;
@Log("绑定设备")
@ApiOperation(value = "绑定设备")
@GetMapping(value = "/binding")
public ResponseEntity<Object> binding(@RequestParam String code) {
busDeviceService.binding(code);
return new ResponseEntity<>("绑定成功", HttpStatus.OK);
}
}

View File

@ -6,6 +6,8 @@ import javax.servlet.http.HttpServletRequest;
public interface WeChatService {
String getQrCode(Long deviceId, String code);
LoginVo authorizeLogin(String code, HttpServletRequest request);
}

View File

@ -6,7 +6,6 @@ import com.alibaba.fastjson2.JSONObject;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.front.domain.dto.LoginVo;
import me.zhengjie.modules.front.domain.dto.WeChatMiniAuthorizeVo;
import me.zhengjie.modules.front.service.CusUserService;
import me.zhengjie.modules.front.service.WeChatService;
import me.zhengjie.modules.security.config.SecurityProperties;
import me.zhengjie.modules.security.security.TokenProvider;
@ -14,13 +13,15 @@ import me.zhengjie.modules.security.service.OnlineUserService;
import me.zhengjie.modules.security.service.dto.JwtUserDto;
import me.zhengjie.modules.system.domain.BusUser;
import me.zhengjie.modules.system.mapper.BusUserMapper;
import me.zhengjie.utils.DateUtil;
import me.zhengjie.utils.RestTemplateUtils;
import me.zhengjie.modules.system.service.BusUserService;
import me.zhengjie.service.LocalStorageService;
import me.zhengjie.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
@ -38,14 +39,22 @@ public class WeChatServiceImpl implements WeChatService {
@Value("${weChat.secret}")
private String secret;
@Autowired private QrCodeUtil qrCodeUtil;
@Autowired private BusUserMapper BusUserMapper;
@Autowired private RestTemplateUtils restTemplateUtil;
@Autowired private TokenProvider tokenProvider;
@Autowired private SecurityProperties properties;
@Autowired private OnlineUserService onlineUserService;
@Autowired private CusUserService BusUserService;
private static final String WECHAT_MINI_SNS_AUTH_CODE2SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code";
@Autowired private BusUserService busUserService;
@Autowired private LocalStorageService localStorageService;
private static final String DEFAULT_AVATAR = "/avatar/avatar.png";
private static final String WECHAT_MINI_SNS_AUTH_CODE2SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code";
@Override
public String getQrCode(Long deviceId, String code) {
MultipartFile mFile = qrCodeUtil.getQrCode(code, code);
return localStorageService.createFile(code, mFile);
}
@Override
public LoginVo authorizeLogin(String code, HttpServletRequest request) {
@ -74,7 +83,7 @@ public class WeChatServiceImpl implements WeChatService {
loginVo.setPhone(BusUser.getPhone());
loginVo.setAvatar(BusUser.getAvatar());
// 生成令牌
JwtUserDto jwtUserDto = BusUserService.addUserCache(BusUser);
JwtUserDto jwtUserDto = busUserService.addUserCache(BusUser);
String token = tokenProvider.createToken(jwtUserDto);
loginVo.setToken(properties.getTokenStartWith() + token);
// 保存在线信息

View File

@ -0,0 +1,46 @@
package me.zhengjie.modules.security.config;
import me.zhengjie.modules.security.config.handler.DeviceWebSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
/**
* WebSocket配置类启用WebSocket功能并注册处理器
*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
private final DeviceWebSocketHandler deviceWebSocketHandler;
public WebSocketConfig(DeviceWebSocketHandler deviceWebSocketHandler) {
this.deviceWebSocketHandler = deviceWebSocketHandler;
}
/**
* 注册WebSocket处理器配置端点和允许的源
* @param registry WebSocket处理器注册器
*/
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(deviceWebSocketHandler, "/ws/device")
.setAllowedOrigins("*");
}
/**
* 配置WebSocket容器参数设置消息缓冲区大小
* @return WebSocket容器工厂Bean
*/
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
}

View File

@ -0,0 +1,77 @@
package me.zhengjie.modules.security.config.handler;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
/**
* 处理客户端WebSocket连接的处理器
* 管理客户端会话并转发设备状态变化
*/
@Component
public class DeviceWebSocketHandler extends TextWebSocketHandler {
// 存储所有活跃的WebSocket会话键为会话ID
private final ConcurrentHashMap<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
private final ThirdPartyWebSocketClient thirdPartyClient;
public DeviceWebSocketHandler(ThirdPartyWebSocketClient thirdPartyClient) {
this.thirdPartyClient = thirdPartyClient;
}
/**
* 处理来自客户端的文本消息
* @param session 客户端会话
* @param message 收到的消息
* @throws IOException 发送消息失败时抛出
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
String payload = message.getPayload();
// 将客户端指令转发给第三方WebSocket服务
thirdPartyClient.sendCommand(payload);
}
/**
* 客户端连接建立后的回调方法
* @param session 新建立的会话
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.put(session.getId(), session);
// 可在此处添加连接初始化逻辑
}
/**
* 客户端连接关闭后的回调方法
* @param session 关闭的会话
* @param status 关闭状态
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
sessions.remove(session.getId());
}
/**
* 通知所有客户端设备状态变化
* @param deviceId 设备ID
* @param status 设备状态
*/
public void notifyDeviceChange(String deviceId, String status) {
sessions.forEach((id, session) -> {
try {
// 向客户端发送JSON格式的设备状态消息
session.sendMessage(new TextMessage(
String.format("{\"deviceId\": \"%s\", \"status\": \"%s\"}", deviceId, status)
));
} catch (IOException e) {
e.printStackTrace();
}
});
}
}

View File

@ -0,0 +1,148 @@
package me.zhengjie.modules.security.config.handler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 与第三方WebSocket服务通信的客户端
* 负责建立连接订阅设备和处理消息
*/
@Component
public class ThirdPartyWebSocketClient extends TextWebSocketHandler {
@Value("${thirdparty.ws.url}")
private String thirdPartyWsUrl;
private WebSocketSession session;
// 存储已订阅的设备键为设备ID值为订阅状态
private final ConcurrentHashMap<String, String> subscribedDevices = new ConcurrentHashMap<>();
private final DeviceWebSocketHandler deviceHandler;
private final ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor();
public ThirdPartyWebSocketClient(DeviceWebSocketHandler deviceHandler) {
this.deviceHandler = deviceHandler;
}
/**
* Bean初始化后连接到第三方WebSocket服务
*/
@PostConstruct
public void init() {
connect();
}
/**
* 建立与第三方WebSocket服务的连接
*/
private void connect() {
WebSocketClient client = new StandardWebSocketClient();
try {
session = client.doHandshake(this, thirdPartyWsUrl).get();
System.out.println("Connected to third-party WebSocket server");
} catch (Exception e) {
e.printStackTrace();
// 连接失败安排重连
scheduleReconnect();
}
}
/**
* 安排重连任务
*/
private void scheduleReconnect() {
reconnectExecutor.schedule(this::connect, 5, TimeUnit.SECONDS);
}
/**
* 订阅指定设备的状态变化
* @param deviceId 设备ID
*/
public void subscribeDevice(String deviceId) {
if (session != null && session.isOpen()) {
try {
// 发送订阅消息到第三方服务
session.sendMessage(new TextMessage(
String.format("{\"action\": \"subscribe\", \"deviceId\": \"%s\"}", deviceId)
));
subscribedDevices.put(deviceId, "subscribed");
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 向第三方服务发送指令
* @param command 指令内容
*/
public void sendCommand(String command) {
if (session != null && session.isOpen()) {
try {
session.sendMessage(new TextMessage(command));
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 处理从第三方服务接收到的文本消息
* @param session 会话
* @param message 收到的消息
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
String payload = message.getPayload();
// 处理设备状态变化消息
processDeviceStatusChange(payload);
}
/**
* 解析设备状态变化消息并通知客户端
* @param payload 消息内容
*/
private void processDeviceStatusChange(String payload) {
// 解析消息并提取设备ID和状态
String deviceId = extractDeviceId(payload);
String status = extractStatus(payload);
if (deviceId != null && status != null) {
// 通知所有连接的客户端设备状态变化
deviceHandler.notifyDeviceChange(deviceId, status);
}
}
/**
* 从消息中提取设备ID
* 需要根据第三方服务的消息格式实现具体解析逻辑
* @param payload 消息内容
* @return 设备ID
*/
private String extractDeviceId(String payload) {
// TODO: 实现JSON解析逻辑
return null;
}
/**
* 从消息中提取设备状态
* 需要根据第三方服务的消息格式实现具体解析逻辑
* @param payload 消息内容
* @return 设备状态
*/
private String extractStatus(String payload) {
// TODO: 实现JSON解析逻辑
return null;
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
import java.sql.Timestamp;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Set;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
/**
* @description /
* @author tz
* @date 2025-05-20
**/
@Data
@TableName("bus_device")
public class BusDevice implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "设备唯一标识")
private Long id;
@ApiModelProperty(value = "设备序列号(唯一)")
private String deviceSn;
@ApiModelProperty(value = "型号")
private String model;
@ApiModelProperty(value = "品牌")
private String brand;
@ApiModelProperty(value = "固件版本")
private String firmwareVersion;
@ApiModelProperty(value = "放置位置")
private String location;
@ApiModelProperty(value = "设备状态1=在线2=离线3=故障)")
private Integer status;
@ApiModelProperty(value = "最近上线时间")
private Timestamp onlineTime;
@ApiModelProperty(value = "最近下线时间")
private Timestamp offlineTime;
@ApiModelProperty(value = "累计时长(分钟)")
private Integer totalPrintTime;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "类型1打印机2摄像头")
private Integer type;
@ApiModelProperty(value = "创建者")
private String createBy;
@ApiModelProperty(value = "更新者")
private String updateBy;
@ApiModelProperty(value = "创建时间")
private Timestamp createTime;
@ApiModelProperty(value = "更新时间")
private Timestamp updateTime;
public void copy(BusDevice source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* @description /
* @author tz
* @date 2025-05-21
**/
@Data
@TableName("bus_device_command")
public class BusDeviceCommand implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "指令唯一标识")
private Long id;
@ApiModelProperty(value = "指令代码(唯一)")
private Long deviceId;
@ApiModelProperty(value = "指令名称")
private String commandName;
@ApiModelProperty(value = "指令类型1=控制2=查询3=校准)")
private Integer commandType;
@ApiModelProperty(value = "指令描述")
private String description;
@ApiModelProperty(value = "是否为系统指令1=是2=否)")
private Integer isSystem;
@ApiModelProperty(value = "指令参数JSON格式")
private String commandParams;
@ApiModelProperty(value = "创建者")
private String createBy;
@ApiModelProperty(value = "更新者")
private String updateBy;
@ApiModelProperty(value = "创建时间")
private Timestamp createTime;
@ApiModelProperty(value = "更新时间")
private Timestamp updateTime;
public void copy(BusDeviceCommand source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

View File

@ -18,15 +18,19 @@ package me.zhengjie.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import me.zhengjie.base.BaseEntity;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.List;
import java.util.Set;
@Getter
@Setter
@ -61,6 +65,10 @@ public class BusUser extends BaseEntity implements Serializable {
@ApiModelProperty(value = "创建时间")
private Timestamp createTime;
@TableField(exist = false)
@ApiModelProperty(value = "指令", hidden = true)
private List<BusTreeVo> commands;
public void copy(BusUser source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.domain;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
import java.sql.Timestamp;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* @description /
* @author tz
* @date 2025-05-23
**/
@Data
@TableName("bus_user_device_command")
public class BusUserCommand implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "操作记录唯一标识")
private Long id;
@NotNull
@ApiModelProperty(value = "用户ID外键")
private Long userId;
@NotNull
@ApiModelProperty(value = "设备ID外键")
private Long deviceId;
@NotNull
@ApiModelProperty(value = "指令ID外键")
private Long commandId;
@ApiModelProperty(value = "创建者")
private String createBy;
@ApiModelProperty(value = "更新者")
private String updateBy;
@NotNull
@ApiModelProperty(value = "创建时间")
private Timestamp createTime;
@NotNull
@ApiModelProperty(value = "更新时间")
private Timestamp updateTime;
public void copy(BusUserCommand source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.domain;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
import java.sql.Timestamp;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* @description /
* @author tz
* @date 2025-05-23
**/
@Data
@TableName("bus_user_device")
public class BusUserDevice implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "绑定记录唯一标识")
private Long id;
@NotNull
@ApiModelProperty(value = "用户ID外键")
private Long userId;
@NotNull
@ApiModelProperty(value = "设备ID外键")
private Long deviceId;
@NotNull
@ApiModelProperty(value = "是否为主用户1=是2=否)")
private Integer isPrimary;
@NotNull
@ApiModelProperty(value = "绑定状态1=有效2=失效)")
private Integer status;
@ApiModelProperty(value = "创建者")
private String createBy;
@ApiModelProperty(value = "更新者")
private String updateBy;
@NotNull
@ApiModelProperty(value = "创建时间")
private Timestamp createTime;
@NotNull
@ApiModelProperty(value = "更新时间")
private Timestamp updateTime;
public void copy(BusUserDevice source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author tz
* @date 2025-05-21
**/
@Data
public class BusCommandQueryCriteria {
@ApiModelProperty(value = "页码", example = "1")
private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10")
private Integer size = 10;
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.domain.dto;
import lombok.Data;
import io.swagger.annotations.ApiModelProperty;
import me.zhengjie.modules.system.domain.BusDevice;
/**
* @author tz
* @date 2025-05-20
**/
@Data
public class BusDeviceQueryCriteria extends BusDevice {
@ApiModelProperty(value = "页码", example = "1")
private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10")
private Integer size = 10;
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author tz
* @date 2025-05-21
**/
@Data
public class BusTreeVo {
@ApiModelProperty(value = "标识")
private String id;
@ApiModelProperty(value = "上级")
private String pid;
@ApiModelProperty(value = "名称")
private String name;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "子节点数目", hidden = true)
private Integer subCount = 0;
@ApiModelProperty(value = "标签名称")
public String getLabel() {
return name;
}
@ApiModelProperty(value = "是否有子节点")
public Boolean getHasChildren() {
return subCount > 0;
}
@ApiModelProperty(value = "是否为叶子")
public Boolean getLeaf() {
return subCount <= 0;
}
}

View File

@ -17,17 +17,19 @@ package me.zhengjie.modules.system.domain.dto;
import lombok.Data;
import io.swagger.annotations.ApiModelProperty;
import me.zhengjie.modules.system.domain.BusUser;
/**
* @author tz
* @date 2025-05-19
**/
@Data
public class BusUserQueryCriteria{
public class BusUserQueryCriteria extends BusUser {
@ApiModelProperty(value = "页码", example = "1")
private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10")
private Integer size = 10;
}

View File

@ -0,0 +1,31 @@
///*
// * Copyright 2019-2025 Tz
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//package me.zhengjie.modules.system.domain.enums;
//
///**
// * @author ZhangHouYing
// * @date 2019-08-10 9:56
// */
//public enum MsgType {
// /** 连接 */
// CONNECT,
// /** 关闭 */
// CLOSE,
// /** 信息 */
// INFO,
// /** 错误 */
// ERROR
//}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.mapper;
import java.util.List;
import me.zhengjie.modules.system.domain.BusDevice;
import me.zhengjie.modules.system.domain.BusDeviceCommand;
import me.zhengjie.modules.system.domain.dto.BusCommandQueryCriteria;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
/**
* @author tz
* @date 2025-05-21
**/
@Mapper
public interface BusCommandMapper extends BaseMapper<BusDeviceCommand> {
IPage<BusDeviceCommand> findAll(@Param("criteria") BusCommandQueryCriteria criteria, Page<Object> page);
List<BusDeviceCommand> findAll(@Param("criteria") BusCommandQueryCriteria criteria);
List<BusTreeVo> getCommandByDeviceId(@Param("pid") Long pid);
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.mapper;
import java.util.List;
import me.zhengjie.modules.system.domain.BusDevice;
import me.zhengjie.modules.system.domain.dto.BusDeviceQueryCriteria;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
/**
* @author tz
* @date 2025-05-20
**/
@Mapper
public interface BusDeviceMapper extends BaseMapper<BusDevice> {
IPage<BusDevice> findAll(@Param("criteria") BusDeviceQueryCriteria criteria, Page<Object> page);
List<BusDevice> findAll(@Param("criteria") BusDeviceQueryCriteria criteria);
List<BusTreeVo> getDevice();
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.mapper;
import me.zhengjie.modules.system.domain.BusUserCommand;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author tz
* @date 2025-05-23
**/
@Mapper
public interface BusUserCommandMapper extends BaseMapper<BusUserCommand> {
void bindingCommand(@Param("userId") Long id, @Param("commandIds") List<Long> commandIds);
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.mapper;
import me.zhengjie.modules.system.domain.BusUserDevice;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author tz
* @date 2025-05-23
**/
@Mapper
public interface BusUserDeviceMapper extends BaseMapper<BusUserDevice> {
void bindingDevice(@Param("userId") Long id, @Param("deviceIds") List<Long> deviceIds);
}

View File

@ -18,6 +18,7 @@ package me.zhengjie.modules.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import me.zhengjie.modules.system.domain.BusUser;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import me.zhengjie.modules.system.domain.dto.BusUserQueryCriteria;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@ -38,4 +39,5 @@ public interface BusUserMapper extends BaseMapper<BusUser> {
List<BusUser> findAll(@Param("criteria") BusUserQueryCriteria criteria);
List<BusTreeVo> getUserCommands(@Param("userId") Long id);
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.rest;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import me.zhengjie.annotation.Log;
import me.zhengjie.modules.system.domain.BusDeviceCommand;
import me.zhengjie.modules.system.domain.dto.BusCommandQueryCriteria;
import me.zhengjie.modules.system.service.BusCommandService;
import me.zhengjie.utils.PageResult;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author tz
* @date 2025-05-21
**/
@RestController
@RequiredArgsConstructor
@Api(tags = "系统:设备指令")
@RequestMapping("/api/busDeviceCommand")
public class BusCommandController {
private final BusCommandService busDeviceCommandService;
@GetMapping
@ApiOperation("查询busCommand")
@PreAuthorize("@el.check('busDeviceCommand:list')")
public ResponseEntity<PageResult<BusDeviceCommand>> queryBusDeviceCommand(BusCommandQueryCriteria criteria){
Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
return new ResponseEntity<>(busDeviceCommandService.queryAll(criteria,page),HttpStatus.OK);
}
@PostMapping
@Log("新增busCommand")
@ApiOperation("新增busCommand")
@PreAuthorize("@el.check('busDeviceCommand:add')")
public ResponseEntity<Object> createBusDeviceCommand(@Validated @RequestBody BusDeviceCommand resources){
busDeviceCommandService.create(resources);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@PutMapping
@Log("修改busCommand")
@ApiOperation("修改busCommand")
@PreAuthorize("@el.check('busDeviceCommand:edit')")
public ResponseEntity<Object> updateBusDeviceCommand(@Validated @RequestBody BusDeviceCommand resources){
busDeviceCommandService.update(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@DeleteMapping
@Log("删除busCommand")
@ApiOperation("删除busCommand")
@PreAuthorize("@el.check('busDeviceCommand:del')")
public ResponseEntity<Object> deleteBusDeviceCommand(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
busDeviceCommandService.deleteAll(ids);
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.rest;
import cn.hutool.core.collection.CollUtil;
import me.zhengjie.annotation.Log;
import me.zhengjie.modules.system.domain.BusDevice;
import me.zhengjie.modules.system.domain.BusDeviceCommand;
import me.zhengjie.modules.system.domain.Menu;
import me.zhengjie.modules.system.domain.dto.BusDeviceQueryCriteria;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import me.zhengjie.modules.system.service.BusCommandService;
import me.zhengjie.modules.system.service.BusDeviceService;
import lombok.RequiredArgsConstructor;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import me.zhengjie.utils.PageResult;
/**
* @author tz
* @date 2025-05-20
**/
@RestController
@RequiredArgsConstructor
@Api(tags = "系统:设备管理")
@RequestMapping("/api/busDevice")
public class BusDeviceController {
private final BusDeviceService busDeviceService;
private final BusCommandService busCommandService;
@ApiOperation("根据ID返回所有子节点ID包含自身ID")
@GetMapping(value = "/child")
public ResponseEntity<Object> child(@RequestParam String id) {
Long newId = Long.parseLong(id.replace("d", "").replace("c", ""));
Set<BusTreeVo> menuSet = new HashSet<>();
BusTreeVo set = new BusTreeVo();
if (id.contains("d")) {
BusDevice entity = busDeviceService.getById(newId);
List<BusTreeVo> list = busDeviceService.getDeviceTree(newId);
set.setId("d" + newId);
set.setPid("0");
set.setName("品牌:" + entity.getBrand() + " | " + "型号:" + entity.getModel());
set.setRemark(entity.getLocation() + "[" + (entity.getType().equals(1) ? "3D打印机" : "摄像头") + "]");
menuSet.add(set);
menuSet.addAll(list);
} else {
BusDeviceCommand entity = busCommandService.getById(newId);
set.setId("c" + newId);
set.setPid("d" + entity.getDeviceId());
set.setName(entity.getCommandName());
set.setRemark(entity.getDescription());
menuSet.add(set);
}
Set<String> ids = menuSet.stream().map(BusTreeVo::getId).collect(Collectors.toSet());
return new ResponseEntity<>(ids,HttpStatus.OK);
}
@ApiOperation("查询设备指令Tree")
@GetMapping(value = "/getDeviceTree")
public ResponseEntity<List<BusTreeVo>> getDeviceTree(@RequestParam String pid) {
pid = pid.replace("d", "").replace("c", "");
return new ResponseEntity<>(busDeviceService.getDeviceTree(Long.parseLong(pid)), HttpStatus.OK);
}
@GetMapping("getQrCode")
@ApiOperation("查询busDevice")
@PreAuthorize("@el.check('busDevice:qrCode')")
public ResponseEntity<String> getQrCode(BusDeviceQueryCriteria criteria) {
String qrCodeUrl = busDeviceService.getQrCode(criteria.getId());
return new ResponseEntity<>(qrCodeUrl, HttpStatus.OK);
}
@GetMapping
@ApiOperation("查询busDevice")
@PreAuthorize("@el.check('busDevice:list')")
public ResponseEntity<PageResult<BusDevice>> queryBusDevice(BusDeviceQueryCriteria criteria){
Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
return new ResponseEntity<>(busDeviceService.queryAll(criteria, page), HttpStatus.OK);
}
@PostMapping
@Log("新增busDevice")
@ApiOperation("新增busDevice")
@PreAuthorize("@el.check('busDevice:add')")
public ResponseEntity<Object> createBusDevice(@Validated @RequestBody BusDevice resources){
busDeviceService.create(resources);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@PutMapping
@Log("修改busDevice")
@ApiOperation("修改busDevice")
@PreAuthorize("@el.check('busDevice:edit')")
public ResponseEntity<Object> updateBusDevice(@Validated @RequestBody BusDevice resources){
busDeviceService.update(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@DeleteMapping
@Log("删除busDevice")
@ApiOperation("删除busDevice")
@PreAuthorize("@el.check('busDevice:del')")
public ResponseEntity<Object> deleteBusDevice(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
busDeviceService.deleteAll(ids);
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -0,0 +1,31 @@
package me.zhengjie.modules.system.rest;
import me.zhengjie.modules.system.service.socket.DeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/printer")
public class BusDeviceSocketController {
@Autowired DeviceService deviceService;
/**
* 订阅设备状态变化的HTTP接口
* @param deviceId 设备ID
*/
@PostMapping("/{deviceId}/subscribe")
public void subscribe(@PathVariable String deviceId) {
deviceService.subscribeToDevice(deviceId);
}
/**
* 向设备发送指令的HTTP接口
* @param deviceId 设备ID
* @param command 指令内容
*/
@PostMapping("/{deviceId}/command")
public void sendCommand(@PathVariable String deviceId, @RequestBody String command) {
deviceService.sendDeviceCommand(deviceId, command);
}
}

View File

@ -17,7 +17,9 @@ package me.zhengjie.modules.system.rest;
import me.zhengjie.annotation.Log;
import me.zhengjie.modules.system.domain.BusUser;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import me.zhengjie.modules.system.domain.dto.BusUserQueryCriteria;
import me.zhengjie.modules.system.service.BusUserDeviceService;
import me.zhengjie.modules.system.service.BusUserService;
import lombok.RequiredArgsConstructor;
import java.util.List;
@ -36,27 +38,44 @@ import me.zhengjie.utils.PageResult;
**/
@RestController
@RequiredArgsConstructor
@Api(tags = "busUser")
@Api(tags = "系统:系统用户")
@RequestMapping("/api/busUser")
public class BusUserController {
private final BusUserService busUserService;
private final BusUserDeviceService busUserDeviceService;
@Log("修改用户设备指令")
@ApiOperation("修改用户设备指令")
@PutMapping(value = "/updUserCommands")
@PreAuthorize("@el.check('roles:edit')")
public ResponseEntity<Object> updUserCommands(@RequestBody BusUser busUser) {
busUserDeviceService.updUserCommands(busUser);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@ApiOperation("查询用户信息")
@GetMapping(value = "/{id}")
@PreAuthorize("@el.check('busUser:list')")
public ResponseEntity<BusUser> findUserById(@PathVariable Long id) {
BusUser busUser = busUserService.getById(id);
List<BusTreeVo> userCommands = busUserService.getUserCommands(busUser.getId());
busUser.setCommands(userCommands);
return new ResponseEntity<>(busUser, HttpStatus.OK);
}
@GetMapping
@ApiOperation("查询busUser")
@PreAuthorize("@el.check('busUser:list')")
public ResponseEntity<PageResult<BusUser>> queryBusUser(BusUserQueryCriteria criteria){
Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
return new ResponseEntity<>(busUserService.queryAll(criteria,page),HttpStatus.OK);
PageResult<BusUser> result = busUserService.queryAll(criteria, page);
List<BusUser> content = result.getContent();
for (BusUser busUser : content) {
List<BusTreeVo> userCommands = busUserService.getUserCommands(busUser.getId());
busUser.setCommands(userCommands);
}
@PostMapping
@Log("新增busUser")
@ApiOperation("新增busUser")
@PreAuthorize("@el.check('busUser:add')")
public ResponseEntity<Object> createBusUser(@Validated @RequestBody BusUser resources){
busUserService.create(resources);
return new ResponseEntity<>(HttpStatus.CREATED);
return new ResponseEntity<>(result, HttpStatus.OK);
}
@PutMapping
@ -68,12 +87,4 @@ public class BusUserController {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@DeleteMapping
@Log("删除busUser")
@ApiOperation("删除busUser")
@PreAuthorize("@el.check('busUser:del')")
public ResponseEntity<Object> deleteBusUser(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
busUserService.deleteAll(ids);
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.service;
import java.util.List;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import me.zhengjie.modules.system.domain.BusDeviceCommand;
import me.zhengjie.modules.system.domain.dto.BusCommandQueryCriteria;
import me.zhengjie.utils.PageResult;
/**
* @description 服务接口
* @author tz
* @date 2025-05-21
**/
public interface BusCommandService extends IService<BusDeviceCommand> {
/**
* 查询数据分页
* @param criteria 条件
* @param page 分页参数
* @return PageResult
*/
PageResult<BusDeviceCommand> queryAll(BusCommandQueryCriteria criteria, Page<Object> page);
/**
* 查询所有数据不分页
* @param criteria 条件参数
* @return List<BusDeviceCommandDto>
*/
List<BusDeviceCommand> queryAll(BusCommandQueryCriteria criteria);
/**
* 创建
* @param resources /
*/
void create(BusDeviceCommand resources);
/**
* 编辑
* @param resources /
*/
void update(BusDeviceCommand resources);
/**
* 多选删除
* @param ids /
*/
void deleteAll(List<Long> ids);
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.service;
import java.util.List;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import me.zhengjie.modules.system.domain.BusDevice;
import me.zhengjie.modules.system.domain.Menu;
import me.zhengjie.modules.system.domain.dto.BusDeviceQueryCriteria;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import me.zhengjie.utils.PageResult;
/**
* @description 服务接口
* @author tz
* @date 2025-05-20
**/
public interface BusDeviceService extends IService<BusDevice> {
/**
* 查询数据分页
* @param criteria 条件
* @param page 分页参数
* @return PageResult
*/
PageResult<BusDevice> queryAll(BusDeviceQueryCriteria criteria, Page<Object> page);
/**
* 查询所有数据不分页
* @param criteria 条件参数
* @return List<BusDeviceDto>
*/
List<BusDevice> queryAll(BusDeviceQueryCriteria criteria);
/**
* 创建
* @param resources /
*/
void create(BusDevice resources);
/**
* 编辑
* @param resources /
*/
void update(BusDevice resources);
/**
* 多选删除
* @param ids /
*/
void deleteAll(List<Long> ids);
String getQrCode(Long id);
void binding(String code);
List<BusTreeVo> getDeviceTree(Long pid);
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import me.zhengjie.modules.system.domain.BusUser;
import me.zhengjie.modules.system.domain.BusUserDevice;
/**
* @description 服务接口
* @author tz
* @date 2025-05-23
**/
public interface BusUserDeviceService extends IService<BusUserDevice> {
void updUserCommands(BusUser busUser);
}

View File

@ -15,16 +15,12 @@
*/
package me.zhengjie.modules.system.service;
import me.zhengjie.domain.BusUser;
import me.zhengjie.domain.dto.BusUserQueryCriteria;
import java.util.Map;
import java.util.List;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import me.zhengjie.modules.security.service.dto.JwtUserDto;
import me.zhengjie.modules.system.domain.BusUser;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import me.zhengjie.modules.system.domain.dto.BusUserQueryCriteria;
import me.zhengjie.utils.PageResult;
@ -69,4 +65,7 @@ public interface BusUserService extends IService<BusUser> {
* @param ids /
*/
void deleteAll(List<Long> ids);
List<BusTreeVo> getUserCommands(Long id);
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.service.impl;
import me.zhengjie.modules.system.domain.BusDeviceCommand;
import me.zhengjie.modules.system.domain.dto.BusCommandQueryCriteria;
import me.zhengjie.modules.system.mapper.BusCommandMapper;
import me.zhengjie.modules.system.service.BusCommandService;
import me.zhengjie.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import me.zhengjie.utils.PageUtil;
import java.util.List;
import me.zhengjie.utils.PageResult;
/**
* @description 服务实现
* @author tz
* @date 2025-05-21
**/
@Service
@RequiredArgsConstructor
public class BusCommandServiceImpl extends ServiceImpl<BusCommandMapper, BusDeviceCommand> implements BusCommandService {
private final BusCommandMapper busDeviceCommandMapper;
@Override
public PageResult<BusDeviceCommand> queryAll(BusCommandQueryCriteria criteria, Page<Object> page){
return PageUtil.toPage(busDeviceCommandMapper.findAll(criteria, page));
}
@Override
public List<BusDeviceCommand> queryAll(BusCommandQueryCriteria criteria){
return busDeviceCommandMapper.findAll(criteria);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void create(BusDeviceCommand resources) {
busDeviceCommandMapper.insert(resources);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(BusDeviceCommand resources) {
BusDeviceCommand busDeviceCommand = getById(resources.getId());
busDeviceCommand.copy(resources);
busDeviceCommandMapper.updateById(busDeviceCommand);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteAll(List<Long> ids) {
busDeviceCommandMapper.deleteBatchIds(ids);
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.service.impl;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.front.service.WeChatService;
import me.zhengjie.modules.system.domain.BusDevice;
import me.zhengjie.modules.system.domain.Menu;
import me.zhengjie.modules.system.domain.dto.BusDeviceQueryCriteria;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import me.zhengjie.modules.system.mapper.BusCommandMapper;
import me.zhengjie.modules.system.mapper.BusDeviceMapper;
import me.zhengjie.modules.system.service.BusDeviceService;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import me.zhengjie.utils.CacheKey;
import me.zhengjie.utils.RedisUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import me.zhengjie.utils.PageUtil;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import me.zhengjie.utils.PageResult;
/**
* @description 服务实现
* @author tz
* @date 2025-05-20
**/
@Service
@RequiredArgsConstructor
public class BusDeviceServiceImpl extends ServiceImpl<BusDeviceMapper, BusDevice> implements BusDeviceService {
private final BusDeviceMapper busDeviceMapper;
private final BusCommandMapper busCommandMapper;
private final WeChatService weChatService;
private final RedisUtils redisUtils;
@Override
public PageResult<BusDevice> queryAll(BusDeviceQueryCriteria criteria, Page<Object> page){
return PageUtil.toPage(busDeviceMapper.findAll(criteria, page));
}
@Override
public List<BusDevice> queryAll(BusDeviceQueryCriteria criteria){
return busDeviceMapper.findAll(criteria);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void create(BusDevice resources) {
busDeviceMapper.insert(resources);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(BusDevice resources) {
BusDevice busDevice = getById(resources.getId());
busDevice.copy(resources);
busDeviceMapper.updateById(busDevice);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteAll(List<Long> ids) {
busDeviceMapper.deleteBatchIds(ids);
}
@Override
public String getQrCode(Long id) {
String code = (new Random().nextInt(900000) + 100000) + "";
String qrCode = weChatService.getQrCode(id, code);
redisUtils.set(CacheKey.QR_CODE + code, id, 1, TimeUnit.DAYS);
return qrCode;
}
@Override
public void binding(String code) {
if (null == redisUtils.get(CacheKey.QR_CODE + code)) {
throw new BadRequestException("二维码已过期,请联系管理员");
}
Long deviceId = Long.parseLong(redisUtils.get(CacheKey.QR_CODE + code).toString());
}
@Override
public List<BusTreeVo> getDeviceTree(Long pid) {
List<BusTreeVo> result;
if(pid != null && !pid.equals(0L)){
result = busCommandMapper.getCommandByDeviceId(pid);
} else {
result = busDeviceMapper.getDevice();
}
return result;
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2019-2025 Tz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import me.zhengjie.modules.system.domain.BusUser;
import me.zhengjie.modules.system.domain.BusUserCommand;
import me.zhengjie.modules.system.domain.BusUserDevice;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import me.zhengjie.modules.system.mapper.BusUserCommandMapper;
import me.zhengjie.modules.system.mapper.BusUserDeviceMapper;
import me.zhengjie.modules.system.service.BusUserDeviceService;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* @description 服务实现
* @author tz
* @date 2025-05-23
**/
@Service
@RequiredArgsConstructor
public class BusUserDeviceServiceImpl extends ServiceImpl<BusUserDeviceMapper, BusUserDevice> implements BusUserDeviceService {
private final BusUserDeviceMapper busUserDeviceMapper;
private final BusUserCommandMapper busUserCommandMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void updUserCommands(BusUser busUser) {
List<BusTreeVo> commands = busUser.getCommands();
if (null == commands || commands.isEmpty()) { return; }
List<BusTreeVo> userDevices = commands.stream().filter(i -> i.getId().contains("d")).collect(Collectors.toList());
List<BusTreeVo> userCommands = commands.stream().filter(i -> i.getId().contains("c")).collect(Collectors.toList());
List<BusUserDevice> oldUserDevices = busUserDeviceMapper.selectList(new LambdaQueryWrapper<BusUserDevice>().eq(BusUserDevice::getUserId, busUser.getId()));
List<BusUserCommand> oldUserCommands = busUserCommandMapper.selectList(new LambdaQueryWrapper<BusUserCommand>().eq(BusUserCommand::getUserId, busUser.getId()));
if (!oldUserDevices.isEmpty()) {
List<Long> delIds = oldUserDevices.stream().map(i -> i.getId()).collect(Collectors.toList());
busUserDeviceMapper.deleteBatchIds(delIds);
}
if (!oldUserCommands.isEmpty()) {
List<Long> delIds = oldUserCommands.stream().map(i -> i.getId()).collect(Collectors.toList());
busUserCommandMapper.deleteBatchIds(delIds);
}
if (!userDevices.isEmpty()) {
List<Long> deviceIds = userDevices.stream().map(i -> Long.parseLong(i.getId().replace("d", ""))).collect(Collectors.toList());
busUserDeviceMapper.bindingDevice(busUser.getId(), deviceIds);
}
if (!userCommands.isEmpty()) {
List<Long> commandIds = userCommands.stream().map(i -> Long.parseLong(i.getId().replace("c", ""))).collect(Collectors.toList());
busUserCommandMapper.bindingCommand(busUser.getId(), commandIds);
}
}
}

View File

@ -19,6 +19,7 @@ import me.zhengjie.modules.security.service.UserCacheManager;
import me.zhengjie.modules.security.service.dto.JwtUserDto;
import me.zhengjie.modules.system.domain.BusUser;
import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.system.domain.dto.BusTreeVo;
import me.zhengjie.modules.system.domain.dto.BusUserQueryCriteria;
import me.zhengjie.modules.system.mapper.BusUserMapper;
import me.zhengjie.modules.system.service.BusUserService;
@ -29,6 +30,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import me.zhengjie.utils.PageUtil;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import me.zhengjie.utils.PageResult;
@ -75,6 +78,11 @@ public class BusUserServiceImpl extends ServiceImpl<BusUserMapper, BusUser> impl
busUserMapper.deleteBatchIds(ids);
}
@Override
public List<BusTreeVo> getUserCommands(Long id) {
return busUserMapper.getUserCommands(id);
}
@Override
public JwtUserDto addUserCache(BusUser BusUser) {
String openId = BusUser.getToken();

View File

@ -0,0 +1,37 @@
package me.zhengjie.modules.system.service.socket;
import me.zhengjie.modules.security.config.handler.ThirdPartyWebSocketClient;
import org.springframework.stereotype.Service;
/**
* 设备管理服务提供与设备相关的业务逻辑
*/
@Service
public class DeviceService {
private final ThirdPartyWebSocketClient webSocketClient;
public DeviceService(ThirdPartyWebSocketClient webSocketClient) {
this.webSocketClient = webSocketClient;
}
/**
* 订阅设备状态变化
* @param deviceId 设备ID
*/
public void subscribeToDevice(String deviceId) {
webSocketClient.subscribeDevice(deviceId);
}
/**
* 向设备发送指令
* @param deviceId 设备ID
* @param command 指令内容
*/
public void sendDeviceCommand(String deviceId, String command) {
// 构建符合第三方服务格式的指令
String formattedCommand = String.format("{\"deviceId\": \"%s\", \"command\": \"%s\"}",
deviceId, command);
webSocketClient.sendCommand(formattedCommand);
}
}

View File

@ -104,8 +104,13 @@ swagger:
# 微信信息配置
weChat:
appId: 1231321
secret: 123123
appId: wx583d0c321ef43dfe
secret: 1b5eca11a1058361b0f14c5933ef6bd6
# socket地址
thirdparty:
ws:
url: ws://127.0.0.1:3030/websocket
# 文件存储路径
file:

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="me.zhengjie.modules.system.mapper.BusCommandMapper">
<resultMap id="BaseResultMap" type="me.zhengjie.modules.system.domain.BusDeviceCommand">
<id column="id" property="id"/>
<result column="device_id" property="deviceId"/>
<result column="command_name" property="commandName"/>
<result column="command_type" property="commandType"/>
<result column="description" property="description"/>
<result column="is_system" property="isSystem"/>
<result column="command_params" property="commandParams"/>
<result column="create_by" property="createBy"/>
<result column="update_by" property="updateBy"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<select id="getCommandByDeviceId" resultType="me.zhengjie.modules.system.domain.dto.BusTreeVo">
select CONCAT('c', id) id, CONCAT('d', device_id) pid, command_name name, description remark from bus_device_command where device_id = #{pid} order by id desc
</select>
<sql id="Base_Column_List">
id, device_id, command_name, command_type, description, is_system, command_params, create_by, update_by, create_time, update_time
</sql>
<select id="findAll" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from bus_device_command
<where>
</where>
order by id desc
</select>
</mapper>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="me.zhengjie.modules.system.mapper.BusDeviceMapper">
<resultMap id="BaseResultMap" type="me.zhengjie.modules.system.domain.BusDevice">
<id column="id" property="id"/>
<result column="device_sn" property="deviceSn"/>
<result column="model" property="model"/>
<result column="brand" property="brand"/>
<result column="firmware_version" property="firmwareVersion"/>
<result column="location" property="location"/>
<result column="status" property="status"/>
<result column="online_time" property="onlineTime"/>
<result column="offline_time" property="offlineTime"/>
<result column="total_print_time" property="totalPrintTime"/>
<result column="remark" property="remark"/>
<result column="type" property="type"/>
<result column="create_by" property="createBy"/>
<result column="update_by" property="updateBy"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<select id="getDevice" resultType="me.zhengjie.modules.system.domain.dto.BusTreeVo">
select CONCAT('d', id) id, 0 pid, CONCAT('品牌:', brand, ' | ', '型号:', model) name, CONCAT(location, '[', IF(type=1, '3D打印机', '摄像头'), ']') remark,
(select count(*) from bus_device_command dc where dc.device_id = bus_device.id) subCount
from bus_device order by id desc
</select>
<sql id="Base_Column_List">
id, device_sn, model, brand, firmware_version, location, status, online_time, offline_time, total_print_time, remark, type, create_by, update_by, create_time, update_time
</sql>
<select id="findAll" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from bus_device
<where>
<if test="criteria.deviceSn != null and criteria.deviceSn != ''"> and device_sn like concat('%', #{criteria.deviceSn}, '%') </if>
<if test="criteria.model != null and criteria.model != ''"> and model like concat('%', #{criteria.model}, '%') </if>
<if test="criteria.brand != null and criteria.brand != ''"> and brand like concat('%', #{criteria.brand}, '%') </if>
<if test="criteria.type != null"> and type = #{criteria.type} </if>
</where>
order by id desc
</select>
</mapper>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="me.zhengjie.modules.system.mapper.BusUserCommandMapper">
<resultMap id="BaseResultMap" type="me.zhengjie.modules.system.domain.BusUserCommand">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="device_id" property="deviceId"/>
<result column="command_id" property="commandId"/>
<result column="create_by" property="createBy"/>
<result column="update_by" property="updateBy"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<insert id="bindingCommand">
insert into bus_user_device_command (user_id, device_id, command_id)
select #{userId}, device_id, id from bus_device_command where id in
<foreach collection="commandIds" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</insert>
<sql id="Base_Column_List">
id, user_id, device_id, command_id, create_by, update_by, create_time, update_time
</sql>
<select id="findAll" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from bus_user_device_command
<where>
</where>
order by id desc
</select>
</mapper>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="me.zhengjie.modules.system.mapper.BusUserDeviceMapper">
<resultMap id="BaseResultMap" type="me.zhengjie.modules.system.domain.BusUserDevice">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="device_id" property="deviceId"/>
<result column="is_primary" property="isPrimary"/>
<result column="status" property="status"/>
<result column="create_by" property="createBy"/>
<result column="update_by" property="updateBy"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<insert id="bindingDevice">
insert into bus_user_device (user_id, device_id) values
<foreach collection="deviceIds" item="id" index="index" separator=",">
(#{userId}, #{id})
</foreach>
</insert>
<sql id="Base_Column_List">
id, user_id, device_id, is_primary, status, create_by, update_by, create_time, update_time
</sql>
<select id="findAll" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from bus_user_device
<where>
</where>
order by id desc
</select>
</mapper>

View File

@ -16,6 +16,12 @@
<result column="update_time" property="updateTime"/>
</resultMap>
<select id="getUserCommands" resultType="me.zhengjie.modules.system.domain.dto.BusTreeVo">
select CONCAT('d', d.id) id, 0 pid, CONCAT('品牌:', d.brand, ' | ', '型号:', d.model) name, CONCAT(d.location, '[', IF(d.type=1, '3D打印机', '摄像头'), ']') remark from bus_user_device ud inner join bus_device d on ud.device_id = d.id where ud.user_id = #{userId}
union all
select CONCAT('c', dc.id) id, CONCAT('d', dc.device_id) pid, dc.command_name name, dc.description remark from bus_user_device_command udc inner join bus_device_command dc on udc.command_id = dc.id where udc.user_id = #{userId}
</select>
<sql id="Base_Column_List">
id, real_name, nickname, avatar, phone, status, token, sex, create_by, update_by, create_time, update_time
</sql>
@ -25,6 +31,9 @@
<include refid="Base_Column_List"/>
from bus_user
<where>
<if test="criteria.nickname != null and criteria.nickname != ''"> and nickname like concat('%', #{criteria.nickname}, '%') </if>
<if test="criteria.phone != null and criteria.phone != ''"> and phone like concat('%', #{criteria.phone}, '%') </if>
<if test="criteria.status != null"> and status = #{criteria.status} </if>
</where>
order by id desc
</select>

View File

@ -4,7 +4,7 @@
<select id="getPieChatData" resultType="me.zhengjie.modules.system.domain.dto.DashboardDataVo">
select t.text, t.value from (
select case_type text, count(*) value from bus_law_enforcement GROUP BY case_type ORDER BY value desc
select CONCAT(d.brand, ' | ', d.model) text, count(cl.id) value from bus_device d LEFT JOIN bus_command_log cl on d.id = cl.device_id GROUP BY d.id ORDER BY value desc
) t limit 10
</select>
@ -47,13 +47,13 @@
select '前七天' text, count(1) value from sys_role where DATE(create_time) = CURDATE() - INTERVAL 7 DAY
</if>
<if test="type == 'law'">
select '前一天' text, count(1) value from bus_law_enforcement where DATE(create_time) = CURDATE() - INTERVAL 1 DAY UNION ALL
select '前两天' text, count(1) value from bus_law_enforcement where DATE(create_time) = CURDATE() - INTERVAL 2 DAY UNION ALL
select '前三天' text, count(1) value from bus_law_enforcement where DATE(create_time) = CURDATE() - INTERVAL 3 DAY UNION ALL
select '前四天' text, count(1) value from bus_law_enforcement where DATE(create_time) = CURDATE() - INTERVAL 4 DAY UNION ALL
select '前五天' text, count(1) value from bus_law_enforcement where DATE(create_time) = CURDATE() - INTERVAL 5 DAY UNION ALL
select '前六天' text, count(1) value from bus_law_enforcement where DATE(create_time) = CURDATE() - INTERVAL 6 DAY UNION ALL
select '前七天' text, count(1) value from bus_law_enforcement where DATE(create_time) = CURDATE() - INTERVAL 7 DAY
select '前一天' text, count(1) value from bus_command_log where DATE(create_time) = CURDATE() - INTERVAL 1 DAY UNION ALL
select '前两天' text, count(1) value from bus_command_log where DATE(create_time) = CURDATE() - INTERVAL 2 DAY UNION ALL
select '前三天' text, count(1) value from bus_command_log where DATE(create_time) = CURDATE() - INTERVAL 3 DAY UNION ALL
select '前四天' text, count(1) value from bus_command_log where DATE(create_time) = CURDATE() - INTERVAL 4 DAY UNION ALL
select '前五天' text, count(1) value from bus_command_log where DATE(create_time) = CURDATE() - INTERVAL 5 DAY UNION ALL
select '前六天' text, count(1) value from bus_command_log where DATE(create_time) = CURDATE() - INTERVAL 6 DAY UNION ALL
select '前七天' text, count(1) value from bus_command_log where DATE(create_time) = CURDATE() - INTERVAL 7 DAY
</if>
</select>

View File

@ -10,8 +10,8 @@
<modules>
<module>eladmin-common</module>
<module>eladmin-logging</module>
<module>eladmin-tools</module>
<module>eladmin-system</module>
<module>eladmin-tools</module>
<module>eladmin-generator</module>
</modules>