xuhy
12 小时以前 26f6f43785afd992496d7fc79775124e557ff16d
Merge remote-tracking branch 'origin/master'

# Conflicts:
# ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TAppUserController.java
9个文件已修改
4个文件已添加
309 ■■■■■ 已修改文件
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TDataStatisticsController.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TAppUserController.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TMissionController.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/application-test.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/FileController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TAppUserLocation.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TMission.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TMissionUser.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TMissionServiceImpl.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/IndexDataDetailVo.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/IndexDataRankVo.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/IndexDataVo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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;
}