ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TDataStatisticsController.java
New file @@ -0,0 +1,112 @@ package com.ruoyi.web.controller.api; import com.ruoyi.common.core.domain.R; import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.model.TMission; import com.ruoyi.system.service.TMissionService; import com.ruoyi.system.vo.IndexDataDetailVo; import com.ruoyi.system.vo.IndexDataVo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * <p> * 专业管理 前端控制器 * </p> * * @author xiaochen * @since 2025-09-28 */ @Api(tags = "数据统计") @RestController @RequestMapping("") public class TDataStatisticsController { private final TMissionService missionService; private final TokenService tokenService; @Autowired public TDataStatisticsController( TMissionService missionService, TokenService tokenService) { this.missionService = missionService; this.tokenService = tokenService; } @ApiOperation(value = "获取数据") @GetMapping(value = "/api/t-data-statistics/getData") public R<IndexDataVo> getData() { IndexDataVo indexDataVo = new IndexDataVo(); List<TMission> list = missionService.list(); if(list.isEmpty()){ return R.ok(indexDataVo); } indexDataVo.setAllMissionNum(list.size()); indexDataVo.setUnAssignMissionNum((int) list.stream().filter(item -> item.getStatus() == 1).count()); indexDataVo.setFinishMissionNum((int) list.stream().filter(item -> item.getStatus() == 4 || item.getStatus() == 6).count()); indexDataVo.setUnFinishMissionNum(indexDataVo.getAllMissionNum()-indexDataVo.getFinishMissionNum()); indexDataVo.setSpecialMissionNum((int) list.stream().filter(item -> item.getUrgencyLevel() == 3).count()); indexDataVo.setEmergencyMissionNum((int) list.stream().filter(item -> item.getUrgencyLevel() == 2).count()); indexDataVo.setRegularMissionNum((int) list.stream().filter(item -> item.getUrgencyLevel() == 1).count()); ArrayList<IndexDataDetailVo> indexDataDetailVos = new ArrayList<>(); // 近7天 for (int i = 6; i >=0 ; i--) { IndexDataDetailVo indexDataDetailVo = new IndexDataDetailVo(); // list根据createTime 获取对应时间的数据 String time = LocalDate.now().minusDays(i).toString(); List<TMission> timeList = list.stream().filter(item -> item.getCreateTime().toLocalDate().toString().equals(time)).collect(Collectors.toList()); indexDataDetailVo.setTime(time); indexDataDetailVo.setSpecialMissionNum((int) timeList.stream().filter(item -> item.getUrgencyLevel() == 3).count()); indexDataDetailVo.setEmergencyMissionNum((int) timeList.stream().filter(item -> item.getUrgencyLevel() == 2).count()); indexDataDetailVo.setRegularMissionNum((int) timeList.stream().filter(item -> item.getUrgencyLevel() == 1).count()); indexDataDetailVos.add(indexDataDetailVo); } indexDataVo.setMissionDetail(indexDataDetailVos); List<TMission> tMissions = list.stream().filter(e -> e.getReceiveTime() != null).collect(Collectors.toList()); double v = tMissions.stream().mapToDouble(TMission::getResTime).average().orElse(0); indexDataVo.setAvgResponseTime(String.valueOf(v)); List<TMission> tMissions1 = list.stream().filter(e -> e.getFinishTime() != null).collect(Collectors.toList()); long count = tMissions1.stream().filter(e -> e.getStatus() == 4).count(); String finishRate = new BigDecimal(count).divide(new BigDecimal(tMissions1.size()), 4, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toString(); indexDataVo.setFinishRate(finishRate); double avgFinishTime = tMissions1.stream().mapToDouble(TMission::getSuccessTime).average().orElse(0); indexDataVo.setAvgFinishTime(String.valueOf(avgFinishTime)); // 平了分的 List<TMission> tMissions2 = list.stream().filter(e -> e.getAddressScore() != null ).collect(Collectors.toList()); long count1 = tMissions2.stream().filter(e -> e.getTimeScore() + e.getAddressScore() + e.getPersonCountScore() + e.getEquipmentScore() + e.getAttemptScore() > 30).count(); return R.ok(indexDataVo); } public static void main(String[] args) { String format = String.format("%05d", 2); System.out.println(format); } } ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TAppUserController.java
@@ -2,12 +2,14 @@ import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.dto.UpdateDetailTeamDto; import com.ruoyi.system.dto.UserIdDto; import com.ruoyi.system.model.TAppUser; import com.ruoyi.system.model.TAppUserEquipment; import com.ruoyi.system.model.TAppUserLocation; @@ -22,6 +24,8 @@ import org.springframework.web.bind.annotation.*; import java.time.LocalDate; import java.util.List; /** * <p> @@ -143,5 +147,28 @@ return R.ok(); } /** * 用户打点 */ @ApiOperation(value = "获取用户打点数据",response = TAppUserLocation.class) @GetMapping(value = "/open/t-app-user/getAppUserLocation") public R<List<TAppUserLocation>> getAppUserLocation() { String userId = tokenService.getLoginUserApplet().getUserId(); List<TAppUserLocation> list = appUserLocationService.list(new LambdaQueryWrapper<TAppUserLocation>().eq(TAppUserLocation::getAppUserId, userId).orderByDesc(TAppUserLocation::getTime)); return R.ok(list); } /** * 用户打点 */ @ApiOperation(value = "获取用户打点数据--详情",response = TAppUserLocation.class) @PostMapping(value = "/open/t-app-user/getAppUserLocationFromId") public R<TAppUserLocation> getAppUserLocationFromId(@RequestBody String param) { UserIdDto dto = JSON.parseObject(param, UserIdDto.class); TAppUserLocation location = appUserLocationService.getById(dto.getId()); return R.ok(location); } } ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TMissionController.java
@@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; @@ -161,10 +162,28 @@ public static void main(String[] args) { LocalDate now = LocalDate.now(); System.out.println(LocalDate.now().minusMonths(5) + " 00:00:00"); LocalDateTime now = LocalDateTime.now(); LocalDateTime localDateTime = LocalDateTime.now().minusDays(5); long hours = Duration.between(localDateTime, now).toHours(); System.out.println(hours); } /** * 接受任务 */ @ApiOperation(value = "获取任务列表--接受任务", response = MissionIdDto.class) @PostMapping(value = "/api/t-mission/acceptMission") public R<?> acceptMission(@RequestBody String param) { MissionIdDto dto = JSON.parseObject(param, MissionIdDto.class); TMission mission = missionService.getById(dto.getMissionId()); mission.setReceiveTime(LocalDateTime.now()); // 计算 mission中的assign_time 和现在时间 时间差 小时 mission.setAssignTime(mission.getAssignTime().plusHours(mission.getAssignTime().getHour())); long hoursDiff = Duration.between(mission.getAssignTime(), mission.getReceiveTime()).toHours(); mission.setResTime((double) hoursDiff); return R.ok(); } /** * 完成任务 ruoyi-applet/src/main/resources/application-test.yml
@@ -190,7 +190,7 @@ location: /file/ qrLocation: /file/qrCode/ accessPath: /file/ allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF|.xls|.xlsx|.rar|.zip|.rar allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF|.xls|.xlsx|.rar|.zip|.rar|.m4a wx: conf: appId: ruoyi-common/src/main/java/com/ruoyi/common/core/controller/FileController.java
@@ -2,6 +2,7 @@ import com.ruoyi.common.config.FileUploadConfig; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.R; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; @@ -32,9 +33,9 @@ @ApiOperation(value = "单文件上传", notes = "单文件上传,rename 默认不重命名") @PostMapping(value = "upload", headers = "content-type=multipart/form-data") public AjaxResult uploadImageMany(@RequestParam(value = "file") MultipartFile mf) throws IOException { public R<?> uploadImageMany(@RequestParam(value = "file") MultipartFile mf) throws IOException { if (mf.isEmpty()) { return AjaxResult.error("请传入文件!"); return R.fail("请传入文件!"); } String TimeDir =new SimpleDateFormat("yyyy-MM-dd").format(new Date()); // String realPath = fileUploadConfig.getLocation() + TimeDir; @@ -50,7 +51,7 @@ String ext = filename.substring(filename.lastIndexOf("."), filename.length()); // 检查文件类型 if (!fileUploadConfig.getAllowExt().contains(ext)) { return AjaxResult.error("上传文件格式不正确,仅支持" + fileUploadConfig.getAllowExt()); return R.fail("上传文件格式不正确,仅支持" + fileUploadConfig.getAllowExt()); } File targetFile = new File(realPath, filename);//目标文件 //开始从源文件拷贝到目标文件 @@ -59,7 +60,7 @@ //拼接数据 // String imgstr = fileUploadConfig.getAccessPath() + TimeDir +"\\"+ filename; String imgstr = TimeDir +"/"+ filename; return AjaxResult.success(imgstr); return R.ok(imgstr); } ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -118,7 +118,7 @@ "/applet/changepwd", "/captchaImage","/getCode","/loginCode", "/operations/getBySingleNum/**", "/user/getUserInfoByNumber/**", "/wxLogin/**","/cos/get/**","/t-mission/**","/api/t-app-user/**" "/wxLogin/**","/cos/get/**","/t-mission/**","/api/t-app-user/**","/open/file/**" ).permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() ruoyi-system/src/main/java/com/ruoyi/system/model/TAppUserLocation.java
@@ -4,11 +4,14 @@ import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.common.core.domain.BaseModel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import java.time.LocalDateTime; /** * <p> @@ -53,4 +56,10 @@ @TableField("file_url") private String fileUrl; @ApiModelProperty(value = "时间") @TableField("time") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime time; } ruoyi-system/src/main/java/com/ruoyi/system/model/TMission.java
@@ -64,7 +64,7 @@ @TableField("mission_electronic") private String missionElectronic; @ApiModelProperty(value = "状态 1=待指派 2=待完成 3=待评分 4=已完成 5=待改派") @ApiModelProperty(value = "状态 1=待指派 2=待完成 3=待评分 4=已完成 5=待改派 6=未完成") @TableField("status") private Integer status; @@ -127,6 +127,14 @@ @TableField("is_mission_pool") private Integer isMissionPool; @ApiModelProperty(value = "响应时长") @TableField("res_time") private Double resTime; @ApiModelProperty(value = "完成时长") @TableField("success_time") private Double successTime; @ApiModelProperty(value = "用户任务id") @TableField(exist = false) ruoyi-system/src/main/java/com/ruoyi/system/model/TMissionUser.java
@@ -83,4 +83,9 @@ @ApiModelProperty(value = "任务来源") private Double score; @ApiModelProperty(value = "接受时间") @TableField("accept_time") private LocalDateTime acceptTime; } ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TMissionServiceImpl.java
@@ -167,8 +167,19 @@ // 判断这个任务还有正在执行的不 Long l = missionUserMapper.selectCount(new LambdaQueryWrapper<TMissionUser>().eq(TMissionUser::getMissionId, user.getMissionId()).eq(TMissionUser::getStatus, 0)); if (l == 0){ // 全部执行完 任务完成 this.update(new LambdaUpdateWrapper<TMission>().eq(TMission::getId, user.getMissionId()).set(TMission::getStatus, 4)); TMission tMission = this.getById(user.getMissionId()); tMission.setFinishTime(LocalDateTime.now()); // 判断是否全部完成 Long unSuccessCount = missionUserMapper.selectCount(new LambdaQueryWrapper<TMissionUser>().eq(TMissionUser::getMissionId, user.getMissionId()).eq(TMissionUser::getStatus, 2)); if(unSuccessCount>0){ tMission.setStatus(6); }else { tMission.setStatus(4); } LocalDateTime assignTime = tMission.getAssignTime(); tMission.setSuccessTime((double) Duration.between(assignTime, tMission.getFinishTime()).toHours()); this.updateById(tMission); } // 更新装备 TAppUserEquipment tAppUserEquipment = appUserEquipmentMapper.selectOne(new LambdaQueryWrapper<TAppUserEquipment>().eq(TAppUserEquipment::getAppUserId, userId).eq(TAppUserEquipment::getAppUserId, userId)); ruoyi-system/src/main/java/com/ruoyi/system/vo/IndexDataDetailVo.java
New file @@ -0,0 +1,21 @@ package com.ruoyi.system.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data @ApiModel("时间-数量Model") public class IndexDataDetailVo { private String time; @ApiModelProperty("特急任务数") private Integer specialMissionNum=0; @ApiModelProperty("紧急任务数") private Integer emergencyMissionNum=0; @ApiModelProperty("一般任务数") private Integer regularMissionNum=0; } ruoyi-system/src/main/java/com/ruoyi/system/vo/IndexDataRankVo.java
New file @@ -0,0 +1,12 @@ package com.ruoyi.system.vo; import io.swagger.annotations.ApiModel; import lombok.Data; @Data @ApiModel("名称-数量Model") public class IndexDataRankVo { private String name; private Integer count; } ruoyi-system/src/main/java/com/ruoyi/system/vo/IndexDataVo.java
New file @@ -0,0 +1,62 @@ package com.ruoyi.system.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.List; @Data @ApiModel("首页数据") public class IndexDataVo { @ApiModelProperty("任务总数") private Integer allMissionNum=0; @ApiModelProperty("待分配任务数") private Integer unAssignMissionNum=0; @ApiModelProperty("待完成任务数") private Integer unFinishMissionNum=0; @ApiModelProperty("已完成任务数") private Integer finishMissionNum=0; @ApiModelProperty("特急任务数") private Integer specialMissionNum=0; @ApiModelProperty("紧急任务数") private Integer emergencyMissionNum=0; @ApiModelProperty("一般任务数") private Integer regularMissionNum=0; @ApiModelProperty("任务详情近7天") private List<IndexDataDetailVo> missionDetail; @ApiModelProperty("平均响应时长") private String avgResponseTime="0"; @ApiModelProperty("完成平均时长") private String avgFinishTime="0"; @ApiModelProperty("任务完成率") private String finishRate="0"; @ApiModelProperty("战损消耗率") private String damageRate="0"; @ApiModelProperty("敌情准确率") private String enemyAccuracy="0"; @ApiModelProperty("本月分队排名") private List<IndexDataRankVo> rank; @ApiModelProperty("任务类型占比") private List<IndexDataRankVo> missionType; @ApiModelProperty("装备库统计") private List<IndexDataRankVo> equip; }