028554ac7af696c25663b05b736315e123L110">110 125
        ],
111 126
        mainWirelessCall: [
112
          { required: true, message: '手机不能为空', trigger: 'blur' }
127
          { required: true, message: '请输入11位手机号', trigger: 'blur' }
113 128
        ],
114 129
        facePicture: [
115 130
          { required: true, message: '图片不能为空', trigger: 'blur' }
@ -118,33 +133,201 @@ export default {
118 133
    }
119 134
  },
120 135
  computed: {
121
    // 显示/隐藏对话框
122
    visibled: {
123
      set(val) {
124
        this.$emit('visibled', val)
136
    isVisibled: {
137
      set (val) {
138
        this.$emit('update:visible', val)
125 139
      },
126
      get() {
140
      get () {
141
        this.getJobPositionList()
127 142
        return this.visible
128 143
      }
129 144
    }
130 145
  },
146
  watch: {
147
    'addUserModal.apartments' (val) {
148
      // 重置部门回显
149
      this.addUserModal.organizeCode = ''
150
      // 查询部门列表
151
      this.queryCycleChildOrg(val)
152
    }
153
  },
154
  mounted() {
155
    this.getJobPositionList()
156
  },
131 157
  methods: {
132 158
    // 确认新增用户
133 159
    submitModalData() {
134
      this.visible = false
135
      console.log(this.visibled)
136
      console.log(this.visible);
160
      this.$refs['addUserModal'].validate((valid) => {
161
        if (valid) {
162
          this.isVisibled = false
163
          let obj = {
164
            name: this.addUserModal.name, // 员工姓名
165
            code: this.addUserModal.code, // 员工编号
166
            organizeCode: this.addUserModal.organizeCode, // 部门CODE
167
            mainWirelessCall: this.addUserModal.mainWirelessCall, // 手机号码
168
            mainJobPosition: this.addUserModal.mainJobPosition, // 职务
169
            field1: this.addUserModal.facePicture, // 照片标识
170
            field4: this.addUserModal.apartments, // 公司ID
171
            birthday: Math.round(new Date() / 1000) // 生日(时间戳)
172
          }
173
          sysapi.creteEmployee({params: obj}).then(res => {
174
            console.log(res)
175
            if (res.data.success === true) {
176
              this.$Message.success('新增成功')
177
            } else {
178
              console.log(res.data.fail.message)
179
              this.$Message.success(res.data.fail.message)
180
            }
181
          })
182
        }
183
      })
137 184
    },
138 185
    // 重置表单数据
139 186
    resetModalData () {
140
      // this.$refs['activityForm'].resetFields()
187
      this.$refs['addUserModal'].resetFields()
141 188
      this.imgUrl = ''
142
      this.visibled = false
189
      this.isVisibled = false
190
    },
191
    // 查询职务列表
192
    async getJobPositionList() {
193
      const res = await sysapi.getJobPositionList()
194
      if (res.status === 200) {
195
        this.jobPoisitonList = res.data.data
196
      } else {
197
        this.$Message.danger('职务类型列表数据获取失败!')
198
      }
199
    },
200
    // 查询部门列表
201
    async queryCycleChildOrg (id) {
202
      const res = await sysapi.queryCycleChildOrg(id)
203
      if (res.status === 200) {
204
        const data = []
205
        // 递归 实现tree组件所需部门数据结构
206
        this.nextDepartment(res.data, data)
207
        // 深拷贝data
208
        const data1 = JSON.parse(JSON.stringify(data))
209
        // 用部门id映射关系代替code与parentCode父子映射关系
210
        data.forEach(item => {
211
          // 删除pid为"-1"的的pid属性,否则tree渲染的时候没有根节点渲染不出来
212
          if (item.pid === '-1') {
213
            delete item.pid
214
          }
215
          item.id = item.orgId
216
          data1.some((item1) => {
217
            if (item.pid === item1.id) {
218
              item.pid = item1.orgId
219
            } else {
220
              return false
221
            }
222
          })
223
        })
224
        // eslint-disable-next-line no-return-assign
225
        this.departmentTypesList = data.filter(item => item.data = item.id)
226
      } else {
227
        this.$Message.danger('部门类型列表数据获取失败!')
228
      }
229
    },
230
    startPickerDateChange() {
231
    },
232
    // 如果部门还有下级 递归
233
    nextDepartment (data, arr) {
234
      if (data.length > 0) {
235
        data.forEach(item => {
236
          arr.push({
237
            orgId: item.id + '',
238
            id: item.code,
239
            label: item.name,
240
            pid: item.parentCode
241
          })
242
          this.nextDepartment(item.departments, arr)
243
        })
244
      }
245
    },
246
    $_onDeleteIconClick (file) {
247
      this.$refs.uploader.removeFile(file)
248
    },
249
    $_onUploadRemoveFlie (file, fileList) {
250
      this.uploadList = fileList
251
    },
252
    $_onUploadPreviewFlie (file) {
253
      console.log(file)
254
    },
255
    $_onUploadProgress (e, file) {
256
      console.log(e)
257
      console.log(file)
258
    },
259
    $_onUploadSuccess (res, file) {
260
      this.imgUrl = res.data.data.toolPictureUrl
261
      this.addDeviceModalForm.pictureUrl = res.data.data.pictureUrl
262
      // 因为演示用的上传服务器返回数据格式的原因,这里模拟添加 url
263
      console.log(file)
264
      this.uploadList.push(file)
265
    },
266
    $_onUploadRemoveFile () { },
267
    $_onUploadExceededSize () { },
268
    $_onUploadError (errMsg, response, file) {
269
      console.log(file)
270
      console.log(errMsg.message)
271
      this.$Notice.warning({
272
        title: `文件${file.name}上传失败`,
273
        desc:
274
          '原因:' + errMsg
275
      })
276
    },
277
    $_onUploadFileExtError (file, files) {
278
      this.$Notice.warning({
279
        title: '文件格式不正确',
280
        desc:
281
          '文件 ' + file.name + ' 格式不正确,请上传 jpg 或 png 格式的图片。'
282
      })
283
      console.log(files)
284
    },
285
    $_onUploadFileExceededSize (file) {
286
      this.$Notice.warning({
287
        title: '超出文件大小限制',
288
        desc: '文件 ' + file.name + ' 太大,不能超过 2M。'
289
      })
290
    },
291
    $_onUploadFormatError () { },
292
    $_onUploadBeforeUpload () {
293
      const check = this.uploadList.length < 5
294
      if (!check) {
295
        this.$Notice.warning({
296
          title: '最多只能上传 5 张图片。'
297
        })
298
      }
299
      return check
143 300
    }
144 301
  }
145 302
}
146 303
</script>
147 304
148
<style>
149
305
<style lang="scss" scoped>
306
.demo-upload-list{
307
  .upload--drag{
308
  background-color:#FFFFFF
309
}
310
}
311
.picture-upload{
312
  display:flex;
313
  flex-direction:column;
314
  width:100%;
315
  height: 100%;
316
  justify-content: center;
317
  .upload-icon{
318
    align-items:center;
319
    justify-content:center;
320
    color:white;
321
    display: flex;
322
  }
323
  .aidicon.aidicon-plus-outline:before{
324
    width: 32px;
325
    height: 32px;
326
    display: flex;
327
    justify-content: center;
328
    align-items: center;
329
    background-color: #D0D0D0;
330
    border-radius:45px;
331
  }
332
}
150 333
</style>

+ 1 - 2
security-protection-platform/src/modules/usermana/index.vue

@ -64,7 +64,7 @@
64 64
        </t-pager>
65 65
      </div>
66 66
    </t-card>
67
    <add-user-modal :department-types-list="departmentTypesList" :company-list="companyTypesList" :visible.sync="addDeviceModal"></add-user-modal>
67
    <add-user-modal :company-list="companyTypesList" :visible.sync="addDeviceModal"></add-user-modal>
68 68
    <auditModal :auditshow.sync="auditshow" :auditid="auditid" :company-name="companyName" @refeshUserList="getUserList"></auditModal>
69 69
  </div>
70 70
</template>
@ -306,7 +306,6 @@ export default {
306 306
    // 显示新增设备对话框
307 307
    showAddDeviceModal () {
308 308
      this.addDeviceModal = true
309
      console.log(this.addDeviceModal)
310 309
    },
311 310
    async editDeviceData (id) {
312 311
      const res = await sysapi.getDeviceInfo(id)

+ 154 - 81
security-protection-platform/src/modules/videoSurveillance/index.vue

@ -11,18 +11,21 @@
11 11
        <t-option v-for="(item,index) in organizationList" :key="index" :value="item.id">{{ item.name }}</t-option>
12 12
      </t-select>
13 13
    </div>
14
    <div class="page-bottom">
15
      <div v-for="(item,index) in videoList" :key="index" :value="item.resourceToolId" style="width:400px;margin:24px 0px 0 24px;">
16
        <videoPlayer
17
          ref="videoPlayer"
18
          :options="videoOptions"
19
          :playsinline="true"
20
          class="vjs-custom-skin videoPlayer"
21
        />
22
        <!-- <rtmp-video :list="getVideoPlayList(item)" @goDistinguishRecord="goDistinguishRecord(item.resourceToolId)" @videoReplay="handleReview"></rtmp-video> -->
23
      </div>
14
15
    <div class="page-bottom grid-demo">
16
      <t-col v-for="(item,index) in videoList" :span="viewLayoutSpan" :key="index" :value="item.resourceToolId" style="margin-top:24px">
17
        <video-player
18
            :ref="'video'+index"
19
            :options="videoOptions[index]"
20
            :playsinline="false"
21
            :events="events"
22
            class="vjs-custom-skin videoPlayer"
23
            @dblclick="handlefullscreenchange"
24
        ></video-player>
25
      </t-col>
26
      <!-- <rtmp-video :list="getVideoPlayList(item)" @goDistinguishRecord="goDistinguishRecord(item.resourceToolId)" @videoReplay="handleReview"></rtmp-video> -->
24 27
    </div>
25
    <t-pager :page-size="videoPageSize" :current="videoCurrent" :total="videoTotal" :sizer-range="[ 5, 10, 20, 30 ]" class="pager" show-elevator @on-change="onChangeGate"></t-pager>
28
    <t-pager :page-size="videoPageSize" :current="videoCurrent" :total="videoTotal" :sizer-range="[ 1, 4, 9, 16 ]" class="pager" show-elevator @on-change="onChangeGate"></t-pager>
26 29
    <replay-dialog :list="replayList" :visibled.sync="showReplayDialog" @get-list="queryMonitorVideoLog" />
27 30
  </div>
28 31
</template>
@ -30,18 +33,15 @@
30 33
import sysapi from '@/api/videoSurveillance'
31 34
import RtmpVideo from './components/rtmpVideoPlay'
32 35
import ReplayDialog from './components/ReplayDialog'
33
import VideoPlayer from '@/components/VideoPlayer'
34 36
import formatDateTime from '@/utils/formatDateTime.js'
35 37
import videojs from 'video.js'
36 38
window.videojs = videojs
37 39
38
require('videojs-flash')
39
require('videojs-playlist')
40 40
require('video.js/dist/lang/zh-CN.js')
41 41
require('video.js/dist/video-js.min.css')
42 42
43 43
export default {
44
  components: { ReplayDialog, VideoPlayer, RtmpVideo },
44
  components: { ReplayDialog, RtmpVideo },
45 45
  filters: {
46 46
    handleText (value) {
47 47
      if (!value) return ''
@ -53,36 +53,9 @@ export default {
53 53
  },
54 54
  data () {
55 55
    return {
56
      videoOptions: {
57
        autoplay: true, // 如果true,浏览器准备好时开始回放。
58
        muted: true, // 默认情况下将会消除任何音频。
59
        loop: false, // 导致视频一结束就重新开始。
60
        preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
61
        language: 'zh-CN',
62
        aspectRatio: '13:10', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
63
        // fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
64
        // 是否流体自适应容器宽高
65
        fluid: true,
66
        // // 设置视频播放器的显示宽度(以像素为单位)
67
        // width: '100px',
68
        // // 设置视频播放器的显示高度(以像素为单位)
69
        // height: '300px',
70
        sources: [{
71
          withCredentials: false,
72
          type: 'application/x-mpegURL', // 这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目
73
          src: 'http://10.19.90.34:1080/live/1.m3u8' // url地址
74
        }],
75
        flash: { hls: { withCredentials: false } },
76
        html5: { hls: { withCredentials: false } },
77
        notSupportedMessage: '此视频暂无法播放,请稍后再试', // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
78
        controlBar: {
79
          timeDivider: false,
80
          // durationDisplay: true,
81
          remainingTimeDisplay: false,
82
          fullscreenToggle: true, // 全屏按钮,
83
          pictureInPictureToggle: false
84
        }
85
      },
56
      viewLayoutSpan: 3,
57
      events: ['fullscreenchange'],
58
      videoOptions: [],
86 59
      videoCurrent: 1, // 大门分页数据
87 60
      videoPageSize: 10, // 大门分页数据
88 61
      videoTotal: 0, // 大门分页总数
@ -105,16 +78,61 @@ export default {
105 78
      // endDay: '2020-12-19 20:14:00'
106 79
    }
107 80
  },
81
  computed: {
82
    player() {
83
      return this.$refs.videoPlayer1.player
84
    }
85
  },
108 86
  mounted () {
109 87
    this.getWindFiledList() // 获取风场列表
110 88
  },
111 89
  methods: {
112
    getVideoPlayList (item) {
113
      return [{
114
        fileId: item.videoUrl,
115
        fileType: 'rtmp/flv',
116
        id: item.resourceToolId
117
      }]
90
    // 修改视频组件控制栏的元素以及样式
91
    async createMyButton () {
92
      this.$nextTick(() => {
93
        const bars = document.querySelectorAll('.vjs-control-bar')
94
        let time = document.getElementsByClassName('vjs-current-time vjs-time-control vjs-control')
95
        let start = document.getElementsByClassName('vjs-play-control vjs-control vjs-button')
96
        let volume = document.getElementsByClassName('vjs-volume-panel vjs-control vjs-volume-panel-horizontal')
97
        let text = document.getElementsByClassName('vjs-live-control vjs-control')
98
        bars.forEach((item, index) => {
99
          time.forEach(item => {
100
            item.remove()
101
          })
102
          start.forEach(item => {
103
            item.remove()
104
          })
105
          volume.forEach(item => {
106
            item.remove()
107
          })
108
          text.forEach(item => {
109
            item.remove()
110
          })
111
          let txt = document.createElement('span')
112
          txt.innerHTML = '集控室(人脸识别)'
113
          txt.style.marginLeft = '12px'
114
          let btn = document.createElement('button')
115
          btn.style.color = 'white'
116
          btn.style.cursor = 'pointer'
117
          btn.style.marginLeft = 'auto'
118
          btn.className = 'aidicon aidicon-image-outline'
119
          btn.setAttribute('title', '识别记录')
120
          btn.addEventListener('click', () => {
121
            this.goDistinguishRecord()
122
          })
123
          let btn1 = document.createElement('button')
124
          btn1.style.color = 'white'
125
          btn1.style.cursor = 'pointer'
126
          btn1.className = 'aidicon aidicon-piechart-outline'
127
          btn1.setAttribute('title', '视频回放')
128
          btn1.addEventListener('click', () => {
129
            this.handleReview(this.videoList[index].id)
130
          })
131
          item.appendChild(txt)
132
          item.appendChild(btn)
133
          item.appendChild(btn1)
134
        })
135
      })
118 136
    },
119 137
    // 获取场景列表
120 138
    getSceneList () {
@ -124,6 +142,19 @@ export default {
124 142
        if (element.id === this.gateFieldData) {
125 143
          sysapi.getMonitorScene(this.organizationList[index].id).then((resp) => {
126 144
            this.sceneList = resp.data.data || []
145
            if (resp.data.data[0].monitorViewLayout === '1X1') {
146
              this.viewLayoutSpan = 10
147
              this.videoPageSize = 1
148
            } else if (resp.data.data[0].monitorViewLayout === '2X2') {
149
              this.viewLayoutSpan = 6
150
              this.videoPageSize = 4
151
            } else if (resp.data.data[0].monitorViewLayout === '3X3') {
152
              this.viewLayoutSpan = 4
153
              this.videoPageSize = 9
154
            } else if (resp.data.data[0].monitorViewLayout === '4X4') {
155
              this.viewLayoutSpan = 3
156
              this.videoPageSize = 16
157
            }
127 158
            this.tabClick(this.sceneList[0].monitorSceneId)
128 159
          })
129 160
        }
@ -170,15 +201,51 @@ export default {
170 201
        console.log(err)
171 202
      })
172 203
    },
204
    handlefullscreenchange(val) {
205
      // 因为我是又封装了一个组件,打印val会有相应所需的属性,全屏状态为:isFullscreen_
206
      val.isFullscreen_ = !val.isFullscreen_
207
      // this.$emit('fullscreenchange', val)
208
    },
173 209
    // 获得风场大门数据
174 210
    getVideoSurveillanceData () {
175 211
      var id = this.tabId
176 212
      this.paramsObj.page = this.videoCurrent
177
      sysapi.getVideoSurveillanceDataForPage({ params: { monitorSceneId: id, pageNumber: this.videoCurrent, pageSize: this.videoPageSize} })
178
      .then(res => {
179
        debugger
180
        this.videoList = res.data.data.data
181
        this.videoTotal = res.data.data.total
213
      sysapi.getVideoSurveillanceData({ params: { monitorSceneId: id } }).then(res => {
214
        this.videoList = res.data.data
215
        for (let i in res.data.data) {
216
          let obj = {
217
            autoplay: true, // 如果true,浏览器准备好时开始回放。
218
            muted: true, // 默认情况下将会消除任何音频。
219
            loop: false, // 导致视频一结束就重新开始。
220
            preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
221
            language: 'zh-CN',
222
            aspectRatio: '13:10', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
223
            // fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
224
            // 是否流体自适应容器宽高
225
            fluid: true,
226
            // // 设置视频播放器的显示宽度(以像素为单位)
227
            // width: '100px',
228
            // // 设置视频播放器的显示高度(以像素为单位)
229
            // height: '300px',
230
            sources: [{
231
              withCredentials: false,
232
              type: 'application/x-mpegURL', // 这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目
233
              src: res.data.data[i].videoUrl // url地址
234
            }],
235
            flash: { hls: { withCredentials: false } },
236
            html5: { hls: { withCredentials: false } },
237
            notSupportedMessage: '此视频暂无法播放,请稍后再试', // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
238
            controlBar: {
239
              timeDivider: false,
240
              durationDisplay: false,
241
              remainingTimeDisplay: false,
242
              fullscreenToggle: true, // 全屏按钮,
243
              pictureInPictureToggle: false
244
            }
245
          }
246
          this.videoOptions.push(obj)
247
        }
248
        this.createMyButton()
182 249
      })
183 250
    },
184 251
    // 进入识别记录界面
@ -247,37 +314,43 @@ export default {
247 314
      }
248 315
    }
249 316
  }
250
  .pager {
317
.pager {
251 318
    margin-right: auto;
252 319
    margin: 21px 0px 24px 0;
253 320
    float: right;
254 321
  }
255
  .btn-primary,
256
  .radio-group-button .form-radio:checked,
257
  .radio-group-button .form-radio[checked] {
258
    color: #0089d4;
259
    background-color: #fff;
260
    border: 1px solid #0089d4;
261
  }
262
  .btn-secondary,
263
  .radio-group-button .form-radio,
264
  .checkbox-group--button .form-checkbox .form-checkbox__inner,
265
  .btn-dashed-secondary,
266
  .btn-outline-secondary {
267
    background: none;
268
  }
269
  .video-js .vjs-control:focus, .video-js .vjs-control:focus:before, .video-js .vjs-control:hover:before{
270
    color:#0089d4;
271
    text-shadow: none;
272
  }
273
  .vjs-control-bar{
274
    justify-content: flex-end;
275
  }
276
  .video-js .vjs-control{
322
.vjs-custom-skin > .video-js .vjs-control-bar .vjs-fullscreen-control{
323
  height:38px !important;
324
}
325
.video-js .vjs-control{
277 326
    cursor: pointer;
327
    width: 28px;
328
    margin-right:12px;
278 329
  }
279
.video-js .vjs-fullscreen-control{
280
  order:2;
330
.vjs-control-bar{
331
    display: flex;
332
    justify-content: center;
333
    align-items: center;
334
}
335
.video-js button{
336
  font-size:16px;
337
}
338
.aidicon-image-outline:hover{
339
  color: #0089d4 !important;
340
}
341
.aidicon-piechart-outline:hover{
342
  color: #0089d4 !important;
343
}
344
.vjs-icon-placeholder:hover{
345
  color: #0089d4 !important;
346
}.vjs-icon-placeholder{
347
  font-size: 13px;
348
  display: flex;
349
  justify-content: center;
350
  align-items: center;
351
}
352
.vjs-tech {
353
  pointer-events: none;
281 354
}
282 355
}
283 356
</style>

+ 1 - 1
security-protection-service/src/main/java/com/ai/bss/security/protection/controller/EmployeeManagementController.java

@ -133,7 +133,7 @@ public class EmployeeManagementController {
133 133
	@ResponseBody
134 134
	@RequestMapping("/uploadEmployeePicture")
135 135
	public CommonResponse<Map<String, String>> uploadEmployeePicture(
136
			@RequestParam(value = "picture") MultipartFile meFile, @RequestParam(required = false) String pictureUrl)
136
			@RequestParam(value = "file") MultipartFile meFile, @RequestParam(required = false) String pictureUrl)
137 137
			throws Exception {
138 138
		if (meFile == null || StringUtils.isEmpty(meFile.getOriginalFilename())) {
139 139
			return CommonResponse.fail("500", "上传失败");

+ 16 - 1
security-protection-service/src/main/java/com/ai/bss/security/protection/controller/InAndOutRecordController.java

@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestParam;
19 19
import org.springframework.web.bind.annotation.ResponseBody;
20 20
21 21
import java.util.HashMap;
22
import java.util.List;
22 23
import java.util.Map;
23 24
24 25
/**
@ -72,5 +73,19 @@ public class InAndOutRecordController {
72 73
73 74
		return aiIdenLogManageService.queryOneAiIdenLog(aiIdenLogId);
74 75
	}
75
76
	
77
	/**
78
	 * 首页查询进出记录
79
	 * @return
80
	 * @throws Exception
81
	 */
82
	@ResponseBody
83
	@RequestMapping("/queryHomeLastInAndOutRecord")
84
	public CommonResponse<List<Map<String, Object>>> queryHomeLastInAndOutRecord(@RequestParam String monitorSceneId) throws Exception {
85
		Map<String, String> params=new HashMap<String, String>();
86
		params.put("sceneId", monitorSceneId);
87
		params.put("aiIdenModel", EbcConstant.AI_MODEL_FACE);
88
		
89
		return aiIdenLogManageService.selectLastAiIdenLog(params);
90
	}
76 91
}

+ 2 - 2
security-protection-service/src/main/java/com/ai/bss/security/protection/controller/ResourceToolManageController.java

@ -154,7 +154,7 @@ public class ResourceToolManageController {
154 154
	@ResponseBody
155 155
	@RequestMapping("/uploadResourceToolPicture")
156 156
	public CommonResponse<Map<String, String>> uploadResourceToolPicture(
157
			@RequestParam(value = "picture") MultipartFile meFile, @RequestParam(required = false) String pictureUrl)
157
			@RequestParam(value = "file") MultipartFile meFile, @RequestParam(required = false) String pictureUrl)
158 158
			throws Exception {
159 159
		if (meFile == null || StringUtils.isEmpty(meFile.getOriginalFilename())) {
160 160
			return CommonResponse.fail("500", "上传失败");
@ -166,7 +166,7 @@ public class ResourceToolManageController {
166 166
	}
167 167
168 168
	/**
169
	 * 根据设备CODE获取场景、AI任务等相关信息
169
	 * 根据设备CODE获取AI任务等相关信息
170 170
	 * @param meFile
171 171
	 * @return
172 172
	 * @throws Exception

+ 122 - 12
security-protection-service/src/main/java/com/ai/bss/security/protection/service/impl/AiIdenLogManageServiceImpl.java

@ -1,5 +1,6 @@
1 1
package com.ai.bss.security.protection.service.impl;
2 2
3
import java.math.BigDecimal;
3 4
import java.util.ArrayList;
4 5
import java.util.HashMap;
5 6
import java.util.List;
@ -13,6 +14,7 @@ import org.springframework.util.CollectionUtils;
13 14
import com.ai.abc.api.model.CommonRequest;
14 15
import com.ai.abc.api.model.CommonResponse;
15 16
import com.ai.bss.components.common.model.PageBean;
17
import com.ai.bss.components.minio.config.MinioConfig;
16 18
import com.ai.bss.security.protection.model.EbcMonitorVideoLog;
17 19
import com.ai.bss.security.protection.service.interfaces.AiIdenLogManageService;
18 20
import com.ai.bss.security.protection.service.interfaces.CharSpecService;
@ -27,6 +29,7 @@ import com.ai.bss.work.safety.service.api.AiTaskCommand;
27 29
import com.ai.bss.work.safety.service.api.AiTaskQuery;
28 30
import com.alibaba.fastjson.JSON;
29 31
import com.alibaba.fastjson.JSONArray;
32
import com.alibaba.fastjson.JSONObject;
30 33
import com.alibaba.fastjson.TypeReference;
31 34
32 35
@Service
@ -50,6 +53,9 @@ public class AiIdenLogManageServiceImpl implements AiIdenLogManageService {
50 53
	@Autowired
51 54
	private MonitorVideoLogManageService monitorVideoLogManageService;
52 55
56
	@Autowired
57
	private MinioConfig minioConfig;
58
53 59
	@Override
54 60
	public CommonResponse<PageBean<Map<String, Object>>> queryPageAiIdenLog(Map<String, Object> params, int pageNumber,
55 61
			int pageSize) throws Exception {
@ -68,14 +74,29 @@ public class AiIdenLogManageServiceImpl implements AiIdenLogManageService {
68 74
		for (Map<String, Object> dataMap : response.getData().getData()) {
69 75
			dataMap.put("taskExecuteTime", DateUtil.formatObjectToDate(dataMap.get("taskExecuteTime")));
70 76
			dataMap.put("idenPictureSnapDate", DateUtil.formatObjectToDate(dataMap.get("idenPictureSnapDate")));
71
			
77
72 78
			// 结果详情
73
			String idenResultArr = dataMap.get("idenResult") == null ? "" : String.valueOf(dataMap.get("idenResult"));
74
			if (StringUtils.isNotEmpty(idenResultArr)) {
75
				List<Map<String, String>> idenResultList = JSONArray.parseObject(idenResultArr, List.class);
76
				if (!CollectionUtils.isEmpty(idenResultList)) {
77
					dataMap.put("idenResult", idenResultList.get(0));
78
					//dataMap.putAll(idenResultList.get(0));
79
			String idenResultString = dataMap.get("idenResult") == null ? ""
80
					: String.valueOf(dataMap.get("idenResult"));
81
			if (StringUtils.isNotEmpty(idenResultString)) {
82
				Object idenResultObject = JSON.parse(idenResultString);
83
				if (idenResultObject instanceof JSONObject) {
84
					Map<String, String> idenResultMap = JSON.parseObject(idenResultString, Map.class);
85
					dataMap.putAll(idenResultMap);
86
87
					if (idenResultMap.get("simi") != null) {
88
						Object simiObject = idenResultMap.get("simi");
89
						BigDecimal simiBigDecimal = new BigDecimal(String.valueOf(simiObject));
90
						float simi = simiBigDecimal.multiply(new BigDecimal("100"))
91
								.setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
92
						dataMap.put("simi", simi + "%");
93
					}
94
95
				} else if (idenResultObject instanceof JSONArray) {
96
					List<Map<String, String>> idenResultList = JSONArray.parseObject(idenResultString, List.class);
97
					if (!CollectionUtils.isEmpty(idenResultList)) {
98
						dataMap.putAll(idenResultList.get(0));
99
					}
79 100
				}
80 101
			}
81 102
@ -92,7 +113,7 @@ public class AiIdenLogManageServiceImpl implements AiIdenLogManageService {
92 113
					dataMap.put("employeeName", employeeResponse.getData().getName());
93 114
					dataMap.put("employeeCode", employeeResponse.getData().getCode());
94 115
95
					dataMap.put("companyName", employeeResponse.getData().getField4());
116
					dataMap.put("companyId", employeeResponse.getData().getField4());
96 117
					dataMap.put("organizationCode", employeeResponse.getData().getOrganizeCode());
97 118
					dataMap.put("organizationName", employeeResponse.getData().getOrgName());
98 119
@ -133,7 +154,7 @@ public class AiIdenLogManageServiceImpl implements AiIdenLogManageService {
133 154
			String pictureUrl = String.valueOf(dataMap.get("idenPictureUrl"));
134 155
			if (pictureUrl.length() > 20 && pictureUrl.lastIndexOf(".") > 18
135 156
					&& pictureUrl.lastIndexOf(EbcConstant.AI_MONITOR_FILE_PREFIX_PICTURE) > 0) {
136
				
157
137 158
				int picturePrefixIndex = pictureUrl.lastIndexOf(EbcConstant.AI_MONITOR_FILE_PREFIX_PICTURE)
138 159
						+ EbcConstant.AI_MONITOR_FILE_PREFIX_PICTURE.length();
139 160
				String fileName = pictureUrl.substring(picturePrefixIndex, picturePrefixIndex + 4) + "-"
@ -165,11 +186,29 @@ public class AiIdenLogManageServiceImpl implements AiIdenLogManageService {
165 186
				new TypeReference<Map<String, String>>() {
166 187
				});
167 188
189
		aiIdenLogInfoMap.put("taskExecuteTime",
190
				DateUtil.formatDate(Long.valueOf(aiIdenLogInfoMap.get("taskExecuteTime"))));
191
168 192
		// 结果详情
169 193
		if (StringUtils.isNotEmpty(aiIdenLog.getIdenResult())) {
170
			List<Map<String, String>> idenResultList = JSONArray.parseObject(aiIdenLog.getIdenResult(), List.class);
171
			if (!CollectionUtils.isEmpty(idenResultList)) {
172
				aiIdenLogInfoMap.putAll(idenResultList.get(0));
194
			Object idenResultObject = JSON.parse(aiIdenLog.getIdenResult());
195
			if (idenResultObject instanceof JSONObject) {
196
				Map<String, String> idenResultMap = JSON.parseObject(aiIdenLog.getIdenResult(), Map.class);
197
				aiIdenLogInfoMap.putAll(idenResultMap);
198
199
				if (idenResultMap.get("simi") != null) {
200
					Object simiObject = idenResultMap.get("simi");
201
					BigDecimal simiBigDecimal = new BigDecimal(String.valueOf(simiObject));
202
					float simi = simiBigDecimal.multiply(new BigDecimal("100")).setScale(2, BigDecimal.ROUND_HALF_UP)
203
							.floatValue();
204
					aiIdenLogInfoMap.put("simi", simi + "%");
205
				}
206
207
			} else if (idenResultObject instanceof JSONArray) {
208
				List<Map<String, String>> idenResultList = JSONArray.parseObject(aiIdenLog.getIdenResult(), List.class);
209
				if (!CollectionUtils.isEmpty(idenResultList)) {
210
					aiIdenLogInfoMap.putAll(idenResultList.get(0));
211
				}
173 212
			}
174 213
		}
175 214
@ -186,6 +225,10 @@ public class AiIdenLogManageServiceImpl implements AiIdenLogManageService {
186 225
				aiIdenLogInfoMap.put("employeeName", employeeResponse.getData().getName());
187 226
				aiIdenLogInfoMap.put("employeeCode", employeeResponse.getData().getCode());
188 227
228
				String headerImageName = employeeResponse.getData().getField1();
229
				String headerImage = uploadFileService.getFileUrl(headerImageName, minioConfig.getBucketHeaderImage());
230
				aiIdenLogInfoMap.put("headerImage", headerImage);
231
189 232
				/*
190 233
				String employeePosition = employeeResponse.getData().getMainJobPosition();
191 234
				aiIdenLogInfoMap.put("employeePosition", employeePosition);
@ -226,6 +269,73 @@ public class AiIdenLogManageServiceImpl implements AiIdenLogManageService {
226 269
	}
227 270
228 271
	@Override
272
	public CommonResponse<List<Map<String, Object>>> selectLastAiIdenLog(Map<String, String> params) throws Exception {
273
		CommonRequest<Map<String, String>> request = new CommonRequest<Map<String, String>>(params);
274
		CommonResponse<List<Map<String, Object>>> response = aiTaskQuery
275
				.selectAiIdenLogBySceneIdAndAiIdenModel(request);
276
277
		if (response == null || response.getData() == null || CollectionUtils.isEmpty(response.getData())) {
278
			return response;
279
		}
280
281
		for (Map<String, Object> dataMap : response.getData()) {
282
			dataMap.put("taskExecuteTime", DateUtil.formatObjectToDate(dataMap.get("taskExecuteTime")));
283
284
			String idenPictureName = String.valueOf(dataMap.get("idenPictureUrl"));
285
			String idenPictureUrl = uploadFileService.getFileUrl(idenPictureName);
286
			dataMap.put("idenPictureUrl", idenPictureUrl);
287
288
			// 结果详情
289
			String idenResultString = dataMap.get("idenResult") == null ? ""
290
					: String.valueOf(dataMap.get("idenResult"));
291
			if (StringUtils.isNotEmpty(idenResultString)) {
292
				Object idenResultObject = JSON.parse(idenResultString);
293
				if (idenResultObject instanceof JSONObject) {
294
					Map<String, String> idenResultMap = JSON.parseObject(idenResultString, Map.class);
295
					dataMap.putAll(idenResultMap);
296
297
					if (idenResultMap.get("simi") != null) {
298
						Object simiObject = idenResultMap.get("simi");
299
						BigDecimal simiBigDecimal = new BigDecimal(String.valueOf(simiObject));
300
						float simi = simiBigDecimal.multiply(new BigDecimal("100"))
301
								.setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
302
						dataMap.put("simi", simi + "%");
303
					}
304
305
				} else if (idenResultObject instanceof JSONArray) {
306
					List<Map<String, String>> idenResultList = JSONArray.parseObject(idenResultString, List.class);
307
					if (!CollectionUtils.isEmpty(idenResultList)) {
308
						dataMap.putAll(idenResultList.get(0));
309
					}
310
				}
311
			}
312
313
			// 员工信息
314
			if (dataMap.get("relateEmployeeRoleId") != null) {
315
				long relateEmployeeRoleId = Long.parseLong(String.valueOf(dataMap.get("relateEmployeeRoleId")));
316
317
				EmployeeDto employeeDto = new EmployeeDto();
318
				employeeDto.setId(relateEmployeeRoleId);
319
				CommonRequest<EmployeeDto> requestEmployeeDto = CommonRequest.<EmployeeDto>builder().data(employeeDto)
320
						.build();
321
				CommonResponse<EmployeeDto> employeeResponse = employeeService.queryEmployee(requestEmployeeDto);
322
323
				if (employeeResponse.getData() != null) {
324
					dataMap.put("employeeName", employeeResponse.getData().getName());
325
					dataMap.put("employeeCode", employeeResponse.getData().getCode());
326
327
					String headerImageName = employeeResponse.getData().getField1();
328
					String headerImage = uploadFileService.getFileUrl(headerImageName,
329
							minioConfig.getBucketHeaderImage());
330
					dataMap.put("headerImage", headerImage);
331
				}
332
			}
333
		}
334
335
		return response;
336
	}
337
338
	@Override
229 339
	public CommonResponse<AiIdenLog> createAiIdenLog(AiIdenLog aiIdenLog) throws Exception {
230 340
		CommonRequest<AiIdenLog> aiIdenLogRequest = new CommonRequest<AiIdenLog>(aiIdenLog);
231 341
		return aiTaskCommand.createAiIdenLog(aiIdenLogRequest);

+ 8 - 0
security-protection-service/src/main/java/com/ai/bss/security/protection/service/interfaces/AiIdenLogManageService.java

@ -45,6 +45,14 @@ public interface AiIdenLogManageService {
45 45
	CommonResponse<Map<String, Object>> queryOneAiIdenLog(Long aiIdenLogId) throws Exception;
46 46
47 47
	/**
48
	 * 查询所有设备最后一个识别记录
49
	 * @param params
50
	 * @return
51
	 * @throws Exception
52
	 */
53
	CommonResponse<List<Map<String, Object>>> selectLastAiIdenLog(Map<String, String> params) throws Exception;
54
	
55
	/**
48 56
	 * 创建AI执行结果
49 57
	 * @param aiIdenLog
50 58
	 * @return

+ 1 - 1
security-protection-service/src/main/java/com/ai/bss/security/protection/service/interfaces/ResourceToolManageService.java

@ -84,7 +84,7 @@ public interface ResourceToolManageService {
84 84
	Map<String, String> uploadResourceToolPicture(MultipartFile meFile, String pictureUrl) throws Exception;
85 85
	
86 86
	/**
87
	 * 根据设备CODE获取场景、AI任务等相关信息
87
	 * 根据设备CODE获取AI任务等相关信息
88 88
	 * @param resourceToolCode
89 89
	 * @return
90 90
	 */

+ 30 - 11
security-protection-service/src/main/java/com/ai/bss/security/protection/service/task/AiResultRecordKafkaTask.java

@ -14,8 +14,9 @@ import com.ai.abc.api.model.CommonRequest;
14 14
import com.ai.abc.api.model.CommonResponse;
15 15
import com.ai.abc.util.JsonUtils;
16 16
import com.ai.bss.security.protection.service.interfaces.AiIdenLogManageService;
17
import com.ai.bss.security.protection.service.interfaces.ResourceToolManageService;
18 17
import com.ai.bss.security.protection.utils.EbcConstant;
18
import com.ai.bss.user.dto.EmployeeDto;
19
import com.ai.bss.user.service.api.EmployeeService;
19 20
import com.ai.bss.work.safety.model.AiIdenLog;
20 21
import com.ai.bss.work.safety.service.api.AiTaskCommand;
21 22
import com.ai.bss.work.safety.service.api.MonitorSceneQuery;
@ -38,14 +39,14 @@ public class AiResultRecordKafkaTask {
38 39
	private AiTaskCommand aiTaskCommand;
39 40
40 41
	@Autowired
41
	private ResourceToolManageService resourceToolManageService;
42
43
	@Autowired
44 42
	private MonitorSceneQuery monitorSceneQuery;
45 43
46 44
	@Autowired
47 45
	private AiIdenLogManageService aiIdenLogManageService;
48 46
47
	@Autowired
48
	private EmployeeService employeeService;
49
49 50
	@KafkaListener(containerFactory = "kafkaBatchListener6", topics = "${kafka.topic.aitask:topic_ai_task}", groupId = "ai_group")
50 51
	public void AiResultRecordListener(ConsumerRecord<String, String> records, Acknowledgment ack) throws Throwable {
51 52
		try {
@ -54,11 +55,15 @@ public class AiResultRecordKafkaTask {
54 55
			String message = records.value();
55 56
			log.info("已接AI任务执行结果消息,消息为:" + message);
56 57
58
			AiIdenLog aiIdenLog = JSON.parseObject(message, new TypeReference<AiIdenLog>() {
59
			});
60
57 61
			JSONObject messageJson = JSONObject.parseObject(message);
58 62
			String resourceToolId = messageJson.getString("resourceToolId");
59 63
			String idenResultType = messageJson.getString("idenResultType");
64
			Long relateEmployeeRoleId = messageJson.getLong("relateEmployeeRoleId");
60 65
61
			// 查询相关数据
66
			// 关联场景信息
62 67
			CommonResponse<List<Map<String, Object>>> sceneTerminalRelResponse = monitorSceneQuery
63 68
					.selectSceneTerminalRel(new CommonRequest<Long>(Long.valueOf(resourceToolId)));
64 69
			if (sceneTerminalRelResponse == null || CollectionUtils.isEmpty(sceneTerminalRelResponse.getData())) {
@ -68,9 +73,6 @@ public class AiResultRecordKafkaTask {
68 73
69 74
			Map<String, Object> sceneTerminalRelMap = sceneTerminalRelResponse.getData().get(0);
70 75
71
			// 封装数据
72
			AiIdenLog aiIdenLog = JSON.parseObject(message, new TypeReference<AiIdenLog>() {
73
			});
74 76
			aiIdenLog.setMonitorSceneId(sceneTerminalRelMap.get("monitorSceneId") == null ? ""
75 77
					: String.valueOf(sceneTerminalRelMap.get("monitorSceneId")));
76 78
			aiIdenLog.setMonitorSceneName(sceneTerminalRelMap.get("monitorSceneName") == null ? ""
@ -82,9 +84,26 @@ public class AiResultRecordKafkaTask {
82 84
			aiIdenLog.setOrganizationId(
83 85
					sceneTerminalRelMap.get("orgId") == null ? "" : String.valueOf(sceneTerminalRelMap.get("orgId")));
84 86
85
			// TODO 人员信息暂时默认
86
			aiIdenLog.setRelateEmployeeRoleId("201613310867");
87
			aiIdenLog.setRelateEmployeeRoleName("王浩");
87
			// 关联员工信息
88
			EmployeeDto employeeDto = null;
89
			if (relateEmployeeRoleId != null) {
90
				employeeDto = new EmployeeDto();
91
				employeeDto.setId(relateEmployeeRoleId);
92
				CommonRequest<EmployeeDto> employeeDtoRequest = new CommonRequest<EmployeeDto>(employeeDto);
93
				CommonResponse<EmployeeDto> employeeDtoResponse = employeeService.queryEmployee(employeeDtoRequest);
94
				if (employeeDtoResponse == null || employeeDtoResponse.getData() == null) {
95
					employeeDto = null;
96
				} else {
97
					employeeDto = employeeDtoResponse.getData();
98
				}
99
			}
100
			if (employeeDto != null) {
101
				aiIdenLog.setRelateEmployeeRoleName(employeeDto.getName());
102
				aiIdenLog.setOrganizationId(String.valueOf(employeeDto.getOrganizeId()));
103
			} else {
104
				aiIdenLog.setRelateEmployeeRoleId("-1");
105
				aiIdenLog.setRelateEmployeeRoleName(null);
106
			}
88 107
89 108
			// 执行操作
90 109
			CommonResponse<AiIdenLog> aiIdenLogRunningResult = aiIdenLogManageService.createAiIdenLog(aiIdenLog);

static - Nuosi Git Service

static

index-mobile.html 7.5KB

    <!DOCTYPE html> <html lang="en"> <head> <title>AISWare IoT-物联网设备管理平台</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"> <meta name="description" content="AISWare IoT"> <meta name="description" content="物联网设备管理平台"> <link rel="shortcut icon" href="favicon.ico" > <link rel="icon" type="image/gif" href="" > <script src="js/browser.js"></script> <script src="js/index-mobile-check.js"></script> <link rel="stylesheet" type="text/css" href="mobile/css-mobile/ipuUI.css"> <link rel="stylesheet" type="text/css" href="mobile/css-mobile/index-mobile.css"> <script src="js/jquery-2.2.4.js"></script> <script src="mobile/js/index-mobile.js"></script> </head> <body class="pages-download"> <!-- 页面头部 --> <div class="app-head"> <div class="main-content"> <div class="app-logo"> <div class="app-logo-left"></div> <div class="app-nav"></div> </div> <div class="app-logo-center"></div> <div class="app-name">AISWare IoT</div> <div class="app-desc">物联网设备管理平台</div> <div class="app-down-link"><a href="javascript:;">立即使用</a></div> <div class="demo-img"></div> </div> </div> <!-- 平台特性 --> <div class="app-feature-info"> <div class="main-content app-feature-info-bg"> <div class="display-content"> <div class="app-feature-info-title"> 物联网设备平台管理特性 </div> <div class="app-feature-info-content"> <div class="app-feature-item app-feature-item-aqkk"> <div class="ipu-flex-middle app-feature-item-head"> <div class="flag "></div> <div class="title">安全可靠</div> </div> <div class="desc"> 支持私有化部署,安全保障企业核心业务和数据的安全,在设备侧,传输层和云端构建多重防护和安全保障 </div> </div> <div class="app-feature-item app-feature-item-dxysp"> <div class="ipu-flex-middle app-feature-item-head"> <div class="flag"></div> <div class="title">多协议适配</div> </div> <div class="desc"> 提供多种协议的设备接入能力,如MQTT、CoAP、Modbus、LwM2M等,既能满足设备长连接实时性的需求,也能满足设备短连接降低功耗的需求 </div> </div> <div class="app-feature-item app-feature-item-ddywjk"> <div class="ipu-flex-middle app-feature-item-head"> <div class="flag "></div> <div class="title ">多端运维监控</div> </div> <div class="desc"> 通过加密通道传输和私有化部署,保证数据传输安全,全面保护企业信息安全 </div> </div> <div class="app-feature-item app-feature-item-gbfsscl"> <div class="ipu-flex-middle app-feature-item-head"> <div class="flag "></div> <div class="title ">高并发实时处理</div> </div> <div class="desc"> 提供高性能、大容量、电信级集群管能力,支持实时处理百万级设备连接和千万级消息并发带来的海量数据 </div> </div> <div class="app-feature-item app-feature-item-glgx"> <div class="ipu-flex-middle app-feature-item-head"> <div class="flag "></div> <div class="title ">管理高效</div> </div> <div class="desc"> 可视化、实施监控运行状态,异常报警及时处理设备故障;低代码、高效、快速生成pc和手机报表,提升10倍效率 </div> </div> <div class="app-feature-item app-feature-item-yssycz"> <div class="ipu-flex-middle app-feature-item-head"> <div class="flag "></div> <div class="title ">易上手易操作</div> </div> <div class="desc"> 平台提供产品管理、设备管理、数据管理、设备管控、统计分析5大模块功能,形成设备对接物联网平台的业务闭环,功能清晰,简洁易用,易上手易操作 </div> </div> </div> </div> </div> </div> <!-- 应用场景 --> <div class="app-scene"> <div class="main-content"> <div class="app-scene-item app-scene-item-sjjx"> <div class="display-img"></div> <div class="display-content"> <div class="ipu-flex-middle app-scene-item-head"> <div class="flag"></div> <div class="title">数据解析</div> </div> <div class="desc"> 物联网应用直接获取平台解析后的设备数据,应用开发者无需关注设备底层信息,大大降低开发难度。通过数据协议解析引擎,快速实现不同数据格式设备数据动态解析的需求 </div> </div> </div> <div class="app-scene-item app-scene-item-sbxt"> <div class="display-img"></div> <div class="display-content"> <div class="ipu-flex-middle app-scene-item-head"> <div class="flag"></div> <div class="title">设备协同</div> </div> <div class="desc"> 可视化方式定义设备之间联动规则,快速建立多个设备之间的协同规则,实现丰富的设备联动场景 </div> </div> </div> <div class="app-scene-item app-scene-item-gjzx"> <div class="display-img"></div> <div class="display-content"> <div class="ipu-flex-middle app-scene-item-head"> <div class="flag"></div> <div class="title">告警中心</div> </div> <div class="desc"> 简单易用的自动化告警规则配置,更加智能地监控设备稳定运行丰富多样的告警通知方式,实现告警信息的实时高效准确送达 </div> </div> </div> <div class="app-scene-item app-scene-item-3d"> <div class="display-img"></div> <div class="display-content"> <div class="ipu-flex-middle app-scene-item-head"> <div class="flag"></div> <div class="title">3D可视化</div> </div> <div class="desc"> 更直观生动的可视化形式呈现现实世界的繁杂数据,切实提升监控管理水平,组件拖拽式的可视化开发,快速实现数据上屏,诠释数据立体之美 </div> </div> </div> </div> </div> <div class="app-down" id="download"> <div class="main-content"> <div class="title"> 立即使用AISWare loT </div> <div class="app-down-link"> <a href="javascript:;">立即使用</a> </div> </div> </div> <div class="app-footer"> <div class="main-content"> <div class="logo"></div> <div class="copyright">©️ 2020 湖南亚信科技有限公司AISWare loT</div> <span>联系我们</span> <div class="display-content"> <div class="qrcode"> <div>联系微信</div> </div> <div class="connection"> <div>联系人:万尧</div> <div>联系电话:15074805979</div> <div>联系邮箱:wanyao@asiainfo.com</div> </div> </div> </div> </div> </div> </body> </html>