MrShi
2025-12-08 3d6ff15899256f33cd100d95237f613c023d50df
优化
已添加12个文件
已删除1个文件
已修改30个文件
1696 ■■■■ 文件已修改
admin/.env.development 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/public/template/member.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/device.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/business/OperaDeviceBroadcastWindow.vue 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/deviceBroadcast.vue 117 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/deviceBroadcastChannel.vue 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/jkSketch.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/whyc_admin/avatar/woman.png 补丁 | 查看 | 原始文档 | blame | 历史
admin/whyc_admin/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
admin/whyc_admin/template/cars.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
admin/whyc_admin/template/member.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
admin/whyc_admin/template/oil.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
admin/whyc_admin/template/traintime.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
server/system_gateway/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/system_service/src/main/java/com/doumee/core/utils/Constants.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/system_service/src/main/java/com/doumee/core/utils/HttpsUtil.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_admin/src/main/java/com/doumee/api/business/DeviceController.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/DeviceCloudController.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/HkSyncCloudController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_admin/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/haikang/model/HKTools.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/haikang/model/param/request/ISAPIBroadcastAddPlanSchemeRequest.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/haikang/service/HKService.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/ClusterCustomKMeans.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/Clustering.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/DistanceCalculator.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/TspSolver.java 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/dao/admin/request/SketchCateModel.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/Device.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/Empower.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/JkCabinet.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/JkSketchCustomer.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/vo/CabinetInfoVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/DeviceService.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/JkSketchService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/ext/HkSyncService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/DeviceServiceImpl.java 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/JkCabinetServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/JkSketchCustomerServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/JkSketchServiceImpl.java 337 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/hksync/HkSyncBaseServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/hksync/HkSyncDeviceServiceImpl.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/.env.development
@@ -3,9 +3,15 @@
# VUE_APP_API_URL  = 'https://atwl.ahzyssl.com/zhyq_interface'
<<<<<<< HEAD
# VUE_APP_API_URL  = 'http://192.168.1.45:10010'
VUE_APP_API_URL  = 'http://192.168.0.7/system_gateway'
=======
VUE_APP_API_URL  = 'http://localhost:10010'
#VUE_APP_API_URL  = 'http://192.168.0.7/system_gateway'
>>>>>>> ce3b9c67f6b2ee83d7edf593d06221d909e9afda
# VUE_APP_API_URL  = 'http://localhost:10010'
admin/public/template/member.xlsx
Binary files differ
admin/src/api/business/device.js
@@ -25,6 +25,9 @@
export function updateById (data) {
  return request.post('/visitsAdmin/cloudService/business/device/updateById', data)
}
export function create (data) {
  return request.post('/visitsAdmin/cloudService/business/device/create', data)
}
// ä¿®æ”¹æ˜¯å¦é—¨ç¦å…¥å£
export function updateEntranceById (data) {
@@ -34,3 +37,20 @@
export function setLedContent (data) {
  return request.post('/visitsAdmin/cloudService/business/hksync/setLedContent', data)
}
export function sendBobao (data) {
  return request.post('/visitsAdmin/cloudService/business/hksync/sendBobao', data)
}
// åˆ é™¤
export function deleteById (id) {
  return request.get(`/visitsAdmin/cloudService/business/device/delete/${id}`)
}
// æ‰¹é‡åˆ é™¤
export function deleteByIdInBatch (ids) {
  return request.get('/visitsAdmin/cloudService/business/device/delete/batch', {
    params: {
      ids
    }
  })
}
admin/src/components/business/OperaDeviceBroadcastWindow.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
<template>
    <GlobalWindow
        :title="title"
        :visible.sync="visible"
        :confirm-working="isWorking"
        @confirm="confirm"
    >
        <el-form :model="form" ref="form" :rules="rules">
          <el-form-item label="设备名称" prop="name">
            <el-input v-model="form.name" placeholder="请输入名称" v-trim/>
          </el-form-item>
          <el-form-item label="设备唯一码" prop="hkId">
            <el-input v-model="form.hkId" placeholder="请输入设备唯一码" v-trim/>
          </el-form-item>
          <el-form-item label="设备序列号" prop="no">
            <el-input v-model="form.no" placeholder="请输入序列号" v-trim/>
          </el-form-item>
          <el-form-item label="连接IP" prop="ip">
            <el-input v-model="form.ip" placeholder="请输入设备连接IP地址" v-trim/>
          </el-form-item>
          <el-form-item label="连接端口" prop="port">
            <el-input v-model="form.port" placeholder="请输入连接端口" v-trim/>
          </el-form-item>
          <el-form-item label="连接账号" prop="doorId">
            <el-input v-model="form.doorId" placeholder="请输入设备连接账号" v-trim/>
          </el-form-item>
          <el-form-item label="连接密码" prop="doorName">
            <el-input v-model="form.doorName" type="password"  maxlength="30" show-password></el-input>
          </el-form-item>
          <el-form-item label="所在位置" prop="regionPathName">
            <el-input v-model="form.regionPathName" placeholder="请输入所在位置" v-trim/>
          </el-form-item>
        </el-form>
    </GlobalWindow>
</template>
<script>
import BaseOpera from '@/components/base/BaseOpera'
import GlobalWindow from '@/components/common/GlobalWindow'
export default {
  name: 'OperaDeviceBroadcastWindow',
  extends: BaseOpera,
  components: { GlobalWindow },
  data () {
    return {
      // è¡¨å•数据
      form: {
        id: null,
        hkId: '',
        name: '',
        regionPathName: '',
        no: '',
        type: 4,
        ip: '',
        port: '',
        doorId: '',
        doorName:''
      },
      // éªŒè¯è§„则
      rules: {
        name: [
          { required: true, message: '请输入设备名称' }
        ],
        ip: [
          { required: true, message: '请输入设备连接IP地址' }
        ],
        port: [
          { required: true, message: '请输入设备连接端口号' }
        ],
        doorId: [
          { required: true, message: '请输入设备连接账号' }
        ],
        doorName: [
          { required: true, message: '请输入设备连接密码' }
        ],
        hkId: [
          { required: true, message: '请输入设备唯一码' }
        ]
      }
    }
  },
  created () {
    this.config({
      api: '/business/device',
      'field.id': 'id'
    })
  }
}
</script>
admin/src/views/business/deviceBroadcast.vue
@@ -12,8 +12,10 @@
        </el-form>
        <!-- è¡¨æ ¼å’Œåˆ†é¡µ -->
        <template v-slot:table-wrap>
            <ul class="toolbar">
                <li><el-button type="primary" @click="synchronousData">同步</el-button></li>
          <ul class="toolbar" v-permissions="['business:device:create', 'business:device:delete']">
            <li><el-button type="primary" :loading="isWorking.delete" @click="synchronousData">同步</el-button></li>
            <li><el-button type="primary" @click="$refs.operaDeviceWindow.open('新建广播')" icon="el-icon-plus" v-permissions="['business:device:create']">新建</el-button></li>
            <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['business:device:delete']">删除</el-button></li>
            </ul>
            <el-table
                :height="tableHeightNew"
@@ -21,13 +23,38 @@
                :data="tableData.list"
                stripe
            >
              <el-table-column type="selection" width="55"></el-table-column>
              <el-table-column label="序号"  width="55" fixed><template slot-scope="scope" >{{scope.$index+1}}</template></el-table-column>
                <el-table-column prop="name" label="名称"></el-table-column>
                <el-table-column prop="hkId" label="广播点唯一标识" min-width="200"></el-table-column>
                <el-table-column prop="channelInfo" label="广播点类型"></el-table-column>
                <el-table-column prop="channelNo" label="通道号"></el-table-column>
                <el-table-column prop="regionName" label="区域名称"></el-table-column>
                <el-table-column prop="hkDate" label="同步时间"></el-table-column>
              <el-table-column prop="hkId" label="唯一标识"  ></el-table-column>
              <el-table-column prop="regionPathName" label="所在位置"></el-table-column>
              <el-table-column prop="no" label="序列号"  ></el-table-column>
              <el-table-column prop="ip" label="IP地址"  ></el-table-column>
              <el-table-column prop="port" label="端口"  ></el-table-column>
              <el-table-column prop="doorId" label="账号"></el-table-column>
              <el-table-column prop="doorName" label="密码" align="center">
                <template slot-scope="{row}">
                  <span :class=" 'blue'">{{row.showPwd?row.doorName:'******'}}</span>
                  <el-button  style="margin-left: 10px" v-if="row.doorName!=null"
                              @click.native.p.prevent="showPassward(row)" type="text">
                   <i class="el-icon-view" :class="row.showPwd?'red':'blue'" :title="row.showPwd?'隐藏':'显示'"></i>
                  </el-button>
                </template>
              </el-table-column>
              <el-table-column prop="editDate" label="最近更新时间"></el-table-column>
              <el-table-column
                  v-if="containPermissions(['business:devicerole:update', 'business:devicerole:delete'])"
                  label="操作"
                  align="center"
                  min-width="120"
                  fixed="right"
              >
                  <template slot-scope="{row}">
                    <el-button type="text" @click="$refs.operaDeviceWindow.open('编辑广播', row)" icon="el-icon-edit" v-permissions="['business:device:update']">编辑</el-button>
                    <el-button type="text" @click="send(row)" icon="el-icon-edit"  v-permissions="['business:device:update']">发送播报</el-button>
                    <el-button type="text" class="red" @click="deleteById(row)" icon="el-icon-delete" v-permissions="['business:device:delete']">删除</el-button>
                  </template>
                </el-table-column>
            </el-table>
            <pagination
                @size-change="handleSizeChange"
@@ -36,8 +63,26 @@
            >
            </pagination>
        </template>
      <el-dialog
          :visible.sync="visibleSend"
          style="z-index: 100000"
          append-to-body
          width="50%"
          height="50%"
          :title="'发送播报-'+ form.name"
      >
        <el-form :model="form" ref="form" >
          <el-form-item label="播报内容" prop="name" required>
            <el-input v-model="form.sendInfo" type="textarea" placeholder="请输入播报内容" v-trim/>
          </el-form-item>
        </el-form>
        <template  v-slot:footer  >
          <el-button @click="sendBobao()" type="primary" :loading="isWorkSending">确认</el-button>
          <el-button @click="sendClose()">返回</el-button>
        </template>
      </el-dialog>
        <!-- æ–°å»º/修改 -->
<!--        <OperaDeviceWindow ref="operaDeviceWindow" @success="handlePageChange"/>-->
    <OperaDeviceBroadcastWindow ref="operaDeviceWindow" @success="handlePageChange"/>
    </TableLayout>
</template>
@@ -45,12 +90,12 @@
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaDeviceWindow from '@/components/business/OperaDeviceWindow'
import { syncDevices, updateEntranceById } from '@/api/business/device'
import OperaDeviceBroadcastWindow from '@/components/business/OperaDeviceBroadcastWindow'
import { syncDevices } from '@/api/business/device'
export default {
  name: 'Device',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaDeviceWindow },
  components: { TableLayout, Pagination, OperaDeviceBroadcastWindow },
  data () {
    return {
      // æœç´¢
@@ -59,8 +104,16 @@
        regionPathName: '',
        isEntrance: '',
        online: '',
        type: 3
        type: 4
      },
      isWorkSending: false,
      form: {
        sendInfo: '',
        id: '',
        hkId: '',
        name: ''
      },
      visibleSend: false,
      options: []
    }
  },
