feiyu02
2024-07-19 4e20a1aaaba1bb843820fca844c20055a33febce
1. 新增webSocket相关功能
已修改17个文件
已添加10个文件
591 ■■■■ 文件已修改
pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/SupervisionApplication.kt 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/business/location/UtilFile.kt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/log/AbstractLogInfo.kt 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/log/BizLog.kt 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/log/WorkStreamLogInfo.kt 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/common/utils/JsonUtil.kt 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/impl/ProblemlistServiceImpl.kt 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/impl/SubtaskServiceImpl.kt 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/StatisticsVo.kt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/ProblemlistController.kt 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/SubtaskController.kt 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/scheduler/ScheduleService.kt 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/socket/WsSessionManager.kt 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/socket/config/SPTextWebSocketHandler.kt 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/socket/config/WebSocketConfig.kt 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/socket/config/WsHandshakeInterceptor.kt 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/socket/processor/WebSocketReceiver.kt 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/cn/flightfeather/supervision/socket/processor/WebSocketSender.kt 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev.yml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-pro.yml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-test.yml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/log4j2.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/ds1/ProblemlistMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/cn/flightfeather/supervision/business/bgtask/AopTaskCtrlTest.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/cn/flightfeather/supervision/business/location/LocationRoadNearbyTest.kt 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -69,6 +69,11 @@
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.plugin/spring-plugin-core -->
        <dependency>
            <groupId>org.springframework.plugin</groupId>