@@ -74,17 +127,47 @@
    this.search()
  },
  methods: {
    changeManufature(e, row) {
      updateEntranceById({
        id: row.id,
        isEntrance: e
    showPassward (row) {
      if (!row.showPwd) {
        this.$set(row, 'showPwd', true)
      } else {
        this.$set(row, 'showPwd', false)
      }
    },
    sendBobao () {
      if (!this.form.sendInfo) {
        return
      }
      this.$dialog.actionConfirm('确认进行广播播报吗?', '操作确认提醒')
        .then(() => {
          this.isWorkSending = true
          this.api.sendBobao(this.form)
            .then(res => {
              this.$tip.apiSuccess(res || '播报请求成功')
              this.sendClose()
      })
            .catch(e => {
            })
            .finally(() => {
              this.isWorkSending = false
            })
        })
        .catch(() => {})
    },
    send (row) {
      this.visibleSend = true
      this.form = { sendInfo: '', id: row.id, hkId: row.hkId, name: row.name }
    },
    sendClose () {
      this.visibleSend = false
      this.isWorkSending = false
      this.form = { sendInfo: '', id: '', hkId: '', name: '' }
    },
    synchronousData () {
      this.$dialog.actionConfirm('操作确认提醒', '您确认同步全部信息吗?')
        .then(() => {
          this.isWorking.delete = true
          syncDevices({type: 3})
          syncDevices({ type: 4 })
            .then(res => {
              this.$tip.apiSuccess(res || '同步成功')
              this.search()
admin/src/views/business/deviceBroadcastChannel.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,103 @@
<template>
    <TableLayout :permissions="['business:device:query']">
        <!-- æœç´¢è¡¨å• -->
        <el-form ref="searchForm"  slot="search-form" :model="searchForm" label-width="100px" inline>
            <el-form-item title="名称" prop="name">
                <el-input v-model="searchForm.name" placeholder="请输入名称" @keypress.enter.native="search"></el-input>
            </el-form-item>
            <section>
                <el-button type="primary" @click="search">搜索</el-button>
                <el-button @click="reset">重置</el-button>
            </section>
        </el-form>
        <!-- è¡¨æ ¼å’Œåˆ†é¡µ -->
        <template v-slot:table-wrap>
            <ul class="toolbar">
                <li><el-button type="primary" @click="synchronousData">同步</el-button></li>
            </ul>
            <el-table
                :height="tableHeightNew"
                v-loading="isWorking.search"
                :data="tableData.list"
                stripe
            >
              <el-table-column label="序号"  width="55" fixed><template slot-scope="scope" >{{scope.$index+1}}</template></el-table-column>
                <el-table-column prop="name" label="名称"></el-table-column>
                <el-table-column prop="hkId" label="广播点唯一标识" min-width="200"></el-table-column>
                <el-table-column prop="channelInfo" label="广播点类型"></el-table-column>
                <el-table-column prop="channelNo" label="通道号"></el-table-column>
                <el-table-column prop="regionName" label="区域名称"></el-table-column>
                <el-table-column prop="hkDate" label="同步时间"></el-table-column>
            </el-table>
            <pagination
                @size-change="handleSizeChange"
                @current-change="handlePageChange"
                :pagination="tableData.pagination"
            >
            </pagination>
        </template>
        <!-- æ–°å»º/修改 -->
<!--        <OperaDeviceWindow ref="operaDeviceWindow" @success="handlePageChange"/>-->
    </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaDeviceWindow from '@/components/business/OperaDeviceWindow'
import { syncDevices, updateEntranceById } from '@/api/business/device'
export default {
  name: 'Device',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaDeviceWindow },
  data () {
    return {
      // æœç´¢
      searchForm: {
        doorName: '',
        regionPathName: '',
        isEntrance: '',
        online: '',
        type: 3
      },
      options: []
    }
  },
  created () {
    this.config({
      module: '设备信息表',
      api: '/business/device',
      'field.id': 'id',
      'field.main': 'id'
    })
    this.search()
  },
  methods: {
    changeManufature(e, row) {
      updateEntranceById({
        id: row.id,
        isEntrance: e
      })
    },
    synchronousData () {
      this.$dialog.actionConfirm('操作确认提醒', '您确认同步全部信息吗?')
        .then(() => {
          this.isWorking.delete = true
          syncDevices({type: 3})
            .then(res => {
              this.$tip.apiSuccess(res || '同步成功')
              this.search()
            })
            .catch(e => {
             //  this.$tip.apiFailed(e)
            })
            .finally(() => {
              this.isWorking.delete = false
            })
        })
        .catch(() => {})
    }
  }
}
</script>
admin/src/views/business/jkSketch.vue
@@ -5,6 +5,12 @@
      <el-form-item label="送货日期" prop="dateInfo">
        <el-date-picker v-model="searchForm.dateInfo" value-format="yyyy-MM-dd" placeholder="请输入日期" @change="search"/>
      </el-form-item>
      <el-form-item label="所属主线路" prop="categoryId">
        <el-select v-model="searchForm.categoryId" clearable filterable placeholder="请选择所属主线路"  @change="search">
          <el-option v-for="item in categoryList" :key="item.id" :label="item.name" :value="item.id">
          </el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="状态" prop="status" >
        <el-select v-model="searchForm.status" placeholder="请选择" clearable @change="search">
          <el-option label="未优化" value="0"></el-option>
@@ -91,6 +97,7 @@
import OperaJkSketchLineListWindow from '@/components/business/OperaJkSketchLineListWindow'
import OperaJkSketchResultWindow from '@/components/business/OperaJkSketchResultWindow'
import OperaJkSketchImportWindow from '@/components/business/OperaJkSketchImportWindow'
import {fetchCateList} from "@/api/business/category";
export default {
  name: 'JkSketch',
  extends: BaseTable,
@@ -100,12 +107,14 @@
      // æœç´¢
      searchForm: {
        status: '',
        categoryId: '',
        dateInfo: ''
      },
      isWorking: {
        export1: false,
        export2: false
      }
      },
      categoryList: []
    }
  },
  created () {
@@ -121,8 +130,14 @@
    const day = now.getDate();
    this.searchForm.dateInfo = `${year}-${month}-${day}`
    this.search()
    this.loadCategory()
  },
  methods: {
    loadCategory () {
      fetchCateList({ type: 4 }).then(res => {
        this.categoryList = res || []
      })
    },
    exportExcel1 () {
      if (!this.searchForm.dateInfo) {
        this.$message.error('请选择指定日期进行该导出操作!')
admin/whyc_admin/avatar/woman.png
admin/whyc_admin/favicon.ico
admin/whyc_admin/template/cars.xlsx
Binary files differ
admin/whyc_admin/template/member.xlsx
Binary files differ
admin/whyc_admin/template/oil.xlsx
Binary files differ
admin/whyc_admin/template/traintime.xlsx
Binary files differ
server/system_gateway/src/main/resources/bootstrap.yml
@@ -1,6 +1,6 @@
spring:
  profiles:
    active: pro
    active: dev
  application:
    name: system_gateway
  # å®‰å…¨é…ç½®
server/system_service/src/main/java/com/doumee/core/utils/Constants.java
@@ -57,6 +57,8 @@
    public static final String SIGN_IN_QRCODE ="SIGN_IN_QRCODE" ;
    public static final String SIGN_IN_ATTENTION ="SIGN_IN_ATTENTION" ;
    public static final String SIGN_IN_QRCODE_PREFIX ="SIGN_IN_QRCODE_PREFIX" ;
    public static final String JK_PLAN_MAX_CLUSTER ="JK_PLAN_MAX_CLUSTER" ;
    public static final String JK_PLAN_DISTANCE_CLUSTER ="JK_PLAN_DISTANCE_CLUSTER" ;
    /**
     * æ“ä½œç±»åž‹ï¼Œç”¨äºŽåšæŽ¥å£éªŒè¯åˆ†ç»„
server/system_service/src/main/java/com/doumee/core/utils/HttpsUtil.java
@@ -1,17 +1,35 @@
package com.doumee.core.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpMethod;
import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@Slf4j
public class HttpsUtil {
    public static String get(String url,boolean ignoreSSL) {
@@ -28,6 +46,65 @@
            return connectionHttp(url, "POST", data, "application/json");
        }
    }
    private static final int CONNECT_TIMEOUT =10000;// è®¾ç½®è¿žæŽ¥å»ºç«‹çš„超时时间为10s
    private static final int SOCKET_TIMEOUT = 10000;//socket读写超时时间(单位毫秒)
    private static void setRequestConfig(HttpRequestBase httpRequestBase) {
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(CONNECT_TIMEOUT)
                .setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
        httpRequestBase.setConfig(requestConfig);
    }
    public static String doPostHk(String host,int port,String UserName,String Password,String url, String Input) {
        try {
            CloseableHttpResponse responseBody = null;
            HttpPost httpPost = new HttpPost("http://"+host+":"+port+url);
            setRequestConfig(httpPost);
            httpPost.setEntity(new StringEntity(Input, "UTF-8"));
            CredentialsProvider credsProvider = new BasicCredentialsProvider();
            credsProvider.setCredentials(new AuthScope(host, port),
                    new UsernamePasswordCredentials(UserName, Password));
            CloseableHttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
            String response = "";
            // ç”±å®¢æˆ·ç«¯æ‰§è¡Œ(发送)Post请求
            responseBody = httpClient.execute(httpPost);
            // ä»Žå“åº”模型中获取响应实体
            HttpEntity responseEntity = responseBody.getEntity();
            log.error("doPostHk响应状态为:" + responseBody.getStatusLine());
            if (responseBody.getStatusLine().getStatusCode() == 302){
                String redirectLocation = responseBody.getHeaders("Location")[0].getValue();
                log.error("doPostHkRedirected to: " + redirectLocation);
                // åœ¨è¿™é‡Œï¼Œä½ å¯ä»¥å‘送一个新的请求到redirectLocation
                httpPost.setURI(URI.create(redirectLocation));
                CloseableHttpResponse responseBody2 = httpClient.execute(httpPost);
                log.error("doPostHk重定向后响应状态为:" + responseBody2.getStatusLine());
                HttpEntity responseEntity2 = responseBody2.getEntity();
                log.error("doPostHk重定向后响应内容长度为:" + responseEntity2.getContentLength());
                response = EntityUtils.toString(responseEntity2);
                log.error("doPostHk重定向后响应内容为:\n" + response);
                responseBody2.close();
                return response;
            }else {
                if (responseEntity != null) {
                    log.error("doPostHk响应内容长度为:" + responseEntity.getContentLength());
                    response = EntityUtils.toString(responseEntity);
                    log.error("doPostHk响应内容为:\n" + response);
                    return response;
                }
            }
            if (httpClient != null) {
                httpClient.close();
            }
            if (responseBody != null) {
                responseBody.close();
            }
        } catch (Exception e) {
            log.error("doPostHk发起请求异常:\n" + e.getMessage());
        }
        return null;
    }
    public static String connection(String url,String method,String data,String contentType,boolean ignoreSSL){
        HttpsURLConnection connection = null;
server/visits/dmvisit_admin/src/main/java/com/doumee/api/business/DeviceController.java
ÎļþÒÑɾ³ý
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/DeviceCloudController.java
@@ -36,6 +36,7 @@
    @PostMapping("/create")
    @CloudRequiredPermission("business:device:create")
    public ApiResponse create(@RequestBody Device device,@RequestHeader(Constants.HEADER_USER_TOKEN) String token){
        device.setLoginUserInfo(this.getLoginUser(token));
        return ApiResponse.success(deviceService.create(device));
    }
@@ -43,7 +44,7 @@
    @GetMapping("/delete/{id}")
    @CloudRequiredPermission("business:device:delete")
    public ApiResponse deleteById(@PathVariable Integer id,@RequestHeader(Constants.HEADER_USER_TOKEN) String token){
        deviceService.deleteById(id);
        deviceService.deleteById(id,this.getLoginUser(token));
        return ApiResponse.success(null);
    }
    @ApiOperation("修改是否门禁入口")
@@ -67,7 +68,7 @@
        for (String id : idArray) {
            idList.add(Integer.valueOf(id));
        }
        deviceService.deleteByIdInBatch(idList);
        deviceService.deleteByIdInBatch(idList,this.getLoginUser(token));
        return ApiResponse.success(null);
    }
@@ -75,6 +76,8 @@
    @PostMapping("/updateById")
    @CloudRequiredPermission("business:device:update")
    public ApiResponse updateById(@RequestBody Device device,@RequestHeader(Constants.HEADER_USER_TOKEN) String token){
        device.setLoginUserInfo(this.getLoginUser(token));
        deviceService.updateById(device);
        return ApiResponse.success(null);
    }
server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/HkSyncCloudController.java
@@ -118,6 +118,9 @@
        }else if(Constants.equalsInteger(param.getType(),Constants.THREE)){
            //同步广播
            result = hkSyncDeviceService.syncHkBroadcast(param);
        }else if(Constants.equalsInteger(param.getType(),Constants.FOUR)){
            //同步广播
            result = hkSyncDeviceService.syncHkBroadcastDevice(param);
        }
        return ApiResponse.success(result);
    }
@@ -351,5 +354,16 @@
        deviceService.setLedContent(body);
        return ApiResponse.success(null);
    }
    @ApiOperation("【海康】广播播报")
    @PostMapping("/sendBobao")
    public ApiResponse<String> setBroadcaseBobao(@RequestBody Device body  , HttpServletResponse response) {
        return ApiResponse.success( deviceService.setBroadcaseBobaoHttp(body));
    }
    @ApiOperation("【海康】广播播报")
    @PostMapping("/sendBobaoOpenapi")
    public ApiResponse<List<Map<String,Object>>> sendBobaoOpenapi(@RequestBody Device body  , HttpServletResponse response) {
        deviceService.setBroadcaseBobao(body);
        return ApiResponse.success(null);
    }
}
server/visits/dmvisit_admin/src/main/resources/bootstrap.yml
@@ -1,6 +1,6 @@
spring:
  profiles:
    active: pro
    active: dev
  application:
    name: visitsAdmin
    # å®‰å…¨é…ç½®
server/visits/dmvisit_service/pom.xml
@@ -40,6 +40,11 @@
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-math3</artifactId>
            <version>3.6.1</version>
        </dependency>
        <dependency>
            <groupId>com.doumee</groupId>
            <artifactId>emaysms</artifactId>
            <version>1.0.0-SNAPSHOT</version>
server/visits/dmvisit_service/src/main/java/com/doumee/core/haikang/model/HKTools.java
@@ -467,7 +467,6 @@
        return result;
    }
    public static String startDoPostStringArtemis(String[] pathStr,Map<String, String> querys,Map<String, String> header,String body){
        Map<String, String> path = getPath(pathStr[0]);
        header.put("tagId","frs");
server/visits/dmvisit_service/src/main/java/com/doumee/core/haikang/model/param/request/ISAPIBroadcastAddPlanSchemeRequest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package com.doumee.core.haikang.model.param.request;
import lombok.Data;
import java.util.List;
@Data
public class ISAPIBroadcastAddPlanSchemeRequest {
    /**
    {
        "terminalInfoList": [
        {
            "terminalID": 1,
                "audioOutID": [
            2
            ],
            "audioOutIndexList": [
            1
            ]
        }
    ],
        "broadcastPlanSchemeList": [
        {
            "planSchemeID": "plan1",
                "enabled": true,
                "sourceID": "fdb4465d7aa5495884845127a8bbbe3c",
                "weklyScheduleInfo": {
            "startTime": "1970-01-01",
                    "stopTime": "1970-01-01",
                    "weeklyScheduleList": [
            {
                "dayOfWeek": 1,
                    "scheduleList": [
                {
                    "planSchemeExecID": 0,
                        "beginTime": "00:00:00",
                        "endTime": "00:00:00",
                        "playNowTime": "00:00:00",
                        "operation": {
                    "audioSource": "customAudio",
                            "customAudioID": [
                    2
                                    ],
                    "URLIDs": [
                    1
                                    ],
                    "speechSynthesisContent": "test",
                            "audioLevel": 0,
                            "TTSLanguageType": "english",
                            "voiceType": "male",
                            "audioVolume": 1
                }
                }
                        ]
            }
                ]
        },
            "dailyScheduleInfo": {
            "startTime": "1970-01-01",
                    "stopTime": "1970-01-01",
                    "dailyScheduleList": [
            {
                "beginTime": "00:00:00",
                    "endTime": "00:00:00",
                    "playNowTime": "00:00:00",
                    "operation": {
                "audioSource": "customAudio",
                        "customAudioID": [
                2
                            ],
                "URLIDs": [
                1
                            ],
                "speechSynthesisContent": "test",
                        "audioLevel": 0,
                        "TTSLanguageType": "english",
                        "audioVolume": 1
            }
            }
                ]
        }
        }
    ]
    }
    */
}
server/visits/dmvisit_service/src/main/java/com/doumee/core/haikang/service/HKService.java
@@ -1424,17 +1424,17 @@
     *分页获取广播点(分页)
     * @return
     */
    public  static  BaseResponse<BaseListPageResponse<FetchAudioChannelInfoResponse>>   fetchAudioDevice(FetchAudioChannelRequest param){
        log.info("【海康获取分页获取广播点】================开始===="+JSONObject.toJSONString(param));
    public  static  BaseResponse<BaseListPageResponse<FetchAudioDeviceInfoResponse>>   fetchAudioDevice(FetchAudioDeviceRequest param){
        log.info("【海康获取分页获取广播设备】================开始===="+JSONObject.toJSONString(param));
        try {
            String res = HKTools.fetchAudioDevice(JSONObject.toJSONString(param));
            TypeReference typeReference =
                    new TypeReference< BaseResponse<BaseListPageResponse<FetchAudioChannelInfoResponse>> >(){};
            BaseResponse<BaseListPageResponse<FetchAudioChannelInfoResponse>>   result = JSONObject.parseObject(res, typeReference.getType());
            logResult(result,"海康获取分页获取广播点");
                    new TypeReference< BaseResponse<BaseListPageResponse<FetchAudioDeviceInfoResponse>> >(){};
            BaseResponse<BaseListPageResponse<FetchAudioDeviceInfoResponse>>   result = JSONObject.parseObject(res, typeReference.getType());
            logResult(result,"海康获取分页获取广播设备");
            return  result;
        }catch (Exception e){
            log.error("【海康获取分页获取广播点】================失败====:\n"+ e.getMessage());
            log.error("【海康获取分页获取广播设备】================失败====:\n"+ e.getMessage());
        }
        return  null;
    }
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/ClusterCustomKMeans.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,147 @@
package com.doumee.core.tsp;
import java.math.BigDecimal;
import java.util.*;
import com.doumee.core.utils.Constants;
import com.doumee.dao.admin.request.SketchCateModel;
import com.doumee.dao.business.model.JkSketchCustomer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.ml.clustering.*;
import org.apache.commons.math3.ml.distance.EuclideanDistance;
/**
 *
 * @author xjx
 *
 */
@Slf4j
public class ClusterCustomKMeans{
    //欧式距离
        public  List<SketchCateModel> clusterPoints(List<JkSketchCustomer> customers, int k, int d){
            List<SketchCateModel> list = new ArrayList<>();
            if(customers.size() <= k){
                int index =0;
                for (JkSketchCustomer c : customers) {
                    SketchCateModel sketchCateModel = new SketchCateModel();
                    sketchCateModel.setId(index);
                    List<JkSketchCustomer> cList = new ArrayList<>();
                    cList.add(c);
                    sketchCateModel.setTotalNum(c.getTotalNum());
                    sketchCateModel.setCustomerList(cList);
                    sketchCateModel.setStartPoint(c);
                    sketchCateModel.setTotalCustomer(1);
                    list.add(sketchCateModel);
                    log.error("Cluster " + index + "members:"+1+"/200 "+" å®¢æˆ·ï¼š"+   c.getName());
                    index ++;
                }
                return list;
            }
            List<DoublePointNew> points = new ArrayList<>();
            for (int i = 0; i < customers.size(); i++) {
                DoublePointNew p = new DoublePointNew(new double[]{customers.get(i).getLatitude().doubleValue(), customers.get(i).getLongitude().doubleValue()});
                p.setCustomer(customers.get(i));
                points.add(p); // ç¤ºä¾‹ç‚¹1
            }
            KMeansPlusPlusClusterer<DoublePointNew> clusterer = new KMeansPlusPlusClusterer<>(k, d, new EuclideanDistance());
            List<CentroidCluster<DoublePointNew>> clusters = clusterer.cluster(points);
            int index  = 0;
            int maxMembersPerCluster = 200; // è®¾ç½®æ¯ä¸ªç°‡çš„æœ€å¤§æˆå‘˜æ•°
            List<CentroidCluster<DoublePointNew>> allList = new ArrayList<>();
            for (CentroidCluster<DoublePointNew> cluster : clusters) {
                if(cluster.getPoints() ==null || cluster.getPoints().size() ==0){
                    log.error("Cluster " + clusters.indexOf(cluster) + "members:"+cluster.getPoints().size()+"/200  æ— æ•°æ®");
                    continue;
                }
                log.error("Cluster " + clusters.indexOf(cluster) + "members:"+cluster.getPoints().size()+"/200 "+"中心点:"+ Arrays.toString(cluster.getCenter().getPoint())+"中心点:"+ cluster.getPoints().get(0).getCustomer().getName());
                if (cluster.getPoints().size() > maxMembersPerCluster) {
                    // è¿™é‡Œå¯ä»¥é‡æ–°è¿è¡Œèšç±»ç®—法或者手动调整簇的分配策略
                    log.error("Cluster " + clusters.indexOf(cluster) + " exceeds maximum members:" + cluster.getPoints().size() + "/200, re-clustering needed.");
                    // å¯ä»¥è€ƒè™‘再次调用KMeansPlusPlusClusterer或其它逻辑来处理超限情况。
                }
                allList.add(cluster);
            }
            for (CentroidCluster<DoublePointNew> cluster : allList) {
//                System.out.println("Cluster " + clusters.indexOf(cluster) + "members:"+cluster.getPoints().size()+"/200 ");
                if (cluster.getPoints().size() > maxMembersPerCluster) {
                    // è¿™é‡Œå¯ä»¥é‡æ–°è¿è¡Œèšç±»ç®—法或者手动调整簇的分配策略
//                    System.out.println("Cluster " + clusters.indexOf(cluster) + " exceeds maximum members:"+cluster.getPoints().size()+"/200, re-clustering needed.");
                    // å¯ä»¥è€ƒè™‘再次调用KMeansPlusPlusClusterer或其它逻辑来处理超限情况。
                }
                SketchCateModel sketchCateModel = new SketchCateModel();
                sketchCateModel.setId(index);
                List<JkSketchCustomer> cList = new ArrayList<>();
                for(DoublePointNew doublePointNew:cluster.getPoints()){
                    cList.add(doublePointNew.getCustomer());
                    sketchCateModel.setTotalNum(Constants.formatBigdecimal(sketchCateModel.getTotalNum()).add(Constants.formatBigdecimal(doublePointNew.getCustomer().getTotalNum())));
                }
                sortPointByCenterPoint(cluster,cList);
                sketchCateModel.setCustomerList(cList);
                sketchCateModel.setStartPoint(cList.get(0));
                sketchCateModel.setTotalCustomer(cList.size());
                list.add(sketchCateModel);
            }
            return list;
        }
    private void sortPointByCenterPoint(CentroidCluster<DoublePointNew> cluster, List<JkSketchCustomer> cList) {
        double clat = Constants.formatBigdecimal(cList.get(0).getLatitude()).doubleValue();
        double clong = Constants.formatBigdecimal(cList.get(0).getLongitude()).doubleValue();
        if(cluster!=null || cluster.getCenter() != null || cluster.getCenter().getPoint() != null && cluster.getCenter().getPoint().length>=2){
            clong = cluster.getCenter().getPoint()[1];
            clat = cluster.getCenter().getPoint()[0];
        }
        for(JkSketchCustomer c : cList){
            double latDiff = Constants.formatBigdecimal(c.getLatitude()).doubleValue() - clat;
            double lonDiff = Constants.formatBigdecimal(c.getLongitude()).doubleValue() - clong;
            c.setDistanceCenter(Math.sqrt(latDiff * latDiff + lonDiff * lonDiff)); // æ¬§æ°è·ç¦»
        }
        Collections.sort(cList, (p1, p2) -> Double.compare(p1.getDistanceCenter(), p2.getDistanceCenter()));
    }
    public  class  DoublePointNew extends  DoublePoint{
        private JkSketchCustomer customer;
        public DoublePointNew(double[] point) {
            super(point);
        }
        public JkSketchCustomer getCustomer() {
            return customer;
        }
        public void setCustomer(JkSketchCustomer customer) {
            this.customer = customer;
        }
    }
    public static void main(String[] args) {
        List<JkSketchCustomer> points = new ArrayList<>();
        for (int i = 0; i <10; i++) {
            JkSketchCustomer a = new JkSketchCustomer();
            a.setLatitude(new BigDecimal(30.19d ));
            a.setLongitude(new BigDecimal(117.40 ));
            a.setName("客户"+i);
            points.add(a);
        }
        for (int i = 0; i <10; i++) {
            JkSketchCustomer a = new JkSketchCustomer();
            a.setLatitude(new BigDecimal(31.19d ));
            a.setLongitude(new BigDecimal(118.40 ));
            a.setName("客户"+i);
            points.add(a);
        }
         for (int i = 10; i <3000; i++) {
            JkSketchCustomer a = new JkSketchCustomer();
            a.setLatitude(new BigDecimal(31.19d-(0.1d)*Math.random()));
            a.setLongitude(new BigDecimal(118.40-(0.2d)*Math.random()));
            a.setName("客户"+i);
            points.add(a);
        }
        double threshold = 1000; // è®¾ç½®è·ç¦»é˜ˆå€¼ï¼Œè¶…过这个距离就不属于同一聚类。
        (new ClusterCustomKMeans()).clusterPoints(points, 100,1);
    }
}
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/Clustering.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package com.doumee.core.tsp;
/**
 * èšç±»åˆ†ç»„
 */
import com.doumee.core.utils.Constants;
import com.doumee.dao.admin.request.SketchCateModel;
import com.doumee.dao.business.model.JkSketchCustomer;
import com.doumee.service.business.impl.JkSketchServiceImpl;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class Clustering {
    public static  List<SketchCateModel>  clusterPoints(List<JkSketchCustomer> points, double threshold) {
        List<SketchCateModel> clusters = new ArrayList<>();
        boolean[] visited = new boolean[points.size()];
        int index =0;
        for (int i = 0; i < points.size(); i++) {
            if (!visited[i]) {
                List<JkSketchCustomer> cluster = new ArrayList<>();
                dfs(points, visited, cluster, i, threshold);
                SketchCateModel sketchCateModel = new SketchCateModel();
                sketchCateModel.setCustomerList(cluster);
                sketchCateModel.setId(index);
                sketchCateModel.setStartPoint(cluster.get(0));
                for (JkSketchCustomer c : cluster){
                    sketchCateModel.setTotalNum(Constants.formatBigdecimal(sketchCateModel.getTotalNum()).add(Constants.formatBigdecimal(c.getTotalNum())));
                }
                sketchCateModel.setTotalCustomer(cluster.size());
                clusters.add(sketchCateModel);
            }
        }
        // æ‰“印每个聚类的点
        for (int i = 0; i < clusters.size(); i++) {
            System.out.println("Cluster " + (i + 1) + ": " + clusters.get(i).getStartPoint().getName()+ ": " + clusters.get(i).getCustomerList().size());
        }
        return  clusters;
    }
    public static double distanceTo(JkSketchCustomer self, JkSketchCustomer other) {
        List<DistanceMapParam> distanceMapParamList  =JkSketchServiceImpl.getListFromJsonStr(self.getDistanceJson());
        DistanceMapParam param = JkSketchServiceImpl.getParamByCustomerIds( other.getId(),distanceMapParamList);
        if(param!=null && param.getDistance()!=0){//如果之前已经获取过
          return (param.getDistance());
        }
        return DistanceCalculator.calculateDistance(Constants.formatBigdecimal(self.getLatitude()).doubleValue()
                ,Constants.formatBigdecimal(self.getLongitude()).doubleValue()
                ,Constants.formatBigdecimal(other.getLatitude()).doubleValue()
                ,Constants.formatBigdecimal(other.getLongitude()).doubleValue());
    }
    private static void dfs(List<JkSketchCustomer> points, boolean[] visited, List<JkSketchCustomer> cluster, int startIndex, double threshold) {
        visited[startIndex] = true;
        cluster.add(points.get(startIndex));
        JkSketchCustomer startPoint = points.get(startIndex);
        for (int i = 0; i < points.size(); i++) {
            if (!visited[i]) {
                double distance = distanceTo(startPoint,points.get(i));
                if (distance <= threshold) {
                    dfs(points, visited, cluster, i, threshold); // é€’归添加到聚类中
                }
            }
        }
    }
    /**
     * 117°40′~118°44′、北纬30°19′~31°34′
     * @param args
     */
    public static void main(String[] args) {
        List<JkSketchCustomer> points = new ArrayList<>();
        for (int i = 0; i <3000; i++) {
            JkSketchCustomer a = new JkSketchCustomer();
            a.setLatitude(new BigDecimal(30.19d+(30.54d-30.19d)*Math.random()));
            a.setLongitude(new BigDecimal(117.40+(117.74d-117.40d)*Math.random()));
            a.setName("客户"+i);
            points.add(a);
        }
        double threshold = 1000; // è®¾ç½®è·ç¦»é˜ˆå€¼ï¼Œè¶…过这个距离就不属于同一聚类。
        clusterPoints(points, threshold);
    }
}
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/DistanceCalculator.java
@@ -167,6 +167,6 @@
                        Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return  (long) (EARTH_RADIUS * c );
        return  (long) (EARTH_RADIUS * c * 1000);
    }
}
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/TspSolver.java
@@ -25,7 +25,7 @@
        data.initDataList();//构造数据
        long start =System.currentTimeMillis();
        System.out.println("=============start=========="+start);
       log.error("=============start=========="+start);
        //创建求解器manager对象,初始化求解器数据
        RoutingIndexManager manager =  new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot);
@@ -71,15 +71,15 @@
        // æ‰“印路线
        printSolution(data, routing, manager, solution);
        long end =System.currentTimeMillis();
        System.out.println("=============end=========="+end);
       log.error("=============end=========="+end);
        System.out.println("=============耗时=========="+(end -start)+"(ms)"+(end -start)/1000 +"s"+(end -start)/60/1000 +"m");
       log.error("=============耗时=========="+(end -start)+"(ms)"+(end -start)/1000 +"s"+(end -start)/60/1000 +"m");
    */}
    public static void startSearch(DataModel data) {
        // åˆå§‹åŒ–数据模型
        Loader.loadNativeLibraries();
        long start =System.currentTimeMillis();
        System.out.println("=============start=========="+start);
       log.error("开始规划=============start=========="+start);
        //创建求解器manager对象,初始化求解器数据
        RoutingIndexManager manager =  new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot);
@@ -87,7 +87,7 @@
        RoutingModel routing = new RoutingModel(manager);
        // æ³¨å†Œå›žè°ƒå‡½æ•°
       /* final int transitCallbackIndex =
       final int transitCallbackIndex =
                routing.registerTransitCallback((long fromIndex, long toIndex) -> {
                    int fromNode = manager.indexToNode(fromIndex);
                    int toNode = manager.indexToNode(toIndex);
@@ -96,7 +96,7 @@
        // å®šä¹‰å›žè°ƒå‡½æ•°è‡³æ¯æ¡è·¯çº¿
        routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
        routing.addDimension(transitCallbackIndex, 0, 30000000,
  /*      routing.addDimension(transitCallbackIndex, 0, 30000000,
                true,
                "Distance");
        RoutingDimension distanceDimension = routing.getMutableDimension("Distance");
@@ -104,7 +104,8 @@
        // æ³¨å†Œå›žè°ƒå‡½æ•°
        final int transitCallbackIndex1 =
                routing.registerTransitCallback((long fromIndex, long toIndex) -> {
                    return 1;
                    int fromNode = manager.indexToNode(fromIndex);
                    return data.customerDemands[fromNode];
                });
        for (int d = 0; d < data.vehicleMaxNodes.length; d++) {
            // å¢žåŠ è·ç¦»ç»´åº¦çº¦æŸ
@@ -121,33 +122,25 @@
        });
        routing.addDimensionWithVehicleCapacity(demandCallbackIndex, 0, data.vehicleCapacities, true, "Capacity");
/*
        // æ·»åŠ å®¹é‡é™åˆ¶
        final int demandCallbackIndex1 = routing.registerUnaryTransitCallback((long fromIndex) -> {
            return 1;
        });
        routing.addDimensionWithVehicleCapacity(demandCallbackIndex1, 0, data.vehicleMaxNodes, true, "Capacity");
*/
        Solver solver = routing.solver();
        //设置搜索方法(
        RoutingSearchParameters searchParameters =
                main.defaultRoutingSearchParameters()
                        .toBuilder()
                        .setTimeLimit(Duration.newBuilder().setSeconds(60*60).build())//最久1小时
                        .setTimeLimit(Duration.newBuilder().setSeconds(60*60*6).build())//最久1小时
                        .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC)
                        .build();
        // æ‰§è¡Œç®—法
        Assignment solution = routing.solveWithParameters(searchParameters);
        if(solution ==null){
            log.error("规划结束=============未找到最优路线!" );
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),"未找到最优路线!");
        }
        // æ‰“印路线
        resultSolution(data, routing, manager, solution);
        long end =System.currentTimeMillis();
        System.out.println("=============end=========="+end);
        System.out.println("=============耗时=========="+(end -start)+"(ms)"+(end -start)/1000 +"s"+(end -start)/60/1000 +"m");
         log.error("规划结束=============耗时=========="+(end -start)+"(ms)"+(end -start)/1000 +"s"+(end -start)/60/1000 +"m");
    }
    static void resultSolution( DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) {
        long maxRouteDistance = 0;
@@ -223,6 +216,7 @@
        //每一个点的商品的数量
        public   long[] demands;
        public   long[] customerDemands;
        //车辆最大容载
        public   long[] vehicleCapacities ;
        public   long[] vehicleMaxNodes ;
@@ -236,17 +230,19 @@
           this.solutions = solutions;
       }
       public  void initDataInfo(int vehicleNumber1, long[] demands1, long[] vehicleCapacities1, long[][] distanceMatrix1,long[] vehicleMaxNodes){
       public  void initDataInfo(int vehicleNumber1, long[] demands1,long[] demands2, long[] vehicleCapacities1, long[][] distanceMatrix1,long[] vehicleMaxNodes){
            this.demands = demands1;
            this.customerDemands = demands2;
            this.vehicleNumber = vehicleNumber1;
            this.vehicleCapacities=vehicleCapacities1;
            this.distanceMatrix=distanceMatrix1;
            this.vehicleMaxNodes =vehicleMaxNodes;
        }
        public   void initDataList(){
            lenght = 20;
            lenght = 10;
            vehicleNumber = 5;
            demands = new long[lenght];
            customerDemands = new long[lenght];
            vehicleCapacities =new long[vehicleNumber];
            vehicleMaxNodes =new long[vehicleNumber];
            distanceMatrix =  new long[lenght][lenght];
@@ -254,15 +250,16 @@
            for (int i = 0; i <vehicleNumber ; i++) {
                long tem = (long) (Math.random() * 1000 + 20000);
                vehicleCapacities[i] = tem;
                vehicleMaxNodes[i] =5;
                vehicleMaxNodes[i] =50;
                total0+=tem;
                System.out.print(tem+" ,");
            }
            System.out.println( "\ntotal Capacity:"+total0+"=====================");
           log.error( "\ntotal Capacity:"+total0+"=====================");
            long total = 0;
            for (int i = 0; i <lenght ; i++) {
                long tem =  (int)(Math.random()*100+100);
                demands[i] =tem;
                customerDemands[i] =1;
                total+=tem;
                System.out.print(tem+" ,");
                for (int j = 0; j <lenght ; j++) {
@@ -276,7 +273,7 @@
                }
            }
            System.out.println( "\ntotal Demands:"+total+"=====================");
           log.error( "\ntotal Demands:"+total+"=====================");
        }
       /* public final long[][] distanceMatrix = {
                {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662},
server/visits/dmvisit_service/src/main/java/com/doumee/dao/admin/request/SketchCateModel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.doumee.dao.admin.request;
import com.baomidou.mybatisplus.annotation.TableField;
import com.doumee.core.annotation.excel.ExcelColumn;
import com.doumee.core.tsp.DistanceMapParam;
import com.doumee.dao.business.model.JkSketchCustomer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
 * å‘˜å·¥ä¿¡æ¯å¯¼å…¥è¡¨
 * @author æ±Ÿè¹„蹄
 * @date 2024/01/16 10:03
 */
@Data
@ApiModel("线路规划聚类分组集合")
public class SketchCateModel {
    private int id;
    private JkSketchCustomer startPoint;
    //中心点纬度
    private BigDecimal latitude;
    //中心点经度
    private BigDecimal longitude;
    @ApiModelProperty(value = "送货量", example = "1")
    private BigDecimal totalNum;
    @ApiModelProperty(value = "总客户数", example = "1")
    private int totalCustomer;
    //点集合
    private  List<JkSketchCustomer> customerList;
}
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/Device.java
@@ -1,5 +1,6 @@
package com.doumee.dao.business.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.doumee.core.annotation.excel.ExcelColumn;
import com.doumee.service.business.third.model.LoginUserModel;
import io.swagger.annotations.ApiModel;
@@ -70,8 +71,8 @@
    @ExcelColumn(name="排序码")
    private Integer sortnum;
    @ApiModelProperty(value = "类型 0门禁 1车库 2LED 3广播", example = "1")
    @ExcelColumn(name="类型 0门禁 1车库 2LED 3广播")
    @ApiModelProperty(value = "类型 0门禁 1车库 2LED 3广播点 4广播设备", example = "1")
    @ExcelColumn(name="类型 0门禁 1车库 2LED 3广播点 4广播设备")
    private Integer type;
    @ApiModelProperty(value = "是否园区出入口 0不是 1是", example = "1")
    @ExcelColumn(name="是否园区出入口 0不是 1是")
@@ -155,8 +156,9 @@
    private String doorName;
    @ApiModelProperty(value = "播报内容")
    @TableField(exist = false)
    private String sendInfo;
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/Empower.java
@@ -59,7 +59,6 @@
    @ApiModelProperty(value = "导入备注")
    private String importInfo;
    @ApiModelProperty(value = "下发人员", example = "1")
    private Integer sendUserId;
    @ApiModelProperty(value = "下发次数", example = "1")
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/JkCabinet.java
@@ -148,6 +148,9 @@
    @ExcelColumn(name="起始编码")
    private Integer noIndex;
    @ApiModelProperty(value = "酒精检测开启:0=开启;1=关闭", example = "1")
    private Integer alcoholStatus;
    @ApiModelProperty(value = "经纬度信息 å‚数值:经度,纬度")
    @TableField(exist = false)
    private String jwd;
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/model/JkSketchCustomer.java
@@ -143,6 +143,9 @@
    //@ExcelColumn(name="状态 0正常 ç¦ç”¨")
    @TableField(exist = false)
    private String  endSteps;
    @ApiModelProperty(value = "距离中心点距离", example = "1")
    @TableField(exist = false)
    private double  distanceCenter;
    @ApiModelProperty(value = "同班组间客户位置距离数组,[{a:12,b:100},{a:13,b:200},...],a:客户编码,b:与客户a之间的距离")
    @TableField(exist = false)
    private List<DistanceMapParam> distanceMapParamList;
server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/vo/CabinetInfoVO.java
@@ -64,6 +64,8 @@
    @ApiModelProperty(value = "激活文件地址")
    private String activateFileUrl;
    @ApiModelProperty(value = "酒精检测开启:0=开启;1=关闭", example = "1")
    private Integer alcoholStatus;
    @ApiModelProperty(value = "轮播图地址", example = "1")
    private List<String> bannerList;
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/DeviceService.java
@@ -1,6 +1,7 @@
package com.doumee.service.business;
import com.doumee.core.haikang.model.param.request.TransparentChannelSingleRequest;
import com.doumee.service.business.third.model.LoginUserInfo;
import com.doumee.service.business.third.model.PageData;
import com.doumee.service.business.third.model.PageWrap;
import com.doumee.dao.business.model.Device;
@@ -26,7 +27,7 @@
     *
     * @param id ä¸»é”®
     */
    void deleteById(Integer id);
    void deleteById(Integer id, LoginUserInfo userInfo);
    /**
     * åˆ é™¤
@@ -40,7 +41,7 @@
     *
     * @param ids ä¸»é”®é›†
     */
    void deleteByIdInBatch(List<Integer> ids);
    void deleteByIdInBatch(List<Integer> ids, LoginUserInfo userInfo);
    /**
     * ä¸»é”®æ›´æ–°
@@ -105,4 +106,7 @@
    void setLedContent(TransparentChannelSingleRequest body);
    void allLedDefualtContent();
    void setBroadcaseBobao(Device body);
    String setBroadcaseBobaoHttp(Device body);
}
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/JkSketchService.java
@@ -108,6 +108,7 @@
    List<JkSketch> importBatch(MultipartFile file, String dateInfo, LoginUserInfo loginUser);
    void startUpdateLineAsyncOld(JkSketch model);
    void startUpdateLineAsync(JkSketch model);
    void distanceCustomer(Category model );
@@ -119,7 +120,7 @@
    void startEditSketchLineAsync(JkSketch model);
    void startInitOriginDistanceBatch(JkSketch list);
    void startEditSketchLineAsyncOld(JkSketch model);
    /**
     * è½¦è¾†çº¿è·¯  - ç”µä¿¡ä½¿ç”¨
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/ext/HkSyncService.java
@@ -97,6 +97,7 @@
    String syncHkLed(Device param);
    String syncHkBroadcast(Device param);
    String syncHkBroadcastDevice(Device param);
    String dealVisitEventIccm(EventVisitIccmRequest param, HttpServletResponse response);
    String dealPlatformStatusEvent(EventPlatformRequest param, HttpServletResponse response);
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/DeviceServiceImpl.java
@@ -1,12 +1,20 @@
package com.doumee.service.business.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.doumee.biz.system.SystemDictDataBiz;
import com.doumee.core.constants.ResponseStatus;
import com.doumee.core.exception.BusinessException;
import com.doumee.core.haikang.model.HKConstants;
import com.doumee.core.haikang.model.param.BaseResponse;
import com.doumee.core.haikang.model.param.request.CustomBroadcastRequest;
import com.doumee.core.haikang.model.param.request.TransparentChannelSingleRequest;
import com.doumee.core.haikang.service.HKService;
import com.doumee.core.utils.HttpsUtil;
import com.doumee.dao.business.*;
import com.doumee.dao.business.model.*;
import com.doumee.service.business.third.model.LoginUserInfo;
import com.doumee.service.business.third.model.PageData;
import com.doumee.service.business.third.model.PageWrap;
import com.doumee.core.utils.Constants;
@@ -49,14 +57,24 @@
    @Override
    public Integer create(Device device) {
        deviceMapper.insert(device);
        return device.getId();
    public Integer create(Device model) {
        model.setCreator(model.getLoginUserInfo().getId()+"");
        model.setEdirot(model.getCreator());
        model.setIsdeleted(Constants.ZERO);
        model.setEditDate(new Date());
        model.setCreateDate(model.getEditDate());
        deviceMapper.insert(model);
        return model.getId();
    }
    @Override
    public void deleteById(Integer id) {
        deviceMapper.deleteById(id);
    public void deleteById(Integer id, LoginUserInfo userInfo) {
        Device update = new Device();
        update.setEdirot(userInfo.getId()+"");
        update.setEditDate(new Date());
        update.setIsdeleted(Constants.ONE);
        update.setId(id);
        deviceMapper.updateById(update);
    }
    @Override
@@ -66,15 +84,19 @@
    }
    @Override
    public void deleteByIdInBatch(List<Integer> ids) {
    public void deleteByIdInBatch(List<Integer> ids, LoginUserInfo userInfo) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        deviceMapper.deleteBatchIds(ids);
        for(Integer id :ids){
            deleteById(id,userInfo);
        }
    }
    @Override
    public void updateById(Device device) {
        device.setEdirot(device.getLoginUserInfo().getId()+"");
        device.setEditDate(new Date());
        deviceMapper.updateById(device);
    }
@@ -232,6 +254,154 @@
    }
    @Override
    public void setBroadcaseBobao(Device model){
        List<String> ids = new ArrayList<>();
        ids.add(model.getHkId());
        CustomBroadcastRequest request = new CustomBroadcastRequest();
        request.setAudioPointIndexCode(ids);
        request.setPlayDuration(15);//单位秒
        request.setBroadCastMode("tts");
        request.setPriority(1);
        request.setState(1);//播放/停止标识 1-播放,0-停止
        request.setPlayTtsContent(model.getSendInfo());
        BaseResponse response =  HKService.customBroadcast(request);
        if(response == null || !StringUtils.equals(response.getCode(), HKConstants.RESPONSE_SUCCEE)){
          throw  new BusinessException(ResponseStatus.SERVER_ERROR.getCode(),"发送失败:"+ JSONObject.toJSONString(response));
        }
    }
    @Override
    public String setBroadcaseBobaoHttp(Device param){
       /* Device model = deviceMapper.selectById(param.getId());
        if(model ==null && Constants.equalsInteger(param.getType(),Constants.FOUR)){
            throw  new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        String input = "";
        JSONObject objext = new JSONObject();
        //重新下发计划
        JSONObject jsonObject = new JSONObject();
        //第一层
        JSONArray firstJsonArray = new JSONArray();
        JSONObject downJson = new JSONObject();
        downJson.put("terminalID",1);
        //不填默认对所有通道下发
        JSONArray audioJsonArray = new JSONArray();
        audioJsonArray.add(1);
        downJson.put("audioOutID",audioJsonArray);
        firstJsonArray.add(downJson);
        jsonObject.put("TerminalInfoList",firstJsonArray);
        //第一层数组
        JSONArray allJsonArray = new JSONArray();
        //数组中对象
        for (WaterTimingBroadcastDO waterTimingBroadcastDO : searchList) {
            JSONObject downArrayData = new JSONObject();
            downArrayData.put("audioOutID",audioJsonArray);
            downArrayData.put("enabled",true);
            downArrayData.put("planSchemeID",waterTimingBroadcastDO.getName());
            JSONObject executeJson = new JSONObject();
            executeJson.put("startTime", getDateStringByZones(waterTimingBroadcastDO.getStartDate()));
            executeJson.put("stopTime",getDateStringByZones(waterTimingBroadcastDO.getEndDate()));
            if (Constant.ONE.equals(waterTimingBroadcastDO.getPeriodType())){
                //日计划
                JSONArray executeJsonArray = new JSONArray();
                JSONObject executeJsonDataItem = new JSONObject();
                executeJsonDataItem.put("beginTime",waterTimingBroadcastDO.getStartTime()+"+08:00");
                executeJsonDataItem.put("endTime",waterTimingBroadcastDO.getEndTime()+"+08:00");
                executeJsonDataItem.put("playMode","loop");
                executeJsonDataItem.put("playNowTime","");
                executeJsonDataItem.put("planSchemeExecID",0);
                JSONObject secondDownOperationJson = new JSONObject();
                if (Constant.ONE.equals(waterTimingBroadcastDO.getContentType())){
                    //文本
                    secondDownOperationJson.put("audioSource","speechSynthesis");
                    secondDownOperationJson.put("speechSynthesisContent",waterTimingBroadcastDO.getContent());
                }else if (Constant.TWO.equals(waterTimingBroadcastDO.getContentType())){
                    //音频
                    secondDownOperationJson.put("audioSource","customAudio");
                    String json = waterTimingBroadcastDO.getJson();
                    JSONObject jsonData = JSONObject.parseObject(json);
                    List<Integer> hkAudioIds = new ArrayList<>();
                    hkAudioIds.add(jsonData.getIntValue(waterDeviceDO.getIp()));
                    secondDownOperationJson.put("customAudioID",hkAudioIds);
                }
                secondDownOperationJson.put("audioLevel",5);
                //语言类型
                secondDownOperationJson.put("TTSLanguageType","chinese");
                secondDownOperationJson.put("voiceType","female");
                secondDownOperationJson.put("audioVolume",100);
                executeJsonDataItem.put("Operation",secondDownOperationJson);
                executeJsonArray.add(executeJsonDataItem);
                executeJson.put("dailyScheduleList",executeJsonArray);
            }else {
                //周计划
                JSONArray executeJsonArray = new JSONArray();
                List<String> executeTime = new ArrayList<>();
                if (Constant.ONE.equals(waterTimingBroadcastDO.getPeriodType())){
                    //日计划 å¾ªçŽ¯ä¸€æ¬¡
                    executeTime.add("1");
                }else if (Constant.TWO.equals(waterTimingBroadcastDO.getPeriodType())){
                    //周计划多一个周几
                    executeTime = Arrays.asList(waterTimingBroadcastDO.getExecuteTime().split(","));
                }
                for (String s : executeTime) {
                    JSONObject executeJsonData = new JSONObject();
                    if (Constant.TWO.equals(waterTimingBroadcastDO.getPeriodType())){
                        //周计划多一个周几
                        executeJsonData.put("dayOfWeek",Integer.parseInt(s));
                    }
                    JSONArray executeJsonDataArray = new JSONArray();
                    JSONObject executeJsonDataItem = new JSONObject();
                    executeJsonDataItem.put("beginTime",waterTimingBroadcastDO.getStartTime()+"+08:00");
                    executeJsonDataItem.put("endTime",waterTimingBroadcastDO.getEndTime()+"+08:00");
                    executeJsonDataItem.put("playMode","loop");
                    executeJsonDataItem.put("playNowTime","");
                    executeJsonDataItem.put("planSchemeExecID",0);
                    JSONObject secondDownOperationJson = new JSONObject();
                    if (Constant.ONE.equals(waterTimingBroadcastDO.getContentType())){
                        //文本
                        secondDownOperationJson.put("audioSource","speechSynthesis");
                        secondDownOperationJson.put("speechSynthesisContent",waterTimingBroadcastDO.getContent());
                    }else if (Constant.TWO.equals(waterTimingBroadcastDO.getContentType())){
                        //音频
                        secondDownOperationJson.put("audioSource","customAudio");
                        String json = waterTimingBroadcastDO.getJson();
                        JSONObject jsonData = JSONObject.parseObject(json);
                        List<Integer> hkAudioIds = new ArrayList<>();
                        hkAudioIds.add(jsonData.getIntValue(waterDeviceDO.getIp()));
                        secondDownOperationJson.put("customAudioID",hkAudioIds);
                    }
                    secondDownOperationJson.put("audioLevel",5);
                    //语言类型
                    secondDownOperationJson.put("TTSLanguageType","chinese");
                    secondDownOperationJson.put("voiceType","female");
                    secondDownOperationJson.put("audioVolume",10);
                    executeJsonDataItem.put("Operation",secondDownOperationJson);
                    executeJsonDataArray.add(executeJsonDataItem);
                    executeJsonData.put("scheduleList",executeJsonDataArray);
                    executeJsonArray.add(executeJsonData);
                }
                executeJson.put("weeklyScheduleList",executeJsonArray);
            }
            if (Constant.ONE.equals(waterTimingBroadcastDO.getPeriodType())){
                //日计划
                downArrayData.put("dailyScheduleInfo",executeJson);
            }else {
                //周计划
                downArrayData.put("weklyScheduleInfo",executeJson);
            }
            allJsonArray.add(downArrayData);
        }
        jsonObject.put("broadcastPlanSchemeList",allJsonArray);
        log.info("海康播放计划下发入参内容 : " + jsonObject);
        String result = HttpsUtil.doPostHk(param.getIp(),Integer.parseInt(StringUtils.defaultString(param.getPort(),"80"))
                ,"/ISAPI/VideoIntercom/broadcast/AddPlanScheme?format=json",param.getDoorId(), param.getDoorName(),jsonObject.toJSONString());
        return result;*/
        return  null;
    }
    @Override
    public void setLedContent(TransparentChannelSingleRequest model) {
        Device device = findById(model.getDeviceId());
        if(device == null
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/JkCabinetServiceImpl.java
@@ -81,6 +81,7 @@
            || CollectionUtils.isEmpty(jkCabinet.getManagerIdList())
            || CollectionUtils.isEmpty(jkCabinet.getAuthMemberIdList())
            || Objects.isNull(jkCabinet.getDoubleAuth())
            || Objects.isNull(jkCabinet.getAlcoholStatus())
            || Objects.isNull(jkCabinet.getRowNum())
            || jkCabinet.getRowNum() <= Constants.ZERO
@@ -214,6 +215,7 @@
                || CollectionUtils.isEmpty(jkCabinet.getAuthMemberIdList())
                || Objects.isNull(jkCabinet.getPort())
                || Objects.isNull(jkCabinet.getDoubleAuth())
                || Objects.isNull(jkCabinet.getAlcoholStatus())
        ){
            throw new BusinessException(ResponseStatus.BAD_REQUEST);
        }
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/JkSketchCustomerServiceImpl.java
@@ -143,7 +143,7 @@
            startmodel.setSteps(allList.get(0).getStartSteps());
            if(StringUtils.isBlank(startmodel.getSteps())){
                // (BigDecimal lat1, BigDecimal lon1, BigDecimal lat2, BigDecimal lon2)
                startmodel.setDistance(DistanceCalculator.calculateDistanceDecinal(startmodel.getStartLatitude(),startmodel.getStartLogitude(),startmodel.getEndLatitude(),startmodel.getEndLatitude()));
                startmodel.setDistance(DistanceCalculator.calculateDistanceDecinal(startmodel.getStartLatitude(),startmodel.getStartLogitude(),startmodel.getEndLatitude(),startmodel.getEndLogitude()));
                startmodel.setSteps(comLocation +";"+allList.get(0).getLongitude()+","+allList.get(0).getLatitude());
            }
            list.add(startmodel);
@@ -186,7 +186,7 @@
                    if(param!=null && param.getDistance()!=0){//如果之前已经获取过
                        tt.setDistance(param.getDistance());
                    }else{
                        tt.setDistance(DistanceCalculator.calculateDistanceDecinal(tt.getStartLatitude(),tt.getStartLogitude(),tt.getEndLatitude(),tt.getEndLatitude()));
                        tt.setDistance(DistanceCalculator.calculateDistanceDecinal(tt.getStartLatitude(),tt.getStartLogitude(),tt.getEndLatitude(),tt.getEndLogitude()));
                    }
                }
                tt.setLocation(allList.get(i).getLocation());
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/JkSketchServiceImpl.java
@@ -12,6 +12,7 @@
import com.doumee.core.wms.model.response.WmsBaseDataResponse;
import com.doumee.core.wms.model.response.WmsBaseResponse;
import com.doumee.dao.admin.request.JkOrdersImport;
import com.doumee.dao.admin.request.SketchCateModel;
import com.doumee.dao.business.*;
import com.doumee.dao.business.dto.TelecomLineInfoDTO;
import com.doumee.dao.business.model.*;
@@ -499,7 +500,6 @@
        c.setNeedList(Constants.equalsInteger(model.getForceUpdate(),0)?needList:customerList);
        return  c;
    }
    /**
     * å¼€å§‹å¼‚步执行线路优化任务
     * @param model
@@ -508,6 +508,74 @@
    @Async
    public void startUpdateLineAsync(JkSketch model) {
        try {
            int maxnum =100,distance = 500;
            try {
                maxnum = Integer.parseInt(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.JK_PLAN_MAX_CLUSTER).getCode());
                distance = Integer.parseInt(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.JK_PLAN_DISTANCE_CLUSTER).getCode());
            }catch (Exception e){
            }
            List<JkSketchCustomer> allCustomerList = model.getCustomerList();
            //两公里内数据客户合并
//            List<SketchCateModel> customerList =Clustering.clusterPoints(allCustomerList,1000);
            List<SketchCateModel> customerList = new ClusterCustomKMeans().clusterPoints(allCustomerList,maxnum,distance);
            List<JkLine> lineList = model.getLineList();
            TspSolver.DataModel dataModel = new TspSolver.DataModel();
            int vehicleNumber1 = lineList.size();//线路数量
            long[] vehicleCapacities1=new long[lineList.size()];//每辆车的最大订单量限制
            long[] vehicleMaxNodes=new long[lineList.size()];//每辆车的最大订单量限制
            long[] demands1 = new long[customerList.size()+1]; //各个点的订单量
            long[] demands2 = new long[customerList.size()+1]; //各个点的客戶量
            long[][] distanceMatrix1 = new long[customerList.size()+1][customerList.size()+1];
            distanceMatrix1[0][0] = 0;
            demands1[0] =0;//原点
            demands2[0] =0;//原点
            for (int i = 0; i < customerList.size(); i++) {
                List<DistanceMapParam>  disList =  customerList.get(i).getStartPoint().getDistanceMapParamList();
                distanceMatrix1[0][i+1] =  disList.get(0).getDistance();
                distanceMatrix1[i+1][0] = disList.get(disList.size() -1).getDistance();
                demands1[i+1] = Constants.formatBigdecimal( customerList.get(i).getTotalNum()).longValue();  //各个点的订单量
                demands2[i+1] = customerList.get(i).getTotalCustomer();  //各个点的订单量
                for (int j = 0; j < customerList.size(); j++) {
                    distanceMatrix1[i+1][j+1] =disList.get(j+1).getDistance() ;
                }
            }
            for (int i = 0; i < lineList.size(); i++) {
                vehicleCapacities1[i] = lineList.get(i).getMaxOrder();//每辆车的最大订单量限制
                vehicleMaxNodes[i] =lineList.get(i).getMaxCustomer();//最大客户数
            }
            //构造优化数据模型
            dataModel.initDataInfo(vehicleNumber1,demands1,demands2,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
            TspSolver.startSearch(dataModel);
            JkSketch nowModel = jkSketchMapper.selectById(model.getId());
            if(StringUtils.equals(model.getJobId(), nowModel.getJobId())){
               log.error( "优化结果已失效,非最近一次操作!");
               return;
            }
            dealSearchSolution(model,  customerList,dataModel);
        }catch (Exception e){
            e.printStackTrace();
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
                    .eq(JkSketch::getId,model.getId() )
                    .eq(JkSketch::getJobId,model.getJobId() )
                    .set(JkSketch::getPlanLineInfo,"最近一次线路优化失败!"+e.getMessage())
                    .set(JkSketch::getStatus,Constants.THREE)
                    .set(JkSketch::getPlanLineEndDate,new Date()));
        }
    }
    private List<JkSketchCustomer> getCustomerCateList(List<JkSketchCustomer> allCustomerList) {
        List<JkSketchCustomer>  list = new ArrayList<>();
        return  list;
    }
    /**
     * å¼€å§‹å¼‚步执行线路优化任务
     * @param model
     */
    @Override
    @Async
    public void startUpdateLineAsyncOld(JkSketch model) {
        try {
            List<JkSketchCustomer> customerList = model.getCustomerList();
            List<JkLine> lineList = model.getLineList();
            TspSolver.DataModel dataModel = new TspSolver.DataModel();
@@ -515,6 +583,7 @@
            long[] vehicleCapacities1=new long[lineList.size()];//每辆车的最大订单量限制
            long[] vehicleMaxNodes=new long[lineList.size()];//每辆车的最大订单量限制
            long[] demands1 = new long[customerList.size()+1]; //各个点的订单量
            long[] demands2 = new long[customerList.size()+1]; //各个点的客户量
            long[][] distanceMatrix1 = new long[customerList.size()+1][customerList.size()+1];
            distanceMatrix1[0][0] = 0;
            demands1[0] =0;//原点
@@ -523,6 +592,7 @@
                distanceMatrix1[0][i+1] =  disList.get(0).getDistance();
                distanceMatrix1[i+1][0] = disList.get(disList.size() -1).getDistance();
                demands1[i+1] = Constants.formatBigdecimal( customerList.get(i).getTotalNum()).longValue();  //各个点的订单量
                demands2[i+1] =1;  //各个点的订单量
                for (int j = 0; j < customerList.size(); j++) {
                    distanceMatrix1[i+1][j+1] =disList.get(j+1).getDistance() ;
                }
@@ -542,9 +612,9 @@
                vehicleMaxNodes[i] =lineList.get(i).getMaxCustomer();//最大客户数
            }
            //构造优化数据模型
            dataModel.initDataInfo(vehicleNumber1,demands1,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
            dataModel.initDataInfo(vehicleNumber1,demands1,demands2,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
            TspSolver.startSearch(dataModel);
            dealSearchSolution(model,dataModel);
            dealSearchSolutionOld(model,dataModel);
        }catch (Exception e){
            e.printStackTrace();
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
@@ -563,6 +633,131 @@
    @Override
    @Async
    public  void startEditSketchLineAsync(JkSketch model) {
        boolean success = true;
        int totalDistance = 0;
        List<JkSketchLine> lineList = model.getSketchLineList();
        try {
            MPJLambdaWrapper<JkSketchCustomer> queryWrapper = new MPJLambdaWrapper<>();
            queryWrapper.selectAll(JkSketchCustomer.class )
                    .selectAs(JkCustomer::getName,JkSketchCustomer::getName)
                    .selectAs(JkCustomer::getCode,JkSketchCustomer::getCode)
                    .selectAs(JkCustomer::getDistance,JkSketchCustomer::getDistanceJson)
                    .selectAs(JkCustomer::getLongitude,JkSketchCustomer::getLongitude)
                    .selectAs(JkCustomer::getLatitude,JkSketchCustomer::getLatitude)
                    .selectAs(JkCustomer::getStartDistance,JkSketchCustomer::getStartDistance)
                    .selectAs(JkCustomer::getEndDistance,JkSketchCustomer::getEndDistance)
                    .leftJoin(JkCustomer.class,JkCustomer::getId,JkSketchCustomer::getCustomerId )
                    .eq(JkSketchCustomer::getType, Constants.equalsInteger(model.getOptStatus(),Constants.ONE)?1:0)
                    .eq(JkSketchCustomer::getSketchId, model.getId())
                    .eq(JkSketchCustomer::getIsdeleted,Constants.ZERO)
                    .orderByAsc(JkSketchCustomer::getSortnum);
            List<JkSketchCustomer> customerList = jkSketchCustomerMapper.selectJoinList(JkSketchCustomer.class,queryWrapper);
            if(customerList == null ||customerList.size() ==0){
                throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"该线路客户信息为空,不满足优化条件!");
            }
            int maxnum =100,distance = 500;
            try {
                maxnum = Integer.parseInt(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.JK_PLAN_MAX_CLUSTER).getCode());
                distance = Integer.parseInt(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.JK_PLAN_DISTANCE_CLUSTER).getCode());
            }catch (Exception e){
            }
            int totalNum = 0;
            for(JkSketchLine line : lineList){
                //完善线路客户优化参数
                List<JkSketchCustomer> customerListParam =  line.getCustomerList() ;
                if(customerListParam ==null || customerListParam.size()==0){
                    throw  new BusinessException(ResponseStatus.BAD_REQUEST);
                }
                initSketchCustomerListParam(line.getCustomerList(),customerList) ;
                totalNum += customerListParam.size();
            }
            if(totalNum != Constants.formatIntegerNum(model.getOrderNum())){
                throw  new BusinessException(ResponseStatus.BAD_REQUEST);
            }
            for(JkSketchLine line : lineList){
                //逐个路线优化
                List<JkSketchCustomer> allCustomerListParam =  line.getCustomerList() ;
//                List<SketchCateModel> customerListParam = Clustering.clusterPoints(allCustomerListParam,1000);
                List<SketchCateModel> customerListParam = new ClusterCustomKMeans().clusterPoints(allCustomerListParam,maxnum,distance);
                TspSolver.DataModel dataModel = new TspSolver.DataModel();
                int vehicleNumber1 = 1;//线路数量
                long[] vehicleCapacities1=new long[]{line.getMaxOrder()};//每辆车的最大订单量限制
                long[] vehicleMaxNodes=new long[]{line.getMaxCustomer()};//每辆车的最大订单量限制
                long[] demands1 = new long[customerListParam.size()+1]; //各个点的订单量
                long[] demands2 = new long[customerListParam.size()+1]; //各个点的订单量
                long[][] distanceMatrix1 = new long[customerListParam.size()+1][customerListParam.size()+1];
                distanceMatrix1[0][0] = 0;
                demands1[0] =0;//原点
                for (int i = 0; i < customerListParam.size(); i++) {
                    List<DistanceMapParam>  disList =  customerListParam.get(i).getStartPoint().getDistanceMapParamList();
                    distanceMatrix1[0][i+1] =  disList.get(0).getDistance();
                    distanceMatrix1[i+1][0] = disList.get(disList.size() -1).getDistance();
                    demands1[i+1] = Constants.formatBigdecimal( customerListParam.get(i).getTotalNum()).longValue();  //各个点的订单量
                    demands2[i+1] =   customerListParam.get(i).getTotalCustomer() ;  //各个点的订单量
                    for (int j = 0; j < customerListParam.size(); j++) {
                        distanceMatrix1[i+1][j+1] =disList.get(j+1).getDistance() ;
                    }
                }
                //构造优化数据模型
                dataModel.initDataInfo(vehicleNumber1,demands1,demands2,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
                TspSolver.startSearch(dataModel);
                if(dataModel.getSolutions()==null || dataModel.getSolutions().size()==0){
                    throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"线路【"+line.getLineName()+"】调整失败 ï¼ŒæœªèŽ·å¾—æœ€ä¼˜äº¤é€šè§„åˆ’æ–¹æ¡ˆï¼");
                }
                JkSketch nowModel = jkSketchMapper.selectById(model.getId());
                if(StringUtils.equals(model.getJobId(), nowModel.getJobId())){
                    throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"线路【"+line.getLineName()+"】调整失败 ï¼Œéžæœ€è¿‘一次操作!");
                }
                TspSolverSolutions so = dataModel.getSolutions().get(0);
                List<Integer> routes = so.getRouteIndex();
                totalDistance += so.getDistance();
                if(routes.size() <=2) {
                    throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"线路【"+line.getLineName()+"】调整失败 ï¼ŒæœªèŽ·å¾—æœ€ä¼˜äº¤é€šè§„åˆ’æ–¹æ¡ˆï¼");
                }
                int index = 0;
                line.setDistance(so.getDistance());
                //有效路径
                for (Integer cIndex : routes){
                    if(cIndex ==0){
                        continue; //起始点不处理
                    }
                    SketchCateModel cateModel = customerListParam.get(cIndex-1);
                    for(int t=0;t<cateModel.getCustomerList().size();t++){
                        JkSketchCustomer customer = cateModel.getCustomerList().get(t);
                        customer.setSortnum(index++);
                        if(t < cateModel.getCustomerList().size()-1){
                            long tdistance = (long)Clustering.distanceTo(customer, cateModel.getCustomerList().get(t+1));
                            line.setDistance(Constants.formatLongNum(line.getDistance()) +tdistance);
                            totalDistance  += tdistance;
                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            success =false;
        }finally {
            if(success){
                List<JkSketchCustomer> allList = new ArrayList<>();
                for(JkSketchLine line : lineList){
                    allList.addAll(line.getCustomerList());
                }
                jkSketchLineMapper.updateById(lineList);
                jkSketchCustomerMapper.updateById(allList);
            }
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
                    .eq(JkSketch::getId,model.getId() )
                    .eq(JkSketch::getJobId,model.getJobId() )
                    .set(success,JkSketch::getDistance,totalDistance)
                    .set(JkSketch::getPlanLineInfo,success?"最近一次线路调整成功":"最近一次线路调整失败!")
                    .set(JkSketch::getStatus,success?Constants.TWO:Constants.THREE)
                    .set(JkSketch::getPlanLineEndDate,new Date()));
        }
    }
    @Override
    @Async
    public  void startEditSketchLineAsyncOld(JkSketch model) {
        boolean success = true;
        int totalDistance = 0;
        List<JkSketchLine> lineList = model.getSketchLineList();
@@ -607,6 +802,7 @@
                long[] vehicleCapacities1=new long[]{line.getMaxOrder()};//每辆车的最大订单量限制
                long[] vehicleMaxNodes=new long[]{line.getMaxCustomer()};//每辆车的最大订单量限制
                long[] demands1 = new long[customerListParam.size()+1]; //各个点的订单量
                long[] demands2 = new long[customerListParam.size()+1]; //各个点的订单量
                long[][] distanceMatrix1 = new long[customerListParam.size()+1][customerListParam.size()+1];
                distanceMatrix1[0][0] = 0;
                demands1[0] =0;//原点
@@ -615,12 +811,13 @@
                    distanceMatrix1[0][i+1] =  disList.get(0).getDistance();
                    distanceMatrix1[i+1][0] = disList.get(disList.size() -1).getDistance();
                    demands1[i+1] = Constants.formatBigdecimal( customerListParam.get(i).getTotalNum()).longValue();  //各个点的订单量
                    demands2[i+1] = 1;  //各个点的订单量
                    for (int j = 0; j < customerListParam.size(); j++) {
                        distanceMatrix1[i+1][j+1] =disList.get(j+1).getDistance() ;
                    }
                }
                //构造优化数据模型
                dataModel.initDataInfo(vehicleNumber1,demands1,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
                dataModel.initDataInfo(vehicleNumber1,demands1,demands2,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
                TspSolver.startSearch(dataModel);
                if(dataModel.getSolutions()==null || dataModel.getSolutions().size()==0){
                    throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"线路【"+line.getLineName()+"】调整失败 ï¼ŒæœªèŽ·å¾—æœ€ä¼˜äº¤é€šè§„åˆ’æ–¹æ¡ˆï¼");
@@ -730,7 +927,7 @@
                DistanceMapParam t = new DistanceMapParam();
                t.setId(cm.getCustomerId());
                 DistanceMapParam param = getParamByCustomerIds( cm.getCustomerId(),distanceMapParamList);
                if(param!=null){//如果之前已经获取过
                if(param!=null && t.getDistance()>0){//如果之前已经获取过
                    t = param;
                }else{
                    //如果未规划,按照直线距离
@@ -758,14 +955,27 @@
                int index =0;
                for(JkSketchCustomer c : customers){
                    if(index ==0){
                        if(Constants.formatLongNum(c.getStartDistance()) >0){
                        lineDistance+= Constants.formatLongNum(c.getStartDistance());
                        }else{
                            lineDistance += DistanceCalculator.calculateDistanceDecinal(c.getLatitude(),c.getLongitude(),cLatitude,cLongitude);
                        }
                    }
                    if(index == customers.size()-1){
                        if(Constants.formatLongNum(c.getEndDistance())>0){
                        lineDistance+= Constants.formatLongNum(c.getEndDistance());
                        }else{
                            lineDistance += DistanceCalculator.calculateDistanceDecinal(c.getLatitude(),c.getLongitude(),cLatitude,cLongitude);
                        }
                        break;
                    }
                    DistanceMapParam param = getParamByCustomerIds( customers.get(index+1).getCustomerId(),getListFromJsonStr(c.getDistanceJson()));
                    lineDistance += param.getDistance();
                    JkSketchCustomer end = customers.get(index+1);
                    DistanceMapParam param1 = getParamByCustomerIds(end.getCustomerId(),getListFromJsonStr(c.getDistanceJson()));
                    if(param1 !=null && param1.getDistance()>0){
                        lineDistance += param1.getDistance();
                    }else{
                        lineDistance += DistanceCalculator.calculateDistanceDecinal(c.getLatitude(),c.getLongitude(),end.getLatitude(),end.getLongitude());
                    }
                    index++;
                  /*  for(JkSketchCustomer cm : customers){
                        DistanceMapParam param = getParamByCustomerIds( cm.getCustomerId(),getListFromJsonStr(c.getDistanceJson()));
@@ -815,7 +1025,116 @@
        return new ArrayList<>();
    }
    private void dealSearchSolution(JkSketch model, TspSolver.DataModel dataModel) {
    private void dealSearchSolution(JkSketch model, List<SketchCateModel> customerList,TspSolver.DataModel dataModel) {
        Date date = new Date();
        List<TspSolverSolutions> solutions = dataModel.getSolutions();
        List<JkSketchLine> sketchLineList = new ArrayList<>();
        List<JkSketchCustomer> sketchCustomerList  = new ArrayList<>();
        long totalDistance = 0l;
        if(solutions!=null && solutions.size()>0){
            for(TspSolverSolutions so : solutions){
                List<Integer> routes = so.getRouteIndex();
                totalDistance+= so.getDistance();
                if(routes.size() <=2) {
                    continue;//无客户的非有效路线
                }
                JkLine  line =model.getLineList().get(so.getLineIndex());
                JkSketchLine tModel =  new JkSketchLine();
                tModel.setSketchId(model.getId());
                tModel.setCreator(model.getEditor());
                tModel.setCreateDate(date);
                tModel.setLineId(line.getId());
                tModel.setTotalNum(new BigDecimal(0));
                tModel.setDateInfo(model.getDateInfo());
                tModel.setType(Constants.ONE);
                tModel.setSortnum(sketchLineList.size()+1);
                tModel.setEditDate(tModel.getCreateDate());
                tModel.setEditor(tModel.getCreator());
                tModel.setIsdeleted(Constants.ZERO);
                tModel.setDistance(so.getDistance());//
                sketchLineList.add(tModel);
                tModel.setCustomerList( new ArrayList<>());
                //有效路径
                for (Integer cIndex : routes){
                    if(cIndex ==0){
                        continue; //起始点不处理
                    }
//                    JkSketchCustomer customer = model.getCustomerList().get(cIndex-1);
//                    tModel.setTotalNum(tModel.getTotalNum().add(Constants.formatBigdecimal(customer.getTotalNum())));//送货量
                    SketchCateModel cateModel = customerList.get(cIndex-1);
                    tModel.setTotalNum(tModel.getTotalNum().add(Constants.formatBigdecimal(cateModel.getTotalNum())));//送货量
                    for(int t=0;t<cateModel.getCustomerList().size();t++){
                        JkSketchCustomer customer = cateModel.getCustomerList().get(t);
                        JkSketchCustomer cModel =  new JkSketchCustomer();
                        cModel.setCreator(tModel.getCreator());
                        cModel.setOrderId(customer.getOrderId());
                        cModel.setCreateDate(tModel.getCreateDate());
                        cModel.setType(Constants.ONE);
                        cModel.setTotalNum(customer.getTotalNum());
                        cModel.setDateInfo(model.getDateInfo());
                        cModel.setSortnum(tModel.getCustomerList().size()+1);
                        cModel.setEditDate(tModel.getCreateDate());
                        cModel.setEditor(tModel.getCreator());
                        cModel.setIsdeleted(Constants.ZERO);
                        cModel.setSketchId(model.getId());
                        cModel.setCustomerId(customer.getCustomerId());
                        if(t < cateModel.getCustomerList().size()-1){
                            long tdistance = (long)Clustering.distanceTo(customer, cateModel.getCustomerList().get(t+1));
                            tModel.setDistance(Constants.formatLongNum(tModel.getDistance()) +tdistance);
                            totalDistance  += tdistance;
                        }
                        tModel.getCustomerList().add(cModel);
                    }
                    tModel.setOrderNum( tModel.getCustomerList().size());
                }
            }
        }
        if(sketchLineList!=null && sketchLineList.size()>0){
            jkSketchLineMapper.update(null,new UpdateWrapper<JkSketchLine>().lambda()
                    .set(JkSketchLine::getIsdeleted,Constants.ONE)
                    .eq(JkSketchLine::getIsdeleted,Constants.ZERO)
                    .eq(JkSketchLine::getType,Constants.ONE)
                    .eq(JkSketchLine::getSketchId,model.getId())
                    .eq(JkSketchLine::getDateInfo,model.getDateInfo()));
            jkSketchCustomerMapper.update(null,new UpdateWrapper<JkSketchCustomer>().lambda()
                    .set(JkSketchCustomer::getIsdeleted,Constants.ONE)
                    .eq(JkSketchCustomer::getIsdeleted,Constants.ZERO)
                    .eq(JkSketchCustomer::getType,Constants.ONE)
                    .eq(JkSketchCustomer::getSketchId,model.getId())
                    .eq(JkSketchCustomer::getDateInfo,model.getDateInfo()));
            if(sketchLineList.size()>0){
                jkSketchLineMapper.insert(sketchLineList);
            }
            for(JkSketchLine l : sketchLineList){
                if(l.getCustomerList()!=null ){
                    for(JkSketchCustomer c :l.getCustomerList()){
                        c.setSketchLineId(l.getId());
                    }
                    sketchCustomerList.addAll(l.getCustomerList());
                }
            }
            if(sketchCustomerList.size()>0){
                jkSketchCustomerMapper.insert(sketchCustomerList);
            }
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
                    .eq(JkSketch::getId,model.getId() )
//                    .eq(JkSketch::getJobId,model.getJobId() )
                    .set(JkSketch::getLineNum,sketchLineList.size() )
                    .set(JkSketch::getOptStatus,Constants.ONE)//已生成优化线路
                    .set(JkSketch::getDistance,totalDistance)
                    .set(JkSketch::getPlanLineInfo,"最近一次线路优化成功,优化后总距离:"+(totalDistance/1000)+"公里!")
                    .set(JkSketch::getStatus,Constants.TWO)
                    .set(JkSketch::getPlanLineEndDate,date));
        }else{
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
                    .eq(JkSketch::getId,model.getId() )
//                    .eq(JkSketch::getJobId,model.getJobId() )
                    .set(JkSketch::getPlanLineInfo,"最近一次线路优化失败,未找到最优路线!")
                    .set(JkSketch::getStatus,Constants.THREE)
                    .set(JkSketch::getPlanLineEndDate,date));
        }
    }
    private void dealSearchSolutionOld(JkSketch model, TspSolver.DataModel dataModel) {
        Date date = new Date();
        List<TspSolverSolutions> solutions = dataModel.getSolutions();
        List<JkSketchLine> sketchLineList = new ArrayList<>();
@@ -876,11 +1195,13 @@
                    .set(JkSketchLine::getIsdeleted,Constants.ONE)
                    .eq(JkSketchLine::getIsdeleted,Constants.ZERO)
                    .eq(JkSketchLine::getType,Constants.ONE)
                    .eq(JkSketchLine::getSketchId,model.getId())
                    .eq(JkSketchLine::getDateInfo,model.getDateInfo()));
            jkSketchCustomerMapper.update(null,new UpdateWrapper<JkSketchCustomer>().lambda()
                    .set(JkSketchCustomer::getIsdeleted,Constants.ONE)
                    .eq(JkSketchCustomer::getIsdeleted,Constants.ZERO)
                    .eq(JkSketchCustomer::getType,Constants.ONE)
                    .eq(JkSketchCustomer::getSketchId,model.getId())
                    .eq(JkSketchCustomer::getDateInfo,model.getDateInfo()));
            if(sketchLineList.size()>0){
                jkSketchLineMapper.insert(sketchLineList);
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/hksync/HkSyncBaseServiceImpl.java
@@ -84,6 +84,11 @@
        return null;
    }
    @Override
    public String syncHkBroadcastDevice(Device param){
        return null;
    }
    @Override
//    @Async
    public String syncPrivilege(PrivilegeGroupRequest param){
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/hksync/HkSyncDeviceServiceImpl.java
@@ -197,6 +197,48 @@
            Constants.DEALING_HK_SYNCDEVICE =false;
        }
    }
    @Override
//    @Async
    public String syncHkBroadcastDevice(Device param){
        if(Constants.DEALING_HK_SYNCDEVICE){
            throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "同步任务正在执行哦,请稍后查看结果!") ;
        }
        Constants.DEALING_HK_SYNCDEVICE =true;
        try {
            List<Device> deleteList = new ArrayList<>();
            List<Device> addList = new ArrayList<>();
            List<Device> editList = new ArrayList<>();
            Date date = new Date();
            //查询全部广播点设备数据
            List<Device> allList = deviceMapper.selectList(new QueryWrapper<Device>().lambda()
                    .eq(Device::getType,Constants.FOUR));
            List<FetchAudioDeviceInfoResponse> allHkList = getAllBroadcastDeviceList();
            /**
             * èŽ·å–å¢žåˆ æ”¹æ•°æ®é›†åˆ
             */
            getBroadcastDeviceDataChangeList(allList,allHkList,addList,editList,deleteList,date);
            if(deleteList.size()>0){
                //逻辑删除
                for(Device d : deleteList){
                    deviceMapper.updateById(d);
                }
            }
            if(addList.size()>0){
                deviceMapper.insert(addList);
            }
            if(editList.size()>0){
                for(Device d : editList){
                    deviceMapper.updateById(d);
                }
            }
            return "同步数据:新增【"+addList.size()+"】条,更新【"+editList.size()+"】条,删除【"+deleteList.size()+"】条";
        }catch (Exception e){
            e.printStackTrace();
            throw  new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), "同步失败!");
        }finally {
            Constants.DEALING_HK_SYNCDEVICE =false;
        }
    }
    private void getBroadcastDataChangeList(List<Device> allList, List<FetchAudioChannelInfoResponse> allHkList, List<Device> addList, List<Device> editList, List<Device> deleteList, Date date) {
        if(allHkList!=null && allHkList.size()>0){
@@ -218,6 +260,27 @@
        }
        //判断获取删除的门禁设备,逻辑删除
        getDeleteBroadcastList(allList,allHkList,deleteList,date);
    }
    private void getBroadcastDeviceDataChangeList(List<Device> allList, List<FetchAudioDeviceInfoResponse> allHkList, List<Device> addList, List<Device> editList, List<Device> deleteList, Date date) {
        if(allHkList!=null && allHkList.size()>0){
            //获取海康全部门禁组数据
            for(FetchAudioDeviceInfoResponse device : allHkList){
                Device model = getExistedBroadcastDevice(device,allList);
                if(model !=null){
                    //如果已存在,则更新数据
                    model =  initBroadcastDeviceDataByHkData(model,device,date);
                    editList.add(model);
                }else{
                    //如果不存在,则新增数据
                    model = new Device();
                    model =  initBroadcastDeviceDataByHkData(model,device,date);
                    model.setIsEntrance(Constants.ZERO);
                    addList.add(model);
                }
            }
        }
        //判断获取删除的门禁设备,逻辑删除
        getDeleteBroadcastDeviceList(allList,allHkList,deleteList,date);
    }
    @Override
@@ -332,6 +395,39 @@
                throw  new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), "对不起,海康同步数据失败~");
            }
            BaseListPageResponse<FetchAudioChannelInfoResponse> r = response.getData();
            curTotal += 100;
            if(curTotal >= r.getTotal()){
                hasNext = false;
            }
            if(r.getList() == null || r.getList().size()==0){
                hasNext =false;
            }else{
                allDoorList.addAll(r.getList());
            }
            curPage++;
        }
        return  allDoorList;
    }
    /**
     * èŽ·å–å…¨éƒ¨å¹¿æ’­ç‚¹æ•°æ®
     * @return
     */
    public  List<FetchAudioDeviceInfoResponse>  getAllBroadcastDeviceList(){
        List<FetchAudioDeviceInfoResponse> allDoorList = new ArrayList<>();
        Date date = new Date();
        boolean hasNext = true;
        int curTotal = 0;
        int curPage = 1;
        while (hasNext){
            //分页遍历循环查询所有门禁设备数据
            FetchAudioDeviceRequest param = new FetchAudioDeviceRequest();
            param.setPageNo(curPage);
            param.setPageSize(100);
            BaseResponse<BaseListPageResponse<FetchAudioDeviceInfoResponse>>  response = HKService.fetchAudioDevice(param);
            if(response == null || !StringUtils.equals(response.getCode(), HKConstants.RESPONSE_SUCCEE)){
                throw  new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), "对不起,海康同步数据失败~");
            }
            BaseListPageResponse<FetchAudioDeviceInfoResponse> r = response.getData();
            curTotal += 100;
            if(curTotal >= r.getTotal()){
                hasNext = false;
@@ -463,6 +559,21 @@
        model.setResourceType(device.getAudioChannelType());
        model.setStatus(device.getState());
        model.setRegionName(device.getRegionName());
        return  model;
    }
    private Device initBroadcastDeviceDataByHkData(Device model, FetchAudioDeviceInfoResponse device, Date date) {
        model.setIsdeleted(Constants.ZERO);
        model.setHkDate(date);
        model.setHkId(device.getDeviceIndexCode());
        model.setName(device.getDeviceName());
        model.setHkStatus(Constants.ONE);
        model.setType(Constants.THREE);
        model.setChannelNo(device.getDeviceCode());
        model.setChannelInfo(device.getDeviceType());
        model.setResourceType(device.getDeviceType());
        model.setStatus(Constants.ZERO);
        model.setRemark(device.getDesc());
        model.setRegionName(device.getRegionIndexCode());
        return  model;
    }
    private Device initDoorDataByHkData(Device model, DoorsInfoResponse door, Date date, List<AcsDeviceInfoResponse> allHkList) {
@@ -614,6 +725,17 @@
        }
        return  null;
    }
    private Device getExistedBroadcastDevice(FetchAudioDeviceInfoResponse device, List<Device> allList) {
        if(allList.size()>0){
            for(Device r : allList){
                if(StringUtils.equals(r.getHkId(), device.getDeviceIndexCode())){
                    //表示未删除
                    return  r;
                }
            }
        }
        return  null;
    }
    private Device getExistedDevice(AcsDeviceInfoResponse device, List<Device> allList) {
        if(allList.size()>0){
            for(Device r : allList){
@@ -662,6 +784,17 @@
    private void getDeleteBroadcastList(List<Device> allList,  List<FetchAudioChannelInfoResponse> allHkList,List<Device> deleteList ,Date date) {
        if(allList!=null && allList.size()>0){
            for(Device device : allList){
                if(isDeletedBroadcast(device,allHkList)){
                    device.setIsdeleted(Constants.ONE);
                    device.setEditDate(date);
                    deleteList.add(device);
                }
            }
        }
    }
    private void getDeleteBroadcastDeviceList(List<Device> allList,  List<FetchAudioDeviceInfoResponse> allHkList,List<Device> deleteList ,Date date) {
        if(allList!=null && allList.size()>0){
            for(Device device : allList){
                if(isDeletedBroadcastDevice(device,allHkList)){
                    device.setIsdeleted(Constants.ONE);
                    device.setEditDate(date);
@@ -695,7 +828,7 @@
        return  true;
    }
    private boolean isDeletedBroadcastDevice(Device device, List<FetchAudioChannelInfoResponse> allHkList) {
    private boolean isDeletedBroadcast(Device device, List<FetchAudioChannelInfoResponse> allHkList) {
        if(allHkList.size()>0){
            for(FetchAudioChannelInfoResponse r : allHkList){
                if(StringUtils.equals(device.getHkId(), r.getAudioChannelIndexCode())){
@@ -707,6 +840,18 @@
        return  true;
    }
    private boolean isDeletedBroadcastDevice(Device device, List<FetchAudioDeviceInfoResponse> allHkList) {
        if(allHkList.size()>0){
            for(FetchAudioDeviceInfoResponse r : allHkList){
                if(StringUtils.equals(device.getHkId(), r.getDeviceIndexCode())){
                    //表示未删除
                    return  false;
                }
            }
        }
        return  true;
    }
    private boolean isDeletedDevice(Device device, List<AcsDeviceInfoResponse> allHkList) {
        if(allHkList.size()>0){
            for(AcsDeviceInfoResponse r : allHkList){