src/main/kotlin/cn/flightfeather/supervision/SupervisionApplication.kt
@@ -2,6 +2,7 @@
import cn.flightfeather.supervision.business.datafetch.FetchController
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.ApplicationRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@@ -11,7 +12,10 @@
@SpringBootApplication
@EnableScheduling
class SupervisionApplication {
class SupervisionApplication(
    @Value("\${mode}")
    var mode: String
) {
    @Autowired
    // é™å®‰åŒºå¤œé—´æ–½å·¥è®¸å¯è¯ä¿¡æ¯èŽ·å–ä»»åŠ¡
@@ -19,7 +23,10 @@
    @Bean
    fun runner() = ApplicationRunner {
        fetchController.run()
        if (mode == "pro") {
            fetchController.run()
        }
        println("mode: $mode")
    }
}
src/main/kotlin/cn/flightfeather/supervision/business/location/UtilFile.kt
@@ -33,6 +33,7 @@
            ExcelUtil.MyCell("单位地址", colSpan = 1),
            ExcelUtil.MyCell("经度", colSpan = 1),
            ExcelUtil.MyCell("纬度", colSpan = 1),
            ExcelUtil.MyCell("区县", colSpan = 1),
            ExcelUtil.MyCell("街道", colSpan = 1),
            ExcelUtil.MyCell("常用联系人", colSpan = 1),
            ExcelUtil.MyCell("联系方式", colSpan = 1),
@@ -66,6 +67,7 @@
            scense.location ?: "",
            scense.longitude?.toDouble() ?: .0,
            scense.latitude?.toDouble() ?: .0,
            scense.districtname ?: "",
            scense.townname ?: "",
            scense.contacts ?: "",
            scense.contactst ?: ""
src/main/kotlin/cn/flightfeather/supervision/common/log/AbstractLogInfo.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.flightfeather.supervision.common.log
import cn.flightfeather.supervision.common.utils.JsonUtil
import com.fasterxml.jackson.annotation.JsonFormat
import com.google.gson.Gson
import java.time.LocalDateTime
/**
 * æ—¥å¿—信息结构
 * @date 2024/7/19
 * @author feiyu02
 */
open class AbstractLogInfo() {
    constructor(event: String) : this() {
        this.event = event
    }
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    var time: LocalDateTime = LocalDateTime.now()
    var event: String = ""
    fun toJsonStr(): String {
        return JsonUtil.gson.toJson(this)
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/log/BizLog.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package cn.flightfeather.supervision.common.log
import cn.flightfeather.supervision.socket.processor.WebSocketSender
import org.springframework.stereotype.Component
/**
 * ä¸šåŠ¡æ—¥å¿—ç®¡ç†
 * ç›¸æ¯”于log4J2等框架日志,此处日志需要记录至数据库或推送至监控平台,方便浏览管理
 * @date 2024/7/19
 * @author feiyu02
 */
@Component
class BizLog(
    private val webSocketSender: WebSocketSender,
) {
    // TODO: 2024/7/19 åŽç»­éœ€æ·»åŠ æ—¥å¿—ç›¸å…³çš„æ•°æ®åº“å­˜å‚¨åŠŸèƒ½
    /**
     * è®°å½•常态日志
     */
    fun info(logInfo: AbstractLogInfo) {
        // å¹¿æ’­æ—¥å¿—
        webSocketSender.broadcast(logInfo.toJsonStr())
        // TODO: 2024/7/19 æ—¥å¿—入库
    }
    /**
     * è®°å½•错误日志
     */
    fun error() {
        // TODO: 2024/7/19 æ—¥å¿—入库
    }
}
src/main/kotlin/cn/flightfeather/supervision/common/log/WorkStreamLogInfo.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.flightfeather.supervision.common.log
import cn.flightfeather.supervision.domain.ds1.entity.Userinfo
/**
 * å·¥ä½œæ—¥å¿—
 * @date 2024/7/19
 * @author feiyu02
 */
class WorkStreamLogInfo : AbstractLogInfo {
    constructor() : super()
    constructor(userId: String?, userName: String?, type: String?, event: String) : super(event) {
        this.userId = userId
        this.userName = userName
        this.type = type
    }
    constructor(userId: String?, userName: String?, event: String) : this(userId, userName, null, event)
    constructor(userInfo: Userinfo?, event: String) : this(userInfo?.guid, userInfo?.realname, null, event)
    var userId: String? = null
    var userName: String? = null
    var type: String? = null
}
src/main/kotlin/cn/flightfeather/supervision/common/utils/JsonUtil.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package cn.flightfeather.supervision.common.utils
import com.google.gson.Gson
import com.google.gson.GsonBuilder
object JsonUtil {
    val gson: Gson = GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create()
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/impl/ProblemlistServiceImpl.kt
@@ -1,6 +1,8 @@
package cn.flightfeather.supervision.lightshare.service.impl
import cn.flightfeather.supervision.common.exception.BizException
import cn.flightfeather.supervision.common.log.BizLog
import cn.flightfeather.supervision.common.log.WorkStreamLogInfo
import cn.flightfeather.supervision.domain.ds1.entity.Problemlist
import cn.flightfeather.supervision.domain.ds1.entity.Scense
import cn.flightfeather.supervision.common.utils.Constant
@@ -42,6 +44,7 @@
    private val taskRep: TaskRep,
    private val subTaskRep: SubTaskRep,
    private val problemRep: ProblemRep,
    private val bizLog: BizLog,
) : ProblemlistService {
    @Resource
@@ -374,14 +377,21 @@
            return BaseResponse(false, "非法的操作指令")
        }
        val p = problemlistMapper.selectByPrimaryKey(pId) ?: return BaseResponse(false, "问题不存在")
        val subtask = p.stguid?.let { subTaskRep.findOne(it) }
        val response = BaseResponse<String>(true)
        var event = ""
        p.apply {
            this.remark = userName
            if (extension3 == Constant.PROBLEM_UNCHECKED) {
                event = "在${subtask?.scensename}审核了一个问题"
                when (action) {
                    0.toByte() -> extension3 = Constant.PROBLEM_CHECK_PASS
                    1.toByte() -> extension3 = Constant.PROBLEM_CHECK_FAIL
                    0.toByte() -> {
                        extension3 = Constant.PROBLEM_CHECK_PASS
                    }
                    1.toByte() -> {
                        extension3 = Constant.PROBLEM_CHECK_FAIL
                    }
                    2.toByte(),
                    3.toByte(),
                    -> {
@@ -403,6 +413,7 @@
                            } else {
                                Constant.CHANGE_CHECK_FAIL
                            }
                            event = "在${subtask?.scensename}审核了一个整改"
                        } else {
                            response.success = false
                            response.message = "问题还未整改,无法进行整改审核!操作无效"
@@ -410,6 +421,7 @@
                    }
                }
            } else if (extension3 == Constant.CHANGE_UNCHECKED) {
                event = "在${subtask?.scensename}审核了一个整改"
                when (action) {
                    0.toByte(),
                    1.toByte(),
@@ -439,6 +451,8 @@
            if (r != 1) {
                response.success = false
                response.message = "问题更新失败!"
            } else {
                bizLog.info(WorkStreamLogInfo(subtask?.executorguids, subtask?.executorrealtimes, event))
            }
        }
        return response
@@ -518,6 +532,12 @@
            inspectionMapper.updateByPrimaryKey(inspection)
        }
        problemlist.stguid?.let {
            val subtask = subTaskRep.findOne(it)
            val event = "在${subtask?.scensename}新增一个问题"
            bizLog.info(WorkStreamLogInfo(subtask?.executorguids, subtask?.executorrealtimes, event))
        }
        return BaseResponse(true)
    }
src/main/kotlin/cn/flightfeather/supervision/lightshare/service/impl/SubtaskServiceImpl.kt
@@ -431,6 +431,7 @@
                if (!bool){
                    daytaskVo.runingstatus = Constant.TaskProgress.RUNINGSTATUS3.text
                }
            }
            //两者状态相同时不做修改,其余情况日任务都为正在执行
            else if (subtask.status != daytaskVo.runingstatus){
@@ -442,30 +443,6 @@
            //****************************************************************************************
            subtaskMapper.updateByPrimaryKeySelective(subtask)
        }
        //判断对应顶层任务的执行状态
//        val daytaskVolist = taskService.getDayTaskByTaskID(toptaskVo.tguid!!)
//        val iterator: Iterator<TaskVo> = daytaskVolist.iterator()
//        if (daytaskVo.runingstatus == Constant.TaskProgress.RUNINGSTATUS3.text
//                && toptaskVo.runingstatus == Constant.TaskProgress.RUNINGSTATUS2.text) {
//            var bool = false
//            while (iterator.hasNext()) {
//                val tmp = iterator.next()
//                if (tmp.runingstatus != Constant.TaskProgress.RUNINGSTATUS3.text) {
//                    bool = true
//                    break
//                }
//            }
//            if (!bool){
//                toptaskVo.runingstatus = Constant.TaskProgress.RUNINGSTATUS3.text
//            }
//        }
//        else if (daytaskVo.runingstatus != toptaskVo.runingstatus){
//            toptaskVo.runingstatus = Constant.TaskProgress.RUNINGSTATUS2.text
//        }
//        val toptask = Task()
//        BeanUtils.copyProperties(toptaskVo, toptask)
//        taskMapper.updateByPrimaryKeySelective(toptask)
        //对已结束的子任务进行自动评分
        if (subtask.status == Constant.TaskProgress.RUNINGSTATUS3.text) {
src/main/kotlin/cn/flightfeather/supervision/lightshare/vo/StatisticsVo.kt
@@ -1,5 +1,6 @@
package cn.flightfeather.supervision.lightshare.vo
import cn.flightfeather.supervision.domain.ds1.entity.Problemlist
import com.fasterxml.jackson.annotation.JsonInclude
import java.util.*
@@ -16,4 +17,6 @@
    var changeCount: Int = 0
    var problems: List<Problemlist>? = null
}
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/ProblemlistController.kt
@@ -1,7 +1,10 @@
package cn.flightfeather.supervision.lightshare.web
import cn.flightfeather.supervision.common.log.BizLog
import cn.flightfeather.supervision.common.log.WorkStreamLogInfo
import cn.flightfeather.supervision.domain.ds1.entity.Problemlist
import cn.flightfeather.supervision.lightshare.service.ProblemlistService
import cn.flightfeather.supervision.lightshare.service.SubtaskService
import cn.flightfeather.supervision.lightshare.vo.*
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
@@ -12,7 +15,10 @@
@Api(tags = ["ProblemlistController"], description = "监管问题API接口")
@RestController
@RequestMapping("/problemlist")
class ProblemlistController(val problemlistService: ProblemlistService) {
class ProblemlistController(
    val problemlistService: ProblemlistService,
    val subtaskService: SubtaskService, private val bizLog: BizLog,
) {
    @GetMapping
    fun getAll() = problemlistService.findAll()
@@ -20,7 +26,15 @@
    fun add(@RequestBody problemlist: Problemlist) = problemlistService.save(problemlist)
    @PostMapping
    fun update(@RequestBody problemlist: Problemlist) = problemlistService.update(problemlist)
    fun update(@RequestBody problemlist: Problemlist):Int{
        val res = problemlistService.update(problemlist)
        problemlist.stguid?.let {
            val subtask = subtaskService.findByID(it)
            val event = "在${subtask.scensename}新增一个问题"
            bizLog.info(WorkStreamLogInfo(subtask.executorguids, subtask.executorrealtimes, event))
        }
        return res
    }
    @GetMapping("/{id}")
    fun getById(@PathVariable id: String) = problemlistService.findByID(id)
src/main/kotlin/cn/flightfeather/supervision/lightshare/web/SubtaskController.kt
@@ -1,5 +1,8 @@
package cn.flightfeather.supervision.lightshare.web
import cn.flightfeather.supervision.common.log.BizLog
import cn.flightfeather.supervision.common.log.WorkStreamLogInfo
import cn.flightfeather.supervision.common.utils.Constant
import cn.flightfeather.supervision.domain.ds1.entity.Subtask
import cn.flightfeather.supervision.lightshare.service.SubtaskService
import cn.flightfeather.supervision.lightshare.vo.AreaVo
@@ -13,7 +16,7 @@
@Api(tags = ["SubtaskController"], description = "巡查子任务API接口")
@RestController
@RequestMapping("/subtask")
class SubtaskController(val subtaskService: SubtaskService) {
class SubtaskController(val subtaskService: SubtaskService, private val bizLog: BizLog) {
    @GetMapping
    fun getAll() = subtaskService.findAll()
@@ -24,7 +27,17 @@
    fun addList(@RequestBody subtasklist: List<Subtask>) = subtaskService.saveList(subtasklist)
    @PostMapping
    fun update(@RequestBody subtask: Subtask) = subtaskService.update(subtask)
    fun update(@RequestBody subtask: Subtask): Int {
        val res = subtaskService.update(subtask)
        if (subtask.status == Constant.TaskProgress.RUNINGSTATUS3.text) {
            val event = "在${subtask.scensename}结束巡查"
            bizLog.info(WorkStreamLogInfo(subtask.executorguids, subtask.executorrealtimes, event))
        }else if (subtask.status == Constant.TaskProgress.RUNINGSTATUS2.text) {
            val event = "在${subtask.scensename}开始巡查"
            bizLog.info(WorkStreamLogInfo(subtask.executorguids, subtask.executorrealtimes, event))
        }
        return res
    }
    @GetMapping("/{id}")
    fun getById(@PathVariable id: String) = subtaskService.findByID(id)
src/main/kotlin/cn/flightfeather/supervision/scheduler/ScheduleService.kt
@@ -6,9 +6,11 @@
import cn.flightfeather.supervision.lightshare.vo.AreaVo
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.scheduling.annotation.Async
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import java.time.LocalDate
import java.time.LocalDateTime
/**
@@ -23,6 +25,8 @@
 */
@Component
class ScheduleService(
    @Value("\${mode}")
    var mode: String,
    private val taskFinishSubtask: TaskFinishSubtask,
    private val taskFinishTopTask: TaskFinishTopTask,
    private val aopTaskCtrl: AopTaskCtrl,
@@ -32,6 +36,8 @@
    @Async
    @Scheduled(cron = "0 0 0 * * *")
    fun eachDay() {
        if (mode != "pro") return
        logger.info("=====>>>>>每日任务执行 {}", System.currentTimeMillis())
        taskFinishSubtask.handle()
        logger.info("=====>>>>>每日任务结束 {}", System.currentTimeMillis())
@@ -40,18 +46,22 @@
    @Async
    @Scheduled(cron = "0 0 0 * * MON")
    fun eachWeek() {
        if (mode != "pro") return
        logger.info("=====>>>>>每周任务执行 {}", System.currentTimeMillis())
        // æ‰§è¡Œä¸Šå‘¨çš„自评任务
        aopTaskCtrl.startAllEvaluation(LocalDateTime.now())
        aopTaskCtrl.startAllEvaluation(LocalDate.now().atStartOfDay())
        logger.info("=====>>>>>每周任务结束 {}", System.currentTimeMillis())
    }
    @Async
    @Scheduled(cron = "0 0 0 2 * *")
    fun eachMonth() {
        if (mode != "pro") return
        logger.info("=====>>>>>每月任务执行 {}", System.currentTimeMillis())
        // æ‰§è¡Œä¸Šä¸ªæœˆçš„完整自评任务
        aopTaskCtrl.startAll(LocalDateTime.now().minusMonths(1))
        aopTaskCtrl.startAll(LocalDate.now().atStartOfDay().minusMonths(1))
        // æ‰§è¡Œé¡¶å±‚任务自动结束任务
        taskFinishTopTask.handle()
        logger.info("=====>>>>>每月任务结束 {}", System.currentTimeMillis())
src/main/kotlin/cn/flightfeather/supervision/socket/WsSessionManager.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package cn.flightfeather.supervision.socket
import org.springframework.web.socket.WebSocketSession
import java.io.IOException
import java.util.concurrent.ConcurrentHashMap
/**
 *
 * @date 2024/7/19
 * @author feiyu02
 */
object WsSessionManager {
    /**
     * ä¿å­˜è¿žæŽ¥ session çš„地方
     */
    private val SESSION_POOL = ConcurrentHashMap<String, WebSocketSession>()
    /**
     * æ·»åŠ  session
     *
     * @param key
     */
    fun add(key: String, session: WebSocketSession) {
        // æ·»åŠ  session
        SESSION_POOL[key] = session
    }
    /**
     * åˆ é™¤ session,会返回删除的 session
     *
     * @param key
     * @return
     */
    fun remove(key: String?): WebSocketSession? {
        // åˆ é™¤ session
        return SESSION_POOL.remove(key)
    }
    /**
     * åˆ é™¤å¹¶åŒæ­¥å…³é—­è¿žæŽ¥
     *
     * @param key
     */
    fun removeAndClose(key: String?) {
        val session: WebSocketSession? = remove(key)
        if (session != null) {
            try {
                // å…³é—­è¿žæŽ¥
                session.close()
            } catch (e: IOException) {
                // todo: å…³é—­å‡ºçŽ°å¼‚å¸¸å¤„ç†
                e.printStackTrace()
            }
        }
    }
    /**
     * èŽ·å¾— session
     *
     * @param key
     * @return
     */
    operator fun get(key: String?): WebSocketSession? {
        // èŽ·å¾— session
        return SESSION_POOL[key]
    }
    fun eachSession(action:(session:WebSocketSession) -> Unit) {
        SESSION_POOL.forEachEntry(10){
            action(it.value)
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/socket/config/SPTextWebSocketHandler.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package cn.flightfeather.supervision.socket.config
import cn.flightfeather.supervision.socket.WsSessionManager
import cn.flightfeather.supervision.socket.processor.WebSocketReceiver
import org.springframework.stereotype.Component
import org.springframework.web.socket.CloseStatus
import org.springframework.web.socket.TextMessage
import org.springframework.web.socket.WebSocketSession
import org.springframework.web.socket.handler.TextWebSocketHandler
/**
 *
 * @date 2024/7/19
 * @author feiyu02
 */
@Component
class SPTextWebSocketHandler(private val webSocketReceiver: WebSocketReceiver) : TextWebSocketHandler() {
    /**
     * socket å»ºç«‹æˆåŠŸäº‹ä»¶
     *
     * @param session
     * @throws Exception
     */
    @Throws(Exception::class)
    override fun afterConnectionEstablished(session: WebSocketSession) {
        val sessionId = session.attributes["session_id"]
        if (sessionId != null) {
            // ç”¨æˆ·è¿žæŽ¥æˆåŠŸï¼Œæ”¾å…¥åœ¨çº¿ç”¨æˆ·ç¼“å­˜
            WsSessionManager.add(sessionId.toString(), session)
        } else {
            throw RuntimeException("用户登录已经失效!")
        }
    }
    /**
     * æŽ¥æ”¶æ¶ˆæ¯äº‹ä»¶
     *
     * @param session
     * @param message
     * @throws Exception
     */
    @Throws(Exception::class)
    override fun handleTextMessage(session: WebSocketSession, message: TextMessage) {
        // èŽ·å¾—å®¢æˆ·ç«¯ä¼ æ¥çš„æ¶ˆæ¯
        val payload = message.payload
        val sessionId = session.attributes["session_id"]
        println("server æŽ¥æ”¶åˆ° $sessionId å‘送的 $payload")
        webSocketReceiver.onReceiveMsg(payload)
//        session.sendMessage(TextMessage("server å‘送给 " + sessionId + " æ¶ˆæ¯ " + payload + " " + LocalDateTime.now()
//            .toString()))
    }
    /**
     * socket æ–­å¼€è¿žæŽ¥æ—¶
     *
     * @param session
     * @param status
     * @throws Exception
     */
    @Throws(Exception::class)
    override fun afterConnectionClosed(session: WebSocketSession, status: CloseStatus) {
        val sessionId = session.attributes["session_id"]
        if (sessionId != null) {
            // ç”¨æˆ·é€€å‡ºï¼Œç§»é™¤ç¼“å­˜
            WsSessionManager.remove(sessionId.toString())
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/socket/config/WebSocketConfig.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package cn.flightfeather.supervision.socket.config
import cn.flightfeather.supervision.scheduler.ScheduleService
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.scheduling.TaskScheduler
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
import org.springframework.web.socket.config.annotation.EnableWebSocket
import org.springframework.web.socket.config.annotation.WebSocketConfigurer
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry
import javax.annotation.Nullable
/**
 *
 * @date 2024/7/19
 * @author feiyu02
 */
@Configuration
@EnableWebSocket
class WebSocketConfig(
    private val handler: SPTextWebSocketHandler,
    private val wsHandshakeInterceptor: WsHandshakeInterceptor
) : WebSocketConfigurer {
    override fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) {
        registry
            .addHandler(handler, "workstream")
            .addInterceptors(wsHandshakeInterceptor)
            .setAllowedOrigins("*")
    }
    /**
     * webSocket定时器配置,解决同步使用定时器[ScheduleService]时的冲突问题
     */
    @Bean
    @Nullable
    fun taskScheduler():TaskScheduler {
        return ThreadPoolTaskScheduler().apply {
            setThreadNamePrefix("SockJS-")
            poolSize = Runtime.getRuntime().availableProcessors()
            isRemoveOnCancelPolicy = true
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/socket/config/WsHandshakeInterceptor.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package cn.flightfeather.supervision.socket.config
import org.apache.logging.log4j.util.Strings
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.stereotype.Component
import org.springframework.web.socket.WebSocketHandler
import org.springframework.web.socket.server.HandshakeInterceptor
/**
 *
 * @date 2024/7/19
 * @author feiyu02
 */
@Component
class WsHandshakeInterceptor : HandshakeInterceptor {
    /**
     * æ¡æ‰‹å‰
     */
    @Throws(Exception::class)
    override fun beforeHandshake(
        request: ServerHttpRequest,
        response: ServerHttpResponse,
        wsHandler: WebSocketHandler,
        attributes: MutableMap<String, Any>,
    ): Boolean {
        println("握手开始")
        val hostName = request.remoteAddress.hostName
        // TODO: 2024/7/19 åŽç»­å¯æ·»åŠ ç”¨æˆ·èº«ä»½éªŒè¯æœºåˆ¶,sessionId可替换为实际的用户id
        val sessionId = hostName + (Math.random() * 1000).toInt().toString()
        if (Strings.isNotBlank(sessionId)) {
            // æ”¾å…¥å±žæ€§åŸŸ
            attributes["session_id"] = sessionId
            println("用户 session_id $sessionId æ¡æ‰‹æˆåŠŸï¼")
            return true
        }
        println("用户登录已失效")
        return false
    }
    /**
     * æ¡æ‰‹åŽ
     */
    override fun afterHandshake(
        request: ServerHttpRequest,
        response: ServerHttpResponse,
        wsHandler: WebSocketHandler,
        exception: Exception?,
    ) {
        println("握手完成")
    }
}
src/main/kotlin/cn/flightfeather/supervision/socket/processor/WebSocketReceiver.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.flightfeather.supervision.socket.processor
import cn.flightfeather.supervision.common.log.BizLog
import cn.flightfeather.supervision.common.log.WorkStreamLogInfo
import org.springframework.stereotype.Component
/**
 * webSocket消息接收管理
 * @date 2024/7/19
 * @author feiyu02
 */
@Component
class WebSocketReceiver(private val bizLog: BizLog) {
    /**
     * æŽ¥æ”¶æ¶ˆæ¯å¤„理
     */
    fun onReceiveMsg(msg: String) {
        // debug
        if (msg == "test"){
            bizLog.info(WorkStreamLogInfo("8FAqSPnAA8ry4ExX", "朱正强", "在上海广发粉煤灰有限公司新增一个问题"))
        }
    }
}
src/main/kotlin/cn/flightfeather/supervision/socket/processor/WebSocketSender.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.flightfeather.supervision.socket.processor
import cn.flightfeather.supervision.socket.WsSessionManager
import org.springframework.stereotype.Component
import org.springframework.web.socket.TextMessage
/**
 * webSocket消息发送管理
 * @date 2024/7/19
 * @author feiyu02
 */
@Component
class WebSocketSender {
    /**
     * å•独发送消息
     */
    fun sendMsg(msg: String, sessionId: String) {
        WsSessionManager[sessionId]?.sendMessage(TextMessage(msg))
    }
    /**
     * å¹¿æ’­ç»™æ‰€æœ‰äºº
     */
    fun broadcast(msg: String) {
        WsSessionManager.eachSession {
            it.sendMessage(TextMessage(msg))
        }
    }
}
src/main/resources/application-dev.yml
@@ -19,3 +19,4 @@
imgPath: target
filePath: target
mode: dev
src/main/resources/application-pro.yml
@@ -12,4 +12,5 @@
      password: ledger_fxxchackxr
imgPath: D:/02product/04supervision/images/
filePath: D:/02product/04supervision/files/
filePath: D:/02product/04supervision/files/
mode: pro
src/main/resources/application-test.yml
@@ -28,4 +28,5 @@
        enabled: true
imgPath: C:\02product\supervision\images
filePath: C:\02product\supervision\files
filePath: C:\02product\supervision\files
mode: test
src/main/resources/application.yml
@@ -87,9 +87,9 @@
#logging路径设置
logging:
#  config: classpath:log4j2.xml
  level:
    cn.flightfeather.supervision.domain: debug
  config: classpath:log4j2.xml
#  level:
#    cn.flightfeather.supervision.domain: debug
springfox:
  documentation:
src/main/resources/log4j2.xml
@@ -78,7 +78,7 @@
        </logger>
        <root level="all">
            <appender-ref ref="Console"/>
<!--            <appender-ref ref="Console"/>-->
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
src/main/resources/mapper/ds1/ProblemlistMapper.xml
@@ -53,6 +53,7 @@
        <result property="endTime" column="endTime"/>
        <result property="count" column="count"/>
        <result property="changeCount" column="changeCount"/>
<!--        <collection property="problems" ofType="cn.flightfeather.supervision.domain.ds1.entity.Problemlist" resultMap="BaseResultMap"/>-->
    </resultMap>
    <resultMap id="SceneProblemSummary" type="cn.flightfeather.supervision.lightshare.vo.SceneProblemSummary">
        <id property="sceneId" column="sceneId"/>
@@ -75,23 +76,6 @@
        LEFT JOIN sm_t_scense AS f ON a.S_GUID = f.S_GUID
        LEFT JOIN tm_t_subtask AS c ON a.ST_GUID = c.ST_GUID
        <where>
<!--        a.ST_GUID IN (-->
<!--            SELECT-->
<!--            d.ST_GUID-->
<!--            FROM-->
<!--            tm_t_subtask AS d LEFT JOIN tm_t_task AS e ON d.T_GUID = e.T_GUID-->
<!--            WHERE-->
<!--            e.T_LevelNum = '2'-->
<!--            <if test="startTime != null">-->
<!--                AND e.T_StartTime &lt;= #{startTime}-->
<!--            </if>-->
<!--            <if test="endTime != null">-->
<!--                AND e.T_EndTime >= #{endTime}-->
<!--            </if>-->
<!--            <if test="districtCode != null">-->
<!--                AND e.T_DistrictCode = #{districtCode}-->
<!--            </if>-->
<!--        )-->
        <if test="startTime != null">
            AND c.ST_PlanStartTime >= #{startTime}
        </if>
src/test/kotlin/cn/flightfeather/supervision/business/bgtask/AopTaskCtrlTest.kt
@@ -12,6 +12,7 @@
import java.io.BufferedReader
import java.io.InputStreamReader
import java.time.LocalDate
import java.time.LocalDateTime
@RunWith(SpringRunner::class)
@@ -27,11 +28,18 @@
    @Test
    fun startEvaluation() {
        val time = LocalDate.of(2024, 6, 23).atStartOfDay()
        val time = LocalDate.of(2024, 7, 23).atStartOfDay()
        aopTaskCtrl.startEvaluation(aopTaskCtrl.getArea(time, "310106", "静安区", Constant.SceneType.TYPE1))
    }
    @Test
    fun startAllEvaluation() {
//        val time = LocalDate.of(2024, 6, 23).atStartOfDay()
        val time = LocalDate.now().atStartOfDay()
        aopTaskCtrl.startAllEvaluation(time)
    }
    @Test
    fun startNewTask(){
        val time = LocalDate.of(2024, 6, 23).atStartOfDay()
        aopTaskCtrl.startNewTask(aopTaskCtrl.getArea(time, "310106", "静安区", Constant.SceneType.TYPE1))
src/test/kotlin/cn/flightfeather/supervision/business/location/LocationRoadNearbyTest.kt
@@ -45,12 +45,42 @@
//            LocationRoadNearby.BasePlace("吕青路-朱吕公路-朱平公路", Pair(121.136318,30.833325), Pair(121.148624,30.836094)),
//            LocationRoadNearby.BasePlace("泖湾支路-吕青路-朱平公路", Pair(121.155048,30.835229), Pair(121.148659,30.829861)),
            LocationRoadNearby.BasePlace("徐汇上师大", Pair(121.419384,31.161433), Pair(121.419384,31.161433)),
            LocationRoadNearby.BasePlace("静安监测站国控点", Pair(121.429439, 31.223632), Pair(121.429439, 31.223632)),
            LocationRoadNearby.BasePlace("金山大道2000号", Pair(121.3404, 30.744262), Pair(121.3404, 30.744262)),
            LocationRoadNearby.BasePlace("仙霞站", Pair(121.394775, 31.203982), Pair(121.419384,31.161433)),
//            LocationRoadNearby.BasePlace("徐汇上师大", Pair(121.419384,31.161433), Pair(121.419384,31.161433)),
//            LocationRoadNearby.BasePlace("静安监测站国控点", Pair(121.429439, 31.223632), Pair(121.429439, 31.223632)),
//            LocationRoadNearby.BasePlace("金山大道2000号", Pair(121.3404, 30.744262), Pair(121.3404, 30.744262)),
            LocationRoadNearby.BasePlace("仙霞站", Pair(121.394775, 31.203982), Pair(121.394775, 31.203982)),
//            LocationRoadNearby.BasePlace("程桥站", Pair(121.362928, 31.192925), Pair(121.362928, 31.192925)),
//            LocationRoadNearby.BasePlace("长阳站", Pair(121.424603, 31.223644), Pair(121.424603, 31.223644)),
//            LocationRoadNearby.BasePlace("古岗路", Pair(121.411559, 30.791152), Pair(121.423728, 30.788246)),
//            LocationRoadNearby.BasePlace("东方红中心路", Pair(121.141623, 30.870369), Pair(121.160251, 30.872135)),
//            LocationRoadNearby.BasePlace("漾平路", Pair(121.222056, 30.852923), Pair(121.237356, 30.853606)),
//            LocationRoadNearby.BasePlace("兴寒路", Pair(121.045916, 30.831142), Pair(121.052351, 30.836051)),
//            LocationRoadNearby.BasePlace("亭朱公路", Pair(121.328213, 30.874901), Pair(121.335347, 30.860441)),
//            LocationRoadNearby.BasePlace("卫八路", Pair(121.286369, 30.704997), Pair(121.279021, 30.722918)),
//            LocationRoadNearby.BasePlace("秋实路", Pair(121.273549, 30.720349), Pair(121.277212, 30.726353)),
//            LocationRoadNearby.BasePlace("恒康路", Pair(121.335671, 30.85661), Pair(121.342813, 30.857601)),
//            LocationRoadNearby.BasePlace("松溪路", Pair(121.236017, 30.894031), Pair(121.24154, 30.892297)),
//            LocationRoadNearby.BasePlace("兴桂路", Pair(121.105814, 30.876818), Pair(121.106139, 30.868925)),
//            LocationRoadNearby.BasePlace("荣丰路", Pair(121.236894, 30.83599), Pair(121.253117, 30.835875)),
//            LocationRoadNearby.BasePlace("叶新公路", Pair(121.164193, 30.936096), Pair(121.023036, 30.933981)),
//            LocationRoadNearby.BasePlace("阳康路", Pair(121.355063, 30.780308), Pair(121.35446, 30.774935)),
//            LocationRoadNearby.BasePlace("张旧支路", Pair(121.321495, 30.783227), Pair(121.325356, 30.785677)),
//            LocationRoadNearby.BasePlace("农建路", Pair(121.134021, 30.855966), Pair(121.141504, 30.856559)),
//            LocationRoadNearby.BasePlace("合展路", Pair(121.407831, 30.801774), Pair(121.426746, 30.797785)),
//            LocationRoadNearby.BasePlace("天华路", Pair(121.451848, 30.776257), Pair(121.4337, 30.80644)),
//            LocationRoadNearby.BasePlace("珠港街", Pair(121.337652, 30.847802), Pair(121.345829, 30.846835)),
//            LocationRoadNearby.BasePlace("荣东路", Pair(121.249271, 30.832921), Pair(121.249176, 30.835928)),
//            LocationRoadNearby.BasePlace("九丰路", Pair(121.254114, 30.903438), Pair(121.254715, 30.893363)),
        )
        listOf(500.0, 1000.0, 2000.0, 3000.0).forEach {
        listOf(
            500.0,
//            1000.0,
            2000.0,
            3000.0
        ).forEach {
            locationRoadNearby.searchList(bList, it)
        }
    }