Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
177e0e4d97 | ||
|
|
f74a2322e4 | ||
|
|
822cdbf539 | ||
|
|
d75f21c17e | ||
|
|
ffee8611fe | ||
|
|
0e8f5e7b14 | ||
|
|
115407026f | ||
|
|
9f545952e4 | ||
|
|
1988e031e5 | ||
|
|
44b0f6ca2b | ||
|
|
ae866f0791 | ||
|
|
4610ac4737 | ||
|
|
6d6920a2b3 | ||
|
|
c7041fa69e | ||
|
|
370f5e0089 | ||
|
|
a47f27967e | ||
|
|
6c08f5f141 | ||
|
|
690f688c6a |
@@ -0,0 +1,10 @@
|
||||
-- QH_WAT_MONITOR_DATA 新增 EVENT_ID(事件ID)和 LOCATE_TIME(定位时间)字段
|
||||
-- 数据库:118-DM 达梦数据库,SQL 全大写规范
|
||||
ALTER TABLE "QH_WAT_MONITOR_DATA" ADD "EVENT_ID" VARCHAR(64) DEFAULT NULL;
|
||||
ALTER TABLE "QH_WAT_MONITOR_DATA" ADD "LOCATE_TIME" DATETIME DEFAULT NULL;
|
||||
|
||||
COMMENT ON COLUMN "QH_WAT_MONITOR_DATA"."EVENT_ID" IS '事件ID';
|
||||
COMMENT ON COLUMN "QH_WAT_MONITOR_DATA"."LOCATE_TIME" IS '定位时间';
|
||||
|
||||
CREATE INDEX "IDX_EVENT_ID" ON "QH_WAT_MONITOR_DATA"("EVENT_ID");
|
||||
CREATE INDEX "IDX_WATCH_NO_EVENT_ID" ON "QH_WAT_MONITOR_DATA"("WATCH_NO", "EVENT_ID");
|
||||
+1
-1
@@ -99,7 +99,7 @@ public enum AnYanApiEnum {
|
||||
/**
|
||||
* 18.查询关爱联络员信息及急救人员查询接口
|
||||
*/
|
||||
QUERY_EMPLOYEE_CARE_PERSON("/aygc-znhpt-sys/0/api/empCareEmp/findPageByIdNo", "", HttpMethod.POST),
|
||||
QUERY_EMPLOYEE_CARE_PERSON("/aygc-znhpt-sys/0/api/empCareEmp/findPage", "", HttpMethod.POST),
|
||||
|
||||
/**
|
||||
* 19.批量更新员工关键信息接口
|
||||
|
||||
+3
@@ -1,5 +1,6 @@
|
||||
package com.renkang.anyan.model.dto;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -34,4 +35,6 @@ public class CareUserDetailsParam {
|
||||
|
||||
@Schema(title = "pageType")
|
||||
private String pageType = "plus";
|
||||
|
||||
private int year = DateUtil.thisYear();
|
||||
}
|
||||
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package com.renkang.consultation.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.renkang.consultation.entity.ConPage;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -11,6 +12,10 @@ public class AccountDTO extends ConPage {
|
||||
@Schema(title = "专家姓名")
|
||||
private String doctorName;
|
||||
|
||||
public void setDoctorName(String doctorName) {
|
||||
this.doctorName = doctorName != null ? HtmlUtil.unescape(doctorName) : null;
|
||||
}
|
||||
|
||||
@Schema(title = "医院选择")
|
||||
private String hospitalName;
|
||||
|
||||
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package com.renkang.consultation.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.renkang.consultation.entity.ConPage;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -10,6 +11,10 @@ public class ConDoctorDTO extends ConPage {
|
||||
|
||||
@Schema(title = "专家姓名")
|
||||
private String doctorName;
|
||||
|
||||
public void setDoctorName(String doctorName) {
|
||||
this.doctorName = doctorName != null ? HtmlUtil.unescape(doctorName) : null;
|
||||
}
|
||||
@Schema(title = "专家编号")
|
||||
private String doctorNo;
|
||||
|
||||
|
||||
+8
@@ -1,5 +1,6 @@
|
||||
package com.renkang.consultation.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.renkang.consultation.entity.ConPage;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@@ -17,6 +18,9 @@ public class ConEvaluateDTO extends ConPage {
|
||||
|
||||
@Schema(title = "咨询人")
|
||||
private String userName;
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName != null ? HtmlUtil.unescape(userName) : null;
|
||||
}
|
||||
|
||||
@Schema(title = "二级单位id")
|
||||
private String sysOrgSecondId;
|
||||
@@ -27,6 +31,10 @@ public class ConEvaluateDTO extends ConPage {
|
||||
@Schema(title = "咨询专家")
|
||||
private String expertName;
|
||||
|
||||
public void setExpertName(String expertName) {
|
||||
this.expertName = expertName != null ? HtmlUtil.unescape(expertName) : null;
|
||||
}
|
||||
|
||||
@Schema(title = "专家医院")
|
||||
private String expertHospital;
|
||||
|
||||
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package com.renkang.consultation.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.renkang.consultation.entity.ConPage;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -10,6 +11,10 @@ public class ConHealthInfoAnswerDTO extends ConPage {
|
||||
@Schema(title = "员工姓名")
|
||||
private String fromName;
|
||||
|
||||
public void setFromName(String fromName) {
|
||||
this.fromName = fromName != null ? HtmlUtil.unescape(fromName) : null;
|
||||
}
|
||||
|
||||
@Schema(title = "手机号")
|
||||
private String phone;
|
||||
|
||||
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package com.renkang.consultation.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.renkang.consultation.entity.ConPage;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -11,6 +12,10 @@ public class ConHelperDTO extends ConPage {
|
||||
@Schema(title = "全科医生姓名")
|
||||
private String userName;
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName != null ? HtmlUtil.unescape(userName) : null;
|
||||
}
|
||||
|
||||
@Schema(title = "状态")
|
||||
private Integer status;
|
||||
|
||||
|
||||
+9
@@ -1,5 +1,6 @@
|
||||
package com.renkang.consultation.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.renkang.consultation.entity.ConPage;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@@ -17,6 +18,10 @@ public class ConSessionDTO extends ConPage {
|
||||
@Schema(title = "员工姓名")
|
||||
private String fromName;
|
||||
|
||||
public void setFromName(String fromName) {
|
||||
this.fromName = fromName != null ? HtmlUtil.unescape(fromName) : null;
|
||||
}
|
||||
|
||||
@Schema(title = "员工id")
|
||||
private String fromAccount;
|
||||
|
||||
@@ -26,6 +31,10 @@ public class ConSessionDTO extends ConPage {
|
||||
@Schema(title = "专家姓名")
|
||||
private String doctorName;
|
||||
|
||||
public void setDoctorName(String doctorName) {
|
||||
this.doctorName = doctorName != null ? HtmlUtil.unescape(doctorName) : null;
|
||||
}
|
||||
|
||||
@Schema(title = "咨询状态(0待确认 1接收预约 2拒绝预约 3已过期 4已取消 5咨询中 6专家咨询结束 7员工咨询结束 8超时自动结束)")
|
||||
private String contentStatus;
|
||||
|
||||
|
||||
-4
@@ -3,7 +3,6 @@ package com.renkang.consultation.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
@@ -249,9 +248,6 @@ public class ConDoctorServiceImpl extends ServiceImpl<ConDoctorMapper, ConDoctor
|
||||
if (StringUtils.hasLength(dto.getHospitalName())) {
|
||||
conDoctor.setResourceId(dto.getHospitalName());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(dto.getDoctorName())){
|
||||
conDoctor.setDoctorName(HtmlUtil.unescape(dto.getDoctorName()));
|
||||
}
|
||||
IPage<ConDoctor> conDoctorIPage = conDoctorMapper.queryPageList(page, conDoctor);
|
||||
List<ConDoctor> conDoctorList = conDoctorIPage.getRecords();
|
||||
if (CollectionUtils.isEmpty(conDoctorList)) {
|
||||
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package com.renkang.emergency.entity.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -7,6 +8,10 @@ import lombok.Data;
|
||||
public class CallLogDTO {
|
||||
@Schema(description = "姓名")
|
||||
private String realName;
|
||||
|
||||
public void setRealName(String realName) {
|
||||
this.realName = realName != null ? HtmlUtil.unescape(realName) : null;
|
||||
}
|
||||
@Schema(description = "呼叫中心")
|
||||
private String centerId;
|
||||
@Schema(description = "坐席工号")
|
||||
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package com.renkang.emergency.entity.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -21,6 +22,10 @@ public class PhoneListDTO {
|
||||
private String phoneType;
|
||||
@Schema(description = "姓名")
|
||||
private String realName;
|
||||
|
||||
public void setRealName(String realName) {
|
||||
this.realName = realName != null ? HtmlUtil.unescape(realName) : null;
|
||||
}
|
||||
/**
|
||||
* 0:全部 1:女 2:男
|
||||
*/
|
||||
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package com.renkang.emergency.entity.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -9,6 +10,10 @@ public class TblQimoNoticeDTO {
|
||||
private String seatId;
|
||||
@Schema(title = "姓名")
|
||||
private String realName;
|
||||
|
||||
public void setRealName(String realName) {
|
||||
this.realName = realName != null ? HtmlUtil.unescape(realName) : null;
|
||||
}
|
||||
@Schema(title = "所属应急中心")
|
||||
private String centerId;
|
||||
@Schema(title = "orgCode")
|
||||
|
||||
+2
@@ -1,6 +1,7 @@
|
||||
package com.renkang.emergency.controller;
|
||||
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.renkang.emergency.entity.EmergencyProfessionUser;
|
||||
import com.renkang.emergency.service.EmergencyProfessionUserService;
|
||||
@@ -40,6 +41,7 @@ public class EmergencyProfessionUserController {
|
||||
@RequestParam(name = "type", defaultValue = "5") Integer type,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||
name = HtmlUtil.unescape(name);
|
||||
IPage<EmergencyProfessionUser> pageList = emergencyProfessionUserService.customPage(name, centerId, type, pageNo, pageSize);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
+4
@@ -6,6 +6,7 @@ import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
@@ -58,6 +59,9 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
|
||||
@Override
|
||||
public IPage<Order> pageCustom(Page<Order> page, Order order, HttpServletRequest req) {
|
||||
if(StrUtil.isNotBlank(order.getSalvageUserName())){
|
||||
order.setSalvageUserName(HtmlUtil.unescape(order.getSalvageUserName()));
|
||||
}
|
||||
String salvageUserName = order.getSalvageUserName();
|
||||
String initiatorUserName = order.getInitiatorUserName();
|
||||
Order orderCopy = new Order();
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
package com.renkang.watch.bean.request;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
@@ -19,6 +20,9 @@ public class AbnormalEventFilter {
|
||||
private String departOrgCode;
|
||||
// 用户名称
|
||||
private String userName;
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName != null ? HtmlUtil.unescape(userName) : null;
|
||||
}
|
||||
// 工具编码
|
||||
private String watchNo;
|
||||
//员工编号
|
||||
|
||||
+14
@@ -52,4 +52,18 @@ public class AbnormalEvent extends BaseUser {
|
||||
private Date bindDate;
|
||||
@Schema(title = "重点指标用户(0:非重点指标用户 1:大病异常 2:慢病异常 3:指标异常)")
|
||||
private List<WatchKeyUser> keyUserList;
|
||||
/**
|
||||
* 事件ID(用于告警事件与GPS位置更新关联的唯一标识)
|
||||
*/
|
||||
@Excel(name = "事件ID", width = 32, orderNum = "12")
|
||||
@Schema(title = "事件ID")
|
||||
private String eventId;
|
||||
/**
|
||||
* 定位时间(手表定时上报的GPS定位时间)
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "定位时间", format = "yyyy-MM-dd HH:mm:ss", width = 20, orderNum = "13")
|
||||
@Schema(title = "定位时间")
|
||||
private Date locateTime;
|
||||
}
|
||||
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package com.renkang.watch.dto;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -18,6 +19,10 @@ public class CalorieDataByUserDTO {
|
||||
private Integer pageSize = 10;
|
||||
@Schema(title = "用户姓名")
|
||||
private String realName;
|
||||
|
||||
public void setRealName(String realName) {
|
||||
this.realName = realName != null ? HtmlUtil.unescape(realName) : null;
|
||||
}
|
||||
@Schema(title = "工号")
|
||||
private String workNo;
|
||||
@Schema(title = "用户id组")
|
||||
|
||||
+14
@@ -151,6 +151,20 @@ public class WatchMonitorData implements Serializable {
|
||||
@Excel(name = "部门编码", width = 15)
|
||||
@Schema(title = "部门编码")
|
||||
private java.lang.String orgCode;
|
||||
/**
|
||||
* 事件ID(用于告警事件与GPS位置更新关联的唯一标识)
|
||||
*/
|
||||
@Excel(name = "事件ID", width = 32)
|
||||
@Schema(title = "事件ID")
|
||||
private java.lang.String eventId;
|
||||
/**
|
||||
* 定位时间(手表上报的GPS定位时间,定时上报场景使用)
|
||||
*/
|
||||
@Excel(name = "定位时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(title = "定位时间")
|
||||
private java.util.Date locateTime;
|
||||
|
||||
@Schema(title = "员工编号")
|
||||
@TableField(exist = false)
|
||||
|
||||
@@ -45,4 +45,15 @@ public class WatchNotifyVo implements Serializable {
|
||||
private Date warnTime;
|
||||
|
||||
private String gpsErrorMsg;
|
||||
|
||||
/**
|
||||
* 事件ID(用于告警事件与GPS位置更新关联的唯一标识)
|
||||
*/
|
||||
private String eventId;
|
||||
|
||||
/**
|
||||
* 定位时间(手表上报的GPS定位时间,定时上报场景使用)
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date locateTime;
|
||||
}
|
||||
|
||||
+12
-10
@@ -18,9 +18,14 @@ public class DeptStatData {
|
||||
/**
|
||||
* 已分配设备数
|
||||
*/
|
||||
@Excel(name = "分配设备数", width = 15,orderNum = "4")
|
||||
@Excel(name = "已分配设备数", width = 15,orderNum = "4")
|
||||
private Integer assignedDeviceNums;
|
||||
|
||||
/**
|
||||
* 未分配设备数(入库设备数 - 已分配设备数,仅一、二级部门有值)
|
||||
*/
|
||||
@Excel(name = "未分配设备数", width = 15,orderNum = "5")
|
||||
private Integer undistributedDeviceNums;
|
||||
|
||||
@Excel(name = "入库设备数", width = 15,orderNum = "3")
|
||||
private Integer totalAssignedDeviceNums;
|
||||
@@ -28,25 +33,22 @@ public class DeptStatData {
|
||||
/**
|
||||
* 佩戴人员数
|
||||
*/
|
||||
@Excel(name = "佩戴人员数", width = 15,orderNum = "5")
|
||||
@Excel(name = "佩戴人员数", width = 15,orderNum = "6")
|
||||
private Integer assignedUserNums;
|
||||
/**
|
||||
* 未佩戴人员数
|
||||
*/
|
||||
@Excel(name = "未佩戴人员数", width = 15,orderNum = "6")
|
||||
@Excel(name = "未佩戴人员数", width = 15,orderNum = "7")
|
||||
private Integer undistributedUserNums;
|
||||
|
||||
@Excel(name = "心率异常人数", width = 15,orderNum = "7")
|
||||
@Excel(name = "心率异常人数", width = 15,orderNum = "8")
|
||||
private Integer heartRateNums;
|
||||
@Excel(name = "血氧异常人数", width = 15,orderNum = "8")
|
||||
@Excel(name = "血氧异常人数", width = 15,orderNum = "9")
|
||||
private Integer spo2Nums;
|
||||
@Excel(name = "压力异常人数", width = 15,orderNum = "9")
|
||||
@Excel(name = "压力异常人数", width = 15,orderNum = "10")
|
||||
private Integer stressNums;
|
||||
@Excel(name = "体温异常人数", width = 15,orderNum = "10")
|
||||
@Excel(name = "体温异常人数", width = 15,orderNum = "11")
|
||||
private Integer tempNums;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -336,8 +336,9 @@ public class AsyncImportService {
|
||||
updatedWatchDevice.setBindDate(watchDevice.getBindDate()==null?new Date():watchDevice.getBindDate());
|
||||
updatedWatchDevice.setUpdateDate(new Date());
|
||||
updatedWatchDevice.setUpdateBy(sysUser.getId());
|
||||
updatedWatchDevice.setDeptId(device.getDeptId());
|
||||
updatedWatchDevice.setOrgCode(device.getOrgCode());
|
||||
// 这里修改成以用户的部门为准,更新手表的所属部门
|
||||
updatedWatchDevice.setDeptId(sysCache.getDepartIdByOrgCode(user.getOrgCode()));
|
||||
updatedWatchDevice.setOrgCode(user.getOrgCode());
|
||||
updateList.add(updatedWatchDevice);
|
||||
}
|
||||
info1.setHandleMsg(index + "/" + count);
|
||||
|
||||
+15
@@ -38,6 +38,21 @@ public class WatchApiController {
|
||||
return watchMonitorDataService.notice(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新告警事件GPS位置信息接口
|
||||
* 根据 watchNo + eventId 联合定位已有告警数据;
|
||||
* 仅更新GPS相关字段,不推送大屏与短信;
|
||||
* 内部会调用高德接口进行GPS84→GCJ02坐标转换与地址反查。
|
||||
*
|
||||
* @param json 入参(watchNo、eventId、lon、lat、address、locateTime、gpsErrorMsg)
|
||||
* @return 处理结果
|
||||
*/
|
||||
@PostMapping("/updateMonitorGps")
|
||||
@Operation(summary = "更新告警事件GPS位置信息")
|
||||
public AjaxJson updateMonitorGps(@RequestBody JSONObject json) {
|
||||
return watchMonitorDataService.updateMonitorGps(json);
|
||||
}
|
||||
|
||||
@GetMapping("/setData")
|
||||
public AjaxJson setData() {
|
||||
AjaxJson ajaxJson = new AjaxJson();
|
||||
|
||||
+15
-7
@@ -207,7 +207,9 @@ public class WatchDeviceController extends JeecgController<WatchDevice, IWatchDe
|
||||
@Operation(summary = "移动端解绑设备(手表)", description = "移动端解绑设备(手表)")
|
||||
@GetMapping("/app/unbundleDevice")
|
||||
public Result unbundleDevice(@RequestParam(name = "watchNo") String watchNo) {
|
||||
return watchDeviceService.unbundleDevice(watchNo);
|
||||
//移动端暂不允许解绑手表
|
||||
return Result.error("请联系健康管理员解绑");
|
||||
// return watchDeviceService.unbundleDevice(watchNo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,6 +357,10 @@ public class WatchDeviceController extends JeecgController<WatchDevice, IWatchDe
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
|
||||
WatchDevice watchDevice = watchDeviceService.getById(id);
|
||||
// 已绑定用户的手表不允许删除
|
||||
if (StringUtils.hasLength(watchDevice.getBindUserId())) {
|
||||
return Result.error("该手表已绑定用户,请先解绑后再删除!");
|
||||
}
|
||||
watchDeviceService.removeById(id);
|
||||
// 删除开关
|
||||
LambdaQueryWrapper<WatchDeviceSwitch> queryWrapper = new LambdaQueryWrapper<>();
|
||||
@@ -434,6 +440,8 @@ public class WatchDeviceController extends JeecgController<WatchDevice, IWatchDe
|
||||
}
|
||||
if (!StringUtils.hasLength(deptId) || "undefined".equals(deptId)) {
|
||||
throw new JeecgBootException("部门不能为空");
|
||||
//可为空,空则算入新疆油田公司
|
||||
// deptId = sysCache.getDepartIdByOrgCode("A01");
|
||||
}
|
||||
String sheetName = "设备导入";
|
||||
String fileName = sheetName + "_" + DateUtils.getDate("yyyyMMddHHmmss");
|
||||
@@ -699,12 +707,12 @@ public class WatchDeviceController extends JeecgController<WatchDevice, IWatchDe
|
||||
@Operation(summary = "获取时间范围的走跑数据", description = "获取时间范围的走跑数据")
|
||||
@PostMapping("getSdcDataMapByUserIds")
|
||||
public Map<String,List<WatchStatUserInfoDaySdc>> getSdcDataMapByUserIds(@RequestBody List<String> userIds,
|
||||
@RequestParam(name = "startTime")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd") Date startTime,
|
||||
@RequestParam(name = "endTime")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd") Date endTime) {
|
||||
@RequestParam(name = "startTime")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd") Date startTime,
|
||||
@RequestParam(name = "endTime")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd") Date endTime) {
|
||||
return watchDeviceService.getSdcDataMapByUserIds(userIds, startTime, endTime);
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -38,6 +38,17 @@ public interface IWatchMonitorDataService extends IService<WatchMonitorData> {
|
||||
*/
|
||||
AjaxJson notice(JSONObject jsonObject);
|
||||
|
||||
/**
|
||||
* 更新告警事件GPS位置信息
|
||||
* 根据 watchNo + eventId 联合定位已有告警数据;
|
||||
* 缺失任一定位字段则忽略;已存在GPS信息则忽略;
|
||||
* 仅更新数据,不推送大屏与短信。
|
||||
*
|
||||
* @param jsonObject 入参(含 watchNo、eventId、lon、lat、address、locateTime、gpsErrorMsg 等)
|
||||
* @return 处理结果
|
||||
*/
|
||||
AjaxJson updateMonitorGps(JSONObject jsonObject);
|
||||
|
||||
/**
|
||||
* 处理历史数据 orgCode为空的问题
|
||||
* @return
|
||||
|
||||
+105
-4
@@ -595,11 +595,32 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
filter.setOrgCodeLeaf("A99A99A99");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询部门统计数据(分页)
|
||||
*
|
||||
* @param filter 查询过滤条件,包含部门编码、时间范围、分页参数等
|
||||
* @return 分页的部门统计数据列表
|
||||
*/
|
||||
@Override
|
||||
public IPage<DeptStatData> getDeptData(DepartStatsFilter filter) {
|
||||
return getDeptData(filter, true).pagedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询部门统计数据的核心实现
|
||||
* <p>
|
||||
* 执行流程:
|
||||
* 1. 权限校验,补全过滤条件
|
||||
* 2. 查询各部门设备总数 Map 和绑定/佩戴数 Map
|
||||
* 3. 根据过滤条件确定最终需要展示的部门列表
|
||||
* 4. 查询各部门异常预警数量
|
||||
* 5. 按需分页,将部门列表转换为统计 DTO
|
||||
* </p>
|
||||
*
|
||||
* @param filter 查询过滤条件
|
||||
* @param paged 是否需要分页,true=分页查询,false=全量查询(用于导出等场景)
|
||||
* @return 包含统计数据和中间结果的传输对象
|
||||
*/
|
||||
private DepartStatTransfer getDeptData(DepartStatsFilter filter, boolean paged) {
|
||||
authCheck(filter);
|
||||
DepartStatTransfer transfer = new DepartStatTransfer()
|
||||
@@ -639,12 +660,29 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
return transfer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将单个部门实体转换为统计 DTO
|
||||
* <p>
|
||||
* orgCode 编码规则:3位一层,如 A01=一级,A01A01=二级,A01A01A01=三级
|
||||
* 区分三种视图:
|
||||
* - 一级部门(orgCode 长度为 3):展示自身信息,叶子名称置为 "-",取自身设备总数
|
||||
* - 二级部门(orgCode 长度为 6):同一级,展示自身信息,叶子名称置为 "-",取自身设备总数
|
||||
* - 三级部门(orgCode 长度为 9):展示所属上级名称 + 自身名称,绑定/佩戴数取自身数据
|
||||
* 当 allSecond=true(全量视图)时,绑定/佩戴数始终取各部门自身数据
|
||||
* </p>
|
||||
*
|
||||
* @param depart 部门实体
|
||||
* @param transfer 包含所有统计中间数据的传输对象
|
||||
* @return 填充完毕的部门统计 DTO
|
||||
*/
|
||||
private DeptStatData transferData(SysDepart depart, DepartStatTransfer transfer) {
|
||||
DeptStatData deptStatData = new DeptStatData();
|
||||
String orgCode = null != depart ? depart.getOrgCode() : null;
|
||||
boolean allSecond = StrUtil.isEmpty(transfer.filter().getOrgCode());
|
||||
boolean isFirst = StrUtil.isNotEmpty(orgCode) && orgCode.length() == 3;
|
||||
boolean isSecond = StrUtil.isNotEmpty(orgCode) && orgCode.length() == 6;
|
||||
if (isSecond) {
|
||||
if (isFirst || isSecond) {
|
||||
// 一级/二级部门行:展示自身信息,叶子名称占位
|
||||
deptStatData.setOrgCode(orgCode);
|
||||
deptStatData.setOrgCodeLeaf(orgCode);
|
||||
deptStatData.setOrgCodeName(depart.getDepartName());
|
||||
@@ -655,6 +693,7 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
.getDeviceCount()
|
||||
);
|
||||
} else {
|
||||
// 三级部门行:展示所属上级名称 + 自身名称
|
||||
deptStatData.setOrgCode(Optional.ofNullable(transfer.secondDepart()).map(SysDepart::getOrgCode).orElse(""));
|
||||
deptStatData.setOrgCodeLeaf(orgCode);
|
||||
deptStatData.setOrgCodeName(Optional.ofNullable(transfer.secondDepart()).map(SysDepart::getDepartName).orElse(""));
|
||||
@@ -662,15 +701,21 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
}
|
||||
fillWatchNums(
|
||||
deptStatData,
|
||||
(!allSecond && isSecond)
|
||||
(!allSecond && (isFirst || isSecond))
|
||||
?
|
||||
transfer.secondDepartBindAndWearNum()
|
||||
:
|
||||
transfer.departBindAndWearNumMap().getOrDefault(orgCode, new DepartDeviceNum())
|
||||
);
|
||||
// 未分配设备数 = 入库设备数 - 已分配设备数,仅一、二级部门有入库数据
|
||||
if (deptStatData.getTotalAssignedDeviceNums() != null) {
|
||||
deptStatData.setUndistributedDeviceNums(
|
||||
deptStatData.getTotalAssignedDeviceNums() - deptStatData.getAssignedDeviceNums()
|
||||
);
|
||||
}
|
||||
fillWarningNums(
|
||||
deptStatData,
|
||||
(!allSecond && isSecond)
|
||||
(!allSecond && (isFirst || isSecond))
|
||||
?
|
||||
transfer.secondDepartAbnormalCount()
|
||||
:
|
||||
@@ -679,12 +724,30 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
return deptStatData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充手表绑定与佩戴相关数量
|
||||
* <p>
|
||||
* assignedDeviceNums = 已绑定设备数
|
||||
* assignedUserNums = 已佩戴人数
|
||||
* undistributedUserNums = 已绑定但未佩戴数(bindCount - wearCount)
|
||||
* </p>
|
||||
*/
|
||||
private void fillWatchNums(DeptStatData deptStatData, DepartDeviceNum departDeviceNum) {
|
||||
deptStatData.setAssignedDeviceNums(Optional.ofNullable(departDeviceNum.getBindCount()).orElse(0));
|
||||
deptStatData.setAssignedUserNums(Optional.ofNullable(departDeviceNum.getWearCount()).orElse(0));
|
||||
deptStatData.setUndistributedUserNums(deptStatData.getAssignedDeviceNums() - deptStatData.getAssignedUserNums());
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充各健康指标的预警数量
|
||||
* <p>
|
||||
* 从 abnormalCountMap 中按事件类型取值:
|
||||
* stress → 压力预警数
|
||||
* heart_rate → 心率预警数
|
||||
* spo2 → 血氧预警数
|
||||
* temperature_anomalies → 体温预警数
|
||||
* </p>
|
||||
*/
|
||||
private void fillWarningNums(DeptStatData deptStatData, Map<String, AbnormalCount> abnormalCountMap) {
|
||||
deptStatData.setStressNums(Optional.ofNullable(abnormalCountMap.get("stress")).map(AbnormalCount::getWarningCount).orElse(0));
|
||||
deptStatData.setHeartRateNums(Optional.ofNullable(abnormalCountMap.get("heart_rate")).map(AbnormalCount::getWarningCount).orElse(0));
|
||||
@@ -722,6 +785,10 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
return sysCache.getDepartByOrgCode(filter.getOrgCodeLeaf());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询各部门的设备总数 Map(orgCode → 设备数)
|
||||
* 当过滤条件指定了三级部门(orgCodeLeaf 不为空)时,设备总数无需展示,直接返回空 Map
|
||||
*/
|
||||
private Map<String, DepartDeviceNum> getDepartDeviceNumMap(DepartStatsFilter filter) {
|
||||
if (StrUtil.isNotEmpty(filter.getOrgCodeLeaf())) {
|
||||
return Collections.emptyMap();
|
||||
@@ -732,6 +799,9 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
.collect(Collectors.toMap(DepartDeviceNum::getOrgCode, Function.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询各部门的设备绑定数与佩戴数 Map(orgCode → 绑定/佩戴数)
|
||||
*/
|
||||
private Map<String, DepartDeviceNum> getDepartBindAndWearNumMap(DepartStatsFilter filter) {
|
||||
return watchStatisticsMapper.deviceBindAndWearNums(filter)
|
||||
.stream()
|
||||
@@ -739,6 +809,10 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
.collect(Collectors.toMap(DepartDeviceNum::getOrgCode, Function.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询各部门各事件类型的异常预警数量,结构为双层 Map:
|
||||
* orgCode → (eventType → AbnormalCount)
|
||||
*/
|
||||
private Map<String, Map<String, AbnormalCount>> getDepartAbnormalCountMap(DepartStatsFilter filter) {
|
||||
return watchStatisticsMapper.abnormalWarningCount(filter)
|
||||
.stream()
|
||||
@@ -746,6 +820,12 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
.collect(Collectors.groupingBy(AbnormalCount::getOrgCode, Collectors.toMap(AbnormalCount::getEventType, Function.identity())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇总二级部门下所有三级部门的绑定/佩戴数,用于二级部门行的数据展示
|
||||
* <p>
|
||||
* 仅在"指定了二级部门但未指定三级部门"时有效,其他情况返回 null
|
||||
* </p>
|
||||
*/
|
||||
private DepartDeviceNum getSecondDepartBindAndWearNum(DepartStatsFilter filter,
|
||||
Map<String, DepartDeviceNum> departBindAndWearNumMap) {
|
||||
if (StrUtil.isNotEmpty(filter.getOrgCodeLeaf()) || StrUtil.isEmpty(filter.getOrgCode())) {
|
||||
@@ -764,6 +844,13 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
return n1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇总二级部门下所有三级部门的异常预警数,用于二级部门行的数据展示
|
||||
* <p>
|
||||
* 仅在"指定了二级部门但未指定三级部门"时有效,其他情况返回 null
|
||||
* 合并时按 eventType 累加 warningCount
|
||||
* </p>
|
||||
*/
|
||||
private Map<String, AbnormalCount> getSecondDepartAbnormalCount(DepartStatsFilter filter,
|
||||
Map<String, Map<String, AbnormalCount>> departAbnormalCountMap) {
|
||||
if (StrUtil.isNotEmpty(filter.getOrgCodeLeaf()) || StrUtil.isEmpty(filter.getOrgCode())) {
|
||||
@@ -778,6 +865,14 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
return n1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据过滤条件确定最终需要展示的部门列表,分三种情况:
|
||||
* <ul>
|
||||
* <li>指定三级部门(orgCodeLeaf 不为空):仅返回该三级部门(无绑定数据则返回空)</li>
|
||||
* <li>指定二级部门(orgCode 不为空):返回该部门本身 + 其下有绑定数据的子部门,按层级(orgCode 长度)升序后再按排序字段排序</li>
|
||||
* <li>全量(orgCode 为空):返回所有有设备数据的部门,同样按层级升序后再按排序字段排序,确保一级部门排在最前</li>
|
||||
* </ul>
|
||||
*/
|
||||
private List<SysDepart> getFinalDeparts(DepartStatsFilter filter,
|
||||
Map<String, DepartDeviceNum> departDeviceNumMap,
|
||||
Map<String, DepartDeviceNum> departBindAndWearNumMap) {
|
||||
@@ -804,7 +899,10 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
return departDeviceNumMap.keySet()
|
||||
.stream()
|
||||
.map(sysBaseAPI::getDepartIdsByOrgCodeNew)
|
||||
.sorted(this::departComparator)
|
||||
.sorted(
|
||||
Comparator.<SysDepart>comparingInt(sysDepart -> sysDepart.getOrgCode().length())
|
||||
.thenComparing(this::departComparator)
|
||||
)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -825,6 +923,9 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
|
||||
return order1.compareTo(order2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对内存中的部门列表执行分页截取
|
||||
*/
|
||||
private List<SysDepart> getPagedDeparts(List<SysDepart> sysDeparts, DepartStatsFilter filter) {
|
||||
return getSubList(sysDeparts, filter.getPageNo(), filter.getPageSize());
|
||||
}
|
||||
|
||||
+3
@@ -3,6 +3,7 @@ package com.renkang.watch.service.impl;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -87,6 +88,7 @@ public class WatchBindHisServiceImpl extends ServiceImpl<WatchBindHisMapper, Wat
|
||||
@Override
|
||||
public IPage<WatchBindHisVo> queryPageList(IPage<WatchBindHisVo> page, WatchBindHisDTO dto) {
|
||||
if (StringUtils.hasLength(dto.getBindRealName())) {
|
||||
dto.setBindRealName(HtmlUtil.unescape(dto.getBindRealName()));
|
||||
List<String> userIdsByDepartAndUsername = watchCloudManager.getUserIdsByDepartAndUsername("", dto.getBindRealName(), "", "");
|
||||
if (CollectionUtil.isEmpty(userIdsByDepartAndUsername)) {
|
||||
return new Page<>();
|
||||
@@ -372,6 +374,7 @@ public class WatchBindHisServiceImpl extends ServiceImpl<WatchBindHisMapper, Wat
|
||||
String orgCode = StrUtil.isNotBlank(dto.getThirdDepart())?dto.getThirdDepart() : dto.getSecondDepart();
|
||||
List<String> userIds = new ArrayList<>();
|
||||
if (StrUtil.isNotBlank(dto.getBindRealName())) {
|
||||
dto.setBindRealName(HtmlUtil.unescape(dto.getBindRealName()));
|
||||
userIds = watchCloudManager.getUserIdsByDepartAndUsername("", dto.getBindRealName(), "", "");
|
||||
if (CollectionUtil.isEmpty(userIds)) {
|
||||
return new Page<>();
|
||||
|
||||
+4
@@ -7,6 +7,7 @@ import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
@@ -104,6 +105,9 @@ public class WatchDataServiceImpl extends ServiceImpl<WatchDataMapper, WatchData
|
||||
}
|
||||
|
||||
String sql = QueryGenerator.installAuthJdbc(WatchData.class);
|
||||
if(StrUtil.isNotBlank(watchData.getUsername())){
|
||||
watchData.setUsername(HtmlUtil.unescape(watchData.getUsername()));
|
||||
}
|
||||
IPage<WatchData> pageList = watchDataMapper.pageCustom(page, watchData, sql);
|
||||
if (CollectionUtil.isNotEmpty(pageList.getRecords())) {
|
||||
List<LoginUserNew> loginUserNews = null;
|
||||
|
||||
+41
-18
@@ -7,6 +7,7 @@ import cn.hutool.core.date.DateUnit;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
@@ -167,6 +168,9 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
||||
@Override
|
||||
public IPage<WatchDeviceVo> queryPageList(IPage<WatchDeviceVo> page, WatchDeviceDTO dto) {
|
||||
if (StrUtil.isNotBlank(dto.getBindRealName()) || StrUtil.isNotBlank(dto.getWorkNo())) {
|
||||
if(StrUtil.isNotBlank(dto.getBindRealName())){
|
||||
dto.setBindRealName(HtmlUtil.unescape(dto.getBindRealName()));
|
||||
}
|
||||
List<String> userIds = watchCloudManager.getUserIdsByDepartAndUsername("", dto.getBindRealName(), "", dto.getWorkNo());
|
||||
if (CollectionUtil.isEmpty(userIds)) {
|
||||
return new Page<>();
|
||||
@@ -297,9 +301,9 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
||||
// 新增手表只能绑定给本单位的人
|
||||
LoginUser userById = sysBaseAPI.getUserById(dto.getBindUserId());
|
||||
|
||||
if (userById.getOrgCode() == null || dto.getOrgCode() == null ||
|
||||
userById.getOrgCode().length() < 6 || dto.getOrgCode().length() < 6 ||
|
||||
!userById.getOrgCode().substring(0, 6).equals(dto.getOrgCode().substring(0, 6))) {
|
||||
if (userById.getOrgCode() == null || watchDevice.getOrgCode() == null ||
|
||||
userById.getOrgCode().length() < 6 || watchDevice.getOrgCode().length() < 6 ||
|
||||
!userById.getOrgCode().substring(0, 6).equals(watchDevice.getOrgCode().substring(0, 6))) {
|
||||
return Result.error("手表只能绑定给本单位人员");
|
||||
}
|
||||
|
||||
@@ -327,6 +331,13 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
||||
}
|
||||
// 更新工具信息
|
||||
watchDevice.setBindDate(dto.getBindDate());
|
||||
// 根据员工更新手表单位信息(如果单位编码大于6位,取6位)
|
||||
String orgCode = userById.getOrgCode();
|
||||
if(StrUtil.isNotBlank(orgCode) && orgCode.length() > 6){
|
||||
orgCode = orgCode.substring(0, 6);
|
||||
}
|
||||
watchDevice.setOrgCode(orgCode);
|
||||
watchDevice.setDeptId(sysCache.getDepartIdByOrgCode(orgCode));
|
||||
bindUserToDevice(watchDevice, dto);
|
||||
if (StrUtil.isNotBlank(dto.getBindUserId())) {
|
||||
// 初始化开关数据
|
||||
@@ -432,11 +443,13 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result unbundleDevice(String watchNo){
|
||||
BindUserDTO dto = new BindUserDTO();
|
||||
dto.setBindUserId(GlobalUtils.getLoginUser().getId());
|
||||
dto.setWatchNo(watchNo);
|
||||
dto.setBindDate(new Date());
|
||||
return unbundleUser(dto);
|
||||
// BindUserDTO dto = new BindUserDTO();
|
||||
// dto.setBindUserId(GlobalUtils.getLoginUser().getId());
|
||||
// dto.setWatchNo(watchNo);
|
||||
// dto.setBindDate(new Date());
|
||||
// return unbundleUser(dto);
|
||||
// 2026.04.09 说 不能从移动端解绑了
|
||||
return Result.error("请联系管理员解绑");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,6 +498,13 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
||||
updateWrapper.eq("id", watchDevice.getId());
|
||||
updateWrapper.set("bind_user_id", dto.getBindUserId());
|
||||
updateWrapper.set("bind_date", watchDevice.getBindDate());
|
||||
if(StrUtil.isNotBlank(watchDevice.getOrgCode())){
|
||||
updateWrapper.set("org_code", watchDevice.getOrgCode());
|
||||
}
|
||||
if (StrUtil.isNotBlank(watchDevice.getDeptId())){
|
||||
updateWrapper.set("dept_id", watchDevice.getDeptId());
|
||||
}
|
||||
|
||||
this.update(updateWrapper);
|
||||
}
|
||||
|
||||
@@ -501,15 +521,18 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
||||
switchUpdateWrapper.eq(WatchDeviceSwitch::getWatchNo, dto.getWatchNo());
|
||||
watchDeviceSwitchService.update(switchUpdateWrapper);
|
||||
String[] wdTypes = {"heart_rate", "spo2", "stress", "temperature"};
|
||||
for (int i = 0; i < wdTypes.length; i++) {
|
||||
WarnSwitch warnSwitch = new WarnSwitch();
|
||||
warnSwitch.setWatchNo(dto.getWatchNo());
|
||||
warnSwitch.setWdType(wdTypes[i]);
|
||||
warnSwitch.setSwitchFlag("1");
|
||||
dataCenterExecutor.saveWarnSwitch(warnSwitch);
|
||||
}
|
||||
//通知更新
|
||||
dataCenterExecutor.cmdUpdateSwitch(dto.getWatchNo());
|
||||
// 异步调用外部系统,防止外部系统异常导致功能异常,解绑这里非必要
|
||||
asyncTaskExecutor.execute(() -> {
|
||||
for (int i = 0; i < wdTypes.length; i++) {
|
||||
WarnSwitch warnSwitch = new WarnSwitch();
|
||||
warnSwitch.setWatchNo(dto.getWatchNo());
|
||||
warnSwitch.setWdType(wdTypes[i]);
|
||||
warnSwitch.setSwitchFlag("1");
|
||||
dataCenterExecutor.saveWarnSwitch(warnSwitch);
|
||||
}
|
||||
//通知更新
|
||||
dataCenterExecutor.cmdUpdateSwitch(dto.getWatchNo());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2238,7 +2261,7 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
|
||||
cloudResultData.add(user);
|
||||
// 判断是否绑定手表
|
||||
List<WatchDevice> watchDeviceList = watchDeviceMapper.selectList(new LambdaQueryWrapper<WatchDevice>()
|
||||
.in(WatchDevice::getBindUserId, cloudResultData.stream().map(FamilyMembersVO::getFamilyMembersId).collect(Collectors.toList())));
|
||||
.in(WatchDevice::getBindUserId, cloudResultData.stream().map(FamilyMembersVO::getFamilyMembersId).collect(Collectors.toList())));
|
||||
if (CollUtil.isEmpty(watchDeviceList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
+99
@@ -265,6 +265,102 @@ public class WatchMonitorDataServiceImpl extends ServiceImpl<WatchMonitorDataMap
|
||||
return ajaxJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新告警事件GPS位置信息
|
||||
* 根据 watchNo + eventId 联合定位已有告警数据;
|
||||
* watchNo 或 eventId 任一缺失则忽略;
|
||||
* 已存在 GPS 信息则忽略本次更新;
|
||||
* 参考 createMonitorData 进行 GPS84 → GCJ02 坐标转换 + 高德地址反查;
|
||||
* 仅更新数据,不推送大屏与短信。
|
||||
*
|
||||
* @param json 入参(含 watchNo、eventId、lon、lat、address、locateTime、gpsErrorMsg 等)
|
||||
* @return 处理结果
|
||||
*/
|
||||
@Override
|
||||
public AjaxJson updateMonitorGps(JSONObject json) {
|
||||
GlobalUtils.setHttpFeignToken();
|
||||
|
||||
AjaxJson ajaxJson = new AjaxJson();
|
||||
logger.info("接收更新告警事件GPS信息=={}", json.toJSONString());
|
||||
|
||||
WatchNotifyVo data = json.toJavaObject(WatchNotifyVo.class);
|
||||
|
||||
// watchNo 或 eventId 任一缺失则忽略
|
||||
if (StrUtil.isBlank(data.getWatchNo()) || StrUtil.isBlank(data.getEventId())) {
|
||||
logger.warn("更新GPS信息缺失关键定位字段,忽略本次请求 watchNo={}, eventId={}", data.getWatchNo(), data.getEventId());
|
||||
ajaxJson.setSuccess(true);
|
||||
return ajaxJson;
|
||||
}
|
||||
|
||||
// watchNo + eventId 联合定位已有告警数据
|
||||
LambdaQueryWrapper<WatchMonitorData> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(WatchMonitorData::getWatchNo, data.getWatchNo())
|
||||
.eq(WatchMonitorData::getEventId, data.getEventId());
|
||||
List<WatchMonitorData> existList = this.list(queryWrapper);
|
||||
if (CollUtil.isEmpty(existList)) {
|
||||
logger.warn("更新GPS信息失败,未匹配到告警记录 watchNo={}, eventId={}", data.getWatchNo(), data.getEventId());
|
||||
ajaxJson.setSuccess(true);
|
||||
return ajaxJson;
|
||||
}
|
||||
|
||||
// 已存在GPS信息则忽略本次更新,避免覆盖已有有效坐标
|
||||
boolean hasGps = existList.stream().anyMatch(m -> m.getLon() != null && m.getLat() != null);
|
||||
if (hasGps) {
|
||||
logger.info("告警记录已有GPS信息,忽略本次更新 watchNo={}, eventId={}", data.getWatchNo(), data.getEventId());
|
||||
ajaxJson.setSuccess(true);
|
||||
return ajaxJson;
|
||||
}
|
||||
|
||||
// 计算高德坐标与高德地址(参考 createMonitorData 逻辑)
|
||||
WatchMonitorData updateEntity = new WatchMonitorData();
|
||||
updateEntity.setLon(data.getLon());
|
||||
updateEntity.setLat(data.getLat());
|
||||
updateEntity.setAddress(data.getAddress());
|
||||
updateEntity.setGpsErrorMsg(data.getGpsErrorMsg());
|
||||
updateEntity.setLocateTime(data.getLocateTime());
|
||||
|
||||
if (Stream.of(data.getLon(), data.getLat()).allMatch(value -> ObjUtil.isNotEmpty(value) && value != 0)) {
|
||||
Gps gps = GPS84ToGCJ02(data.getLon(), data.getLat());
|
||||
if (null != gps) {
|
||||
// 写入高德坐标
|
||||
updateEntity.setLonGd(BigDecimal.valueOf(gps.getLongitude()));
|
||||
updateEntity.setLatGd(BigDecimal.valueOf(gps.getLatitude()));
|
||||
// 调用高德接口获取地址信息
|
||||
AmapResultVo amapResultVo = amapService.getFormattedAddress(gps.getLongitude(), gps.getLatitude(), 5000);
|
||||
if (amapResultVo.isSuccess()) {
|
||||
updateEntity.setAddressGd(amapResultVo.getAddress());
|
||||
updateEntity.setAddress(amapResultVo.getAddress());
|
||||
} else {
|
||||
String originError = StrUtil.isBlank(updateEntity.getGpsErrorMsg()) ? "" : updateEntity.getGpsErrorMsg() + ";";
|
||||
updateEntity.setGpsErrorMsg(originError + amapResultVo.getErrorMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 仅更新 GPS 相关字段,不推送大屏、不发短信
|
||||
boolean updated = this.lambdaUpdate()
|
||||
.eq(WatchMonitorData::getWatchNo, data.getWatchNo())
|
||||
.eq(WatchMonitorData::getEventId, data.getEventId())
|
||||
.set(WatchMonitorData::getLon, updateEntity.getLon())
|
||||
.set(WatchMonitorData::getLat, updateEntity.getLat())
|
||||
.set(WatchMonitorData::getAddress, updateEntity.getAddress())
|
||||
.set(WatchMonitorData::getLonGd, updateEntity.getLonGd())
|
||||
.set(WatchMonitorData::getLatGd, updateEntity.getLatGd())
|
||||
.set(WatchMonitorData::getAddressGd, updateEntity.getAddressGd())
|
||||
.set(WatchMonitorData::getGpsErrorMsg, updateEntity.getGpsErrorMsg())
|
||||
.set(WatchMonitorData::getLocateTime, updateEntity.getLocateTime())
|
||||
.update();
|
||||
|
||||
if (!updated) {
|
||||
logger.warn("更新GPS信息失败 watchNo={}, eventId={}", data.getWatchNo(), data.getEventId());
|
||||
} else {
|
||||
logger.info("更新GPS信息成功 watchNo={}, eventId={}, 共更新{}条", data.getWatchNo(), data.getEventId(), existList.size());
|
||||
}
|
||||
|
||||
ajaxJson.setSuccess(true);
|
||||
return ajaxJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建异常数据
|
||||
*
|
||||
@@ -272,13 +368,16 @@ public class WatchMonitorDataServiceImpl extends ServiceImpl<WatchMonitorDataMap
|
||||
* @return 异常数据
|
||||
*/
|
||||
private WatchMonitorData createMonitorData(WatchNotifyVo data) {
|
||||
|
||||
log.info("开始构建异常数据 => {}", JSONObject.toJSONString(data));
|
||||
WatchMonitorData monitorDataEntity = new WatchMonitorData();
|
||||
monitorDataEntity.setAddress(data.getAddress());
|
||||
monitorDataEntity.setWatchNo(data.getWatchNo());
|
||||
monitorDataEntity.setLon(data.getLon());
|
||||
monitorDataEntity.setLat(data.getLat());
|
||||
monitorDataEntity.setLocateTime(data.getLocateTime());
|
||||
monitorDataEntity.setEventType(data.getEventType());
|
||||
monitorDataEntity.setEventId(data.getEventId());
|
||||
monitorDataEntity.setDataValue(data.getDataValue());
|
||||
monitorDataEntity.setCreateDate(data.getCreateTime());
|
||||
monitorDataEntity.setWarnTime(data.getWarnTime());
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.apache.http.impl.client.HttpClients;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.ExceptionAssertsUtil;
|
||||
import org.jeecg.common.util.filter.StrAttackFilter;
|
||||
import org.jeecg.util.GlobalManager;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -51,7 +52,9 @@ public class MyUploadUtil {
|
||||
private static String muFileListUploadPath;
|
||||
private static String muDownPath;
|
||||
private static String fileTypeWhiteList;
|
||||
// private static final int timeout = 10000;
|
||||
/** 全局配置管理器,用于读取 Redis 中的动态配置 */
|
||||
private static GlobalManager globalManager;
|
||||
// private static final int timeout = 10000;
|
||||
//有些大文件可能需要比较长的时间,这里改成5分钟
|
||||
private static final int socketTimeout = 10*60*1000;
|
||||
private static final int connectTimeout = 60*1000;
|
||||
@@ -108,6 +111,10 @@ public class MyUploadUtil {
|
||||
MyUploadUtil.fileTypeWhiteList = fileTypeWhiteList;
|
||||
}
|
||||
|
||||
public static void setGlobalManager(GlobalManager globalManager) {
|
||||
MyUploadUtil.globalManager = globalManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
@@ -116,43 +123,52 @@ public class MyUploadUtil {
|
||||
*/
|
||||
|
||||
public static String upload(MultipartFile file, String bizPath) throws Exception {
|
||||
String fileUrl = "";
|
||||
String fileName = file.getOriginalFilename();// 获取文件名
|
||||
checkFileLegitimacy(fileName);
|
||||
//???先屏蔽了
|
||||
// if (StringUtils.hasText(file.getOriginalFilename())) {
|
||||
// String originalFilename = file.getOriginalFilename();
|
||||
// String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
|
||||
// fileName = fileName + ext;
|
||||
// }
|
||||
bizPath = StrAttackFilter.filter(bizPath);
|
||||
// .tar 文件固定走 MyUpload
|
||||
if (fileName != null && (fileName.endsWith(".tar") || fileName.endsWith(".apk"))) {
|
||||
log.info("上传文件为tar/apk文件,使用MyUploadUtil上传");
|
||||
try {
|
||||
return upload(file.getInputStream(), bizPath, fileName,true);
|
||||
} catch (Exception e) {
|
||||
log.error("upload error {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// 其他文件:oss_flag=1 走 XjOssUtil,否则走 MyUpload
|
||||
String ossFlag = globalManager != null ? globalManager.getConfigValueByKey("oss_flag") : "";
|
||||
if ("1".equals(ossFlag)) {
|
||||
log.info("开启新疆OSS上传");
|
||||
return XjOssUtil.upload(file, bizPath);
|
||||
}
|
||||
log.info("使用MyUploadUtil上传");
|
||||
try {
|
||||
fileUrl = upload(file.getInputStream(), bizPath, fileName);
|
||||
return upload(file.getInputStream(), bizPath, fileName);
|
||||
} catch (Exception e) {
|
||||
fileUrl = null;
|
||||
log.error("upload error", e);
|
||||
log.error("upload error {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
return fileUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件 带缩略图
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
public static String uploadImg(MultipartFile file, String bizPath, Boolean needThumbnailFlag) throws Exception {
|
||||
String fileUrl = "";
|
||||
String fileName = file.getOriginalFilename();// 获取文件名
|
||||
checkFileLegitimacy(fileName);
|
||||
bizPath = StrAttackFilter.filter(bizPath);
|
||||
try {
|
||||
fileUrl = uploadImg(file.getInputStream(), bizPath, fileName, needThumbnailFlag);
|
||||
} catch (Exception e) {
|
||||
log.error("upload error", e);
|
||||
}
|
||||
return fileUrl;
|
||||
}
|
||||
// /**
|
||||
// * 上传文件 带缩略图
|
||||
// *
|
||||
// * @param file
|
||||
// * @return
|
||||
// */
|
||||
// public static String uploadImg(MultipartFile file, String bizPath, Boolean needThumbnailFlag) throws Exception {
|
||||
// String fileUrl = "";
|
||||
// String fileName = file.getOriginalFilename();// 获取文件名
|
||||
// checkFileLegitimacy(fileName);
|
||||
// bizPath = StrAttackFilter.filter(bizPath);
|
||||
// try {
|
||||
// fileUrl = uploadImg(file.getInputStream(), bizPath, fileName, needThumbnailFlag);
|
||||
// } catch (Exception e) {
|
||||
// log.error("upload error", e);
|
||||
// }
|
||||
// return fileUrl;
|
||||
// }
|
||||
|
||||
public static String upload(InputStream stream, String bizPath, String fileName, boolean check) throws Exception {
|
||||
if (check) {
|
||||
@@ -220,145 +236,152 @@ public class MyUploadUtil {
|
||||
* @return
|
||||
*/
|
||||
public static String upload(InputStream stream, String bizPath, String fileName) throws Exception {
|
||||
// 其他文件:oss_flag=1 走 XjOssUtil,否则走 MyUpload
|
||||
String ossFlag = globalManager != null ? globalManager.getConfigValueByKey("oss_flag") : "";
|
||||
if ("1".equals(ossFlag)) {
|
||||
log.info("开启新疆OSS上传");
|
||||
return XjOssUtil.upload(stream, bizPath,fileName);
|
||||
}
|
||||
log.info("使用MyUploadUtil上传");
|
||||
return upload(stream, bizPath, fileName, true);
|
||||
}
|
||||
/**
|
||||
* 上传文件到myupload
|
||||
*
|
||||
* @param stream
|
||||
* @return
|
||||
*/
|
||||
public static String uploadImg(InputStream stream, String bizPath, String fileName, Boolean needThumbnailFlag) throws Exception {
|
||||
//避免重复文件覆盖,这里处理一下
|
||||
if (fileName.indexOf(".") > -1) {
|
||||
//String fileNameLeft = fileName.substring(0, fileName.lastIndexOf("."));
|
||||
String fileNameLeft = UUID.randomUUID().toString();
|
||||
fileName = fileNameLeft + "_" + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."));
|
||||
} else {
|
||||
fileName += "_" + System.currentTimeMillis();
|
||||
}
|
||||
String nowday = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||
String path = bizPath + File.separator + nowday;
|
||||
String url = muUrl + "/" + muFileUploadPath;
|
||||
String fileUrl = "";
|
||||
//创建post方法连接实例,在post方法中传入待连接地址
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse httpResponse = null;
|
||||
// /**
|
||||
// * 上传文件到myupload
|
||||
// *
|
||||
// * @param stream
|
||||
// * @return
|
||||
// */
|
||||
// public static String uploadImg(InputStream stream, String bizPath, String fileName, Boolean needThumbnailFlag) throws Exception {
|
||||
// //避免重复文件覆盖,这里处理一下
|
||||
// if (fileName.indexOf(".") > -1) {
|
||||
// //String fileNameLeft = fileName.substring(0, fileName.lastIndexOf("."));
|
||||
// String fileNameLeft = UUID.randomUUID().toString();
|
||||
// fileName = fileNameLeft + "_" + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."));
|
||||
// } else {
|
||||
// fileName += "_" + System.currentTimeMillis();
|
||||
// }
|
||||
// String nowday = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||
// String path = bizPath + File.separator + nowday;
|
||||
// String url = muUrl + "/" + muFileUploadPath;
|
||||
// String fileUrl = "";
|
||||
// //创建post方法连接实例,在post方法中传入待连接地址
|
||||
// CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
// CloseableHttpResponse httpResponse = null;
|
||||
//
|
||||
// byte[] bytes = IoUtil.readBytes(stream);
|
||||
//
|
||||
// try {
|
||||
// URI uri = new URIBuilder(url)
|
||||
// .addParameter("fileName", fileName)
|
||||
// .addParameter("path", path)
|
||||
// .build();
|
||||
// HttpPost httppost = new HttpPost(uri);
|
||||
// RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
|
||||
// httppost.setConfig(requestConfig);
|
||||
// httppost.addHeader("token", muToken);
|
||||
// HttpEntity reqEntity = MultipartEntityBuilder.create()
|
||||
// //设置浏览器兼容模式
|
||||
// .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
|
||||
// //设置请求的编码格式
|
||||
// .setCharset(StandardCharsets.UTF_8)
|
||||
// .addBinaryBody("file", stream, ContentType.APPLICATION_OCTET_STREAM, fileName)
|
||||
// .build();
|
||||
// httppost.setEntity(reqEntity);
|
||||
// httppost.setEntity(reqEntity);
|
||||
//
|
||||
// httpResponse = httpClient.execute(httppost);
|
||||
//
|
||||
//
|
||||
// int backCode = httpResponse.getStatusLine().getStatusCode();
|
||||
// if (backCode != HttpStatus.SC_OK) {
|
||||
// log.error("上传文件失败" + backCode);
|
||||
// } else {
|
||||
// fileUrl = path + File.separator + fileName;
|
||||
// fileUrl = relaceSlash(fileUrl);
|
||||
// }
|
||||
// } catch (IOException e) {
|
||||
// log.error("文件服务器连接失败", e);
|
||||
// } finally {
|
||||
// //释放资源
|
||||
// try {
|
||||
// httpClient.close();
|
||||
// if (httpResponse != null) {
|
||||
// httpResponse.close();
|
||||
// }
|
||||
// } catch (IOException e) {
|
||||
//// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (needThumbnailFlag) {
|
||||
// uploadThumbnail(url, bytes, fileName, path);
|
||||
// }
|
||||
//
|
||||
// return fileUrl;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 上传缩略图到myupload
|
||||
// *
|
||||
// * @return
|
||||
// */
|
||||
// private static void uploadThumbnail(String url, byte[] bytes, String fileName, String path) {
|
||||
// //创建post方法连接实例,在post方法中传入待连接地址
|
||||
// CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
// CloseableHttpResponse httpResponse = null;
|
||||
// try {
|
||||
// String thumbnailName = CommonConstant.THUMBNAIL_PREFIX + fileName;
|
||||
//
|
||||
// BufferedImage bufferedImage = Thumbnails.of(new ByteArrayInputStream(bytes))
|
||||
// .scale(CommonConstant.THUMBNAIL_SCALE)
|
||||
// .asBufferedImage();
|
||||
//
|
||||
// ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
// ImageIO.write(bufferedImage, "png", os);
|
||||
// InputStream is = new ByteArrayInputStream(os.toByteArray());
|
||||
// URI uri = new URIBuilder(url)
|
||||
// .addParameter("fileName", thumbnailName)
|
||||
// .addParameter("path", path)
|
||||
// .build();
|
||||
// HttpPost httppost = new HttpPost(uri);
|
||||
// RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
|
||||
// httppost.setConfig(requestConfig);
|
||||
// httppost.addHeader("token", muToken);
|
||||
// HttpEntity reqEntity = MultipartEntityBuilder.create()
|
||||
// //设置浏览器兼容模式
|
||||
// .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
|
||||
// //设置请求的编码格式
|
||||
// .setCharset(Consts.UTF_8)
|
||||
//
|
||||
// .addBinaryBody("file", is, ContentType.APPLICATION_OCTET_STREAM, thumbnailName)
|
||||
// .build();
|
||||
// httppost.setEntity(reqEntity);
|
||||
//
|
||||
// httpResponse = httpClient.execute(httppost);
|
||||
//
|
||||
// int backCode = httpResponse.getStatusLine().getStatusCode();
|
||||
// if (backCode != HttpStatus.SC_OK) {
|
||||
// log.error("上传缩略图失败" + backCode);
|
||||
// }
|
||||
// } catch (IOException e) {
|
||||
// log.error("文件服务器连接失败", e);
|
||||
// } catch (URISyntaxException e) {
|
||||
// throw new RuntimeException(e);
|
||||
// } finally {
|
||||
// //释放资源
|
||||
// try {
|
||||
// httpClient.close();
|
||||
// if (httpResponse != null) {
|
||||
// httpResponse.close();
|
||||
// }
|
||||
// } catch (IOException e) {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
byte[] bytes = IoUtil.readBytes(stream);
|
||||
|
||||
try {
|
||||
URI uri = new URIBuilder(url)
|
||||
.addParameter("fileName", fileName)
|
||||
.addParameter("path", path)
|
||||
.build();
|
||||
HttpPost httppost = new HttpPost(uri);
|
||||
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
|
||||
httppost.setConfig(requestConfig);
|
||||
httppost.addHeader("token", muToken);
|
||||
HttpEntity reqEntity = MultipartEntityBuilder.create()
|
||||
//设置浏览器兼容模式
|
||||
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
|
||||
//设置请求的编码格式
|
||||
.setCharset(StandardCharsets.UTF_8)
|
||||
.addBinaryBody("file", stream, ContentType.APPLICATION_OCTET_STREAM, fileName)
|
||||
.build();
|
||||
httppost.setEntity(reqEntity);
|
||||
httppost.setEntity(reqEntity);
|
||||
|
||||
httpResponse = httpClient.execute(httppost);
|
||||
|
||||
|
||||
int backCode = httpResponse.getStatusLine().getStatusCode();
|
||||
if (backCode != HttpStatus.SC_OK) {
|
||||
log.error("上传文件失败" + backCode);
|
||||
} else {
|
||||
fileUrl = path + File.separator + fileName;
|
||||
fileUrl = relaceSlash(fileUrl);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("文件服务器连接失败", e);
|
||||
} finally {
|
||||
//释放资源
|
||||
try {
|
||||
httpClient.close();
|
||||
if (httpResponse != null) {
|
||||
httpResponse.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (needThumbnailFlag) {
|
||||
uploadThumbnail(url, bytes, fileName, path);
|
||||
}
|
||||
|
||||
return fileUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传缩略图到myupload
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static void uploadThumbnail(String url, byte[] bytes, String fileName, String path) {
|
||||
//创建post方法连接实例,在post方法中传入待连接地址
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse httpResponse = null;
|
||||
try {
|
||||
String thumbnailName = CommonConstant.THUMBNAIL_PREFIX + fileName;
|
||||
|
||||
BufferedImage bufferedImage = Thumbnails.of(new ByteArrayInputStream(bytes))
|
||||
.scale(CommonConstant.THUMBNAIL_SCALE)
|
||||
.asBufferedImage();
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
ImageIO.write(bufferedImage, "png", os);
|
||||
InputStream is = new ByteArrayInputStream(os.toByteArray());
|
||||
URI uri = new URIBuilder(url)
|
||||
.addParameter("fileName", thumbnailName)
|
||||
.addParameter("path", path)
|
||||
.build();
|
||||
HttpPost httppost = new HttpPost(uri);
|
||||
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
|
||||
httppost.setConfig(requestConfig);
|
||||
httppost.addHeader("token", muToken);
|
||||
HttpEntity reqEntity = MultipartEntityBuilder.create()
|
||||
//设置浏览器兼容模式
|
||||
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
|
||||
//设置请求的编码格式
|
||||
.setCharset(Consts.UTF_8)
|
||||
|
||||
.addBinaryBody("file", is, ContentType.APPLICATION_OCTET_STREAM, thumbnailName)
|
||||
.build();
|
||||
httppost.setEntity(reqEntity);
|
||||
|
||||
httpResponse = httpClient.execute(httppost);
|
||||
|
||||
int backCode = httpResponse.getStatusLine().getStatusCode();
|
||||
if (backCode != HttpStatus.SC_OK) {
|
||||
log.error("上传缩略图失败" + backCode);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("文件服务器连接失败", e);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
//释放资源
|
||||
try {
|
||||
httpClient.close();
|
||||
if (httpResponse != null) {
|
||||
httpResponse.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getMuFileExistPath() {
|
||||
return muFileExistPath;
|
||||
}
|
||||
// public String getMuFileExistPath() {
|
||||
// return muFileExistPath;
|
||||
// }
|
||||
|
||||
public static void setMuFileExistPath(String muFileExistPath) {
|
||||
MyUploadUtil.muFileExistPath = muFileExistPath;
|
||||
|
||||
@@ -0,0 +1,542 @@
|
||||
package org.jeecg.common.util;
|
||||
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.jeecg.common.exception.ExceptionAssertsUtil;
|
||||
import org.jeecg.common.util.filter.StrAttackFilter;
|
||||
import org.jeecg.util.RedisClientUtil;
|
||||
import org.jeecg.util.RedisKeyPrefix;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 新疆油田平台(IOSP)OSS 文件工具类
|
||||
* <p>
|
||||
* 功能:
|
||||
* 1. OAuth2 client_credentials 鉴权,token 缓存到 Redis,401 时自动重新鉴权
|
||||
* 2. 文件上传(支持 InputStream 和 MultipartFile)
|
||||
* 3. 文件下载(通过 fileKey 获取文件流,处理 302 重定向)
|
||||
* </p>
|
||||
* <p>
|
||||
* 配置项(Nacos 中配置,前缀 xj.oss):
|
||||
* - gateway-url 网关地址,如 http://api.iosp.ydpt.tech
|
||||
* - client-id OAuth2 客户端ID
|
||||
* - client-secret OAuth2 客户端密钥
|
||||
* - organization-id 组织ID(上传/下载路径参数)
|
||||
* - config-code 配置Code(上传/下载路径参数)
|
||||
* - base-url-prefix 文件基础路径前缀(可选,用于截取 Location 中的相对路径)
|
||||
* </p>
|
||||
*/
|
||||
@Slf4j
|
||||
public class XjOssUtil {
|
||||
|
||||
// ===================== 静态配置字段(由 XjOssConfig 注入) =====================
|
||||
|
||||
/** 网关地址,如 http://api.iosp.ydpt.tech */
|
||||
private static String gatewayUrl;
|
||||
/** OAuth2 客户端ID */
|
||||
private static String clientId;
|
||||
/** OAuth2 客户端密钥 */
|
||||
private static String clientSecret;
|
||||
/** 组织ID */
|
||||
private static String organizationId;
|
||||
/** 配置Code */
|
||||
private static String configCode;
|
||||
/** 文件基础路径前缀,如 http://11.71.10.44:8060/wbwj/scyx/scfz/aygc/jkgl/0 */
|
||||
private static String baseUrlPrefix;
|
||||
/** 允许上传的文件类型白名单,逗号分隔,如 jpg,png,pdf */
|
||||
private static String fileTypeWhiteList;
|
||||
/** 授权上传目录,如 scyx/scfz/aygc/jkgl/ */
|
||||
private static String directory;
|
||||
|
||||
// ===================== Redis 工具(由 XjOssConfig 注入) =====================
|
||||
|
||||
private static RedisClientUtil redisClientUtil;
|
||||
|
||||
// ===================== 常量 =====================
|
||||
|
||||
/** Redis key 前缀(不含过期时间,仅用于 delete/get 操作) */
|
||||
private static final RedisKeyPrefix REDIS_PREFIX_NO_TTL = new RedisKeyPrefix("xj:oss");
|
||||
/** Redis 中存储 token 的 key */
|
||||
private static final String TOKEN_KEY = "access_token";
|
||||
/** token 过期时间预留缓冲(秒),提前刷新避免临界过期 */
|
||||
private static final int TOKEN_EXPIRE_BUFFER = 60;
|
||||
/** HTTP 连接超时(毫秒) */
|
||||
private static final int CONNECT_TIMEOUT = 30_000;
|
||||
/** HTTP Socket 超时(毫秒),大文件上传需要较长时间 */
|
||||
private static final int SOCKET_TIMEOUT = 10 * 60_000;
|
||||
private static final String DIR = "/jkgl/";
|
||||
|
||||
// ===================== Setter(供 XjOssConfig 注入) =====================
|
||||
|
||||
public static void setGatewayUrl(String gatewayUrl) {
|
||||
XjOssUtil.gatewayUrl = gatewayUrl;
|
||||
}
|
||||
|
||||
public static void setClientId(String clientId) {
|
||||
XjOssUtil.clientId = clientId;
|
||||
}
|
||||
|
||||
public static void setClientSecret(String clientSecret) {
|
||||
XjOssUtil.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public static void setOrganizationId(String organizationId) {
|
||||
XjOssUtil.organizationId = organizationId;
|
||||
}
|
||||
|
||||
public static void setConfigCode(String configCode) {
|
||||
XjOssUtil.configCode = configCode;
|
||||
}
|
||||
|
||||
public static void setBaseUrlPrefix(String baseUrlPrefix) {
|
||||
XjOssUtil.baseUrlPrefix = baseUrlPrefix;
|
||||
}
|
||||
|
||||
public static void setFileTypeWhiteList(String fileTypeWhiteList) {
|
||||
XjOssUtil.fileTypeWhiteList = fileTypeWhiteList;
|
||||
}
|
||||
|
||||
public static void setDirectory(String directory) {
|
||||
XjOssUtil.directory = directory;
|
||||
}
|
||||
|
||||
public static void setRedisClientUtil(RedisClientUtil redisClientUtil) {
|
||||
XjOssUtil.redisClientUtil = redisClientUtil;
|
||||
}
|
||||
|
||||
// ===================== 鉴权 =====================
|
||||
|
||||
/**
|
||||
* 向 IOSP 网关发起 OAuth2 鉴权,获取 access_token 并存入 Redis
|
||||
* <p>
|
||||
* 接口:POST {gatewayUrl}/oauth/oauth/token
|
||||
* 参数:grant_type=client_credentials, client_id, client_secret
|
||||
* </p>
|
||||
*
|
||||
* @return 获取到的 access_token,失败返回 null
|
||||
*/
|
||||
public static String authenticate() {
|
||||
String url = gatewayUrl + "/oauth/oauth/token";
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
HttpPost post = new HttpPost(url);
|
||||
post.setConfig(buildRequestConfig());
|
||||
// 使用 multipart/form-data 提交鉴权参数
|
||||
post.setEntity(MultipartEntityBuilder.create()
|
||||
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
|
||||
.setCharset(StandardCharsets.UTF_8)
|
||||
.addTextBody("grant_type", "client_credentials")
|
||||
.addTextBody("client_id", clientId)
|
||||
.addTextBody("client_secret", clientSecret)
|
||||
.build());
|
||||
|
||||
response = httpClient.execute(post);
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode != HttpStatus.SC_OK) {
|
||||
log.error("[XjOssUtil] 鉴权失败,HTTP状态码: {}", statusCode);
|
||||
return null;
|
||||
}
|
||||
|
||||
String body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||
JSONObject json = JSON.parseObject(body);
|
||||
String token = json.getString("access_token");
|
||||
// expires_in 单位为秒,减去缓冲时间后存入 Redis
|
||||
int expiresIn = json.getIntValue("expires_in");
|
||||
int ttl = Math.max(expiresIn - TOKEN_EXPIRE_BUFFER, 30);
|
||||
|
||||
// 存入 Redis,TTL = expires_in - 缓冲;动态创建带 TTL 的 RedisKeyPrefix
|
||||
RedisKeyPrefix prefixWithTtl = new RedisKeyPrefix(ttl, "xj:oss");
|
||||
redisClientUtil.set(prefixWithTtl, TOKEN_KEY, token);
|
||||
log.info("[XjOssUtil] 鉴权成功,token 有效期 {}s(含缓冲)", ttl);
|
||||
return token;
|
||||
} catch (Exception e) {
|
||||
log.error("[XjOssUtil] 鉴权异常", e);
|
||||
return null;
|
||||
} finally {
|
||||
closeQuietly(httpClient, response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有效的 access_token
|
||||
* <p>
|
||||
* 优先从 Redis 读取缓存,不存在时自动重新鉴权
|
||||
* </p>
|
||||
*
|
||||
* @return 有效的 access_token,失败返回 null
|
||||
*/
|
||||
public static String getToken() {
|
||||
String token = redisClientUtil.get(REDIS_PREFIX_NO_TTL, TOKEN_KEY, String.class);
|
||||
if (token == null) {
|
||||
log.info("[XjOssUtil] Redis 中 token 不存在或已过期,重新鉴权");
|
||||
token = authenticate();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
// ===================== 文件上传 =====================
|
||||
|
||||
/**
|
||||
* 上传文件(MultipartFile 方式)
|
||||
*
|
||||
* @param file 前端上传的文件
|
||||
* @param bizPath 业务子路径,如 import、temp,拼接在授权目录之后
|
||||
* @return 上传成功后的文件 URL,失败返回 null
|
||||
*/
|
||||
public static String upload(MultipartFile file, String bizPath) {
|
||||
try {
|
||||
return upload(file.getInputStream(), bizPath, file.getOriginalFilename());
|
||||
} catch (Exception e) {
|
||||
log.error("[XjOssUtil] MultipartFile 上传失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件(InputStream 方式)
|
||||
*
|
||||
* @param stream 文件输入流
|
||||
* @param bizPath 业务子路径,如 import、temp,拼接在授权目录之后
|
||||
* @param fileName 文件名,如 test.png
|
||||
* @return 上传成功后的文件 URL,失败返回 null
|
||||
*/
|
||||
public static String upload(InputStream stream, String bizPath, String fileName) {
|
||||
checkFileLegitimacy(fileName);
|
||||
// 过滤上传文件夹名特殊字符,防止路径攻击
|
||||
bizPath = StrAttackFilter.filter(bizPath);
|
||||
// 构造上传 URL:/hfle/v2/{organizationId}/files/{configCode}/multipart
|
||||
String url = gatewayUrl + "/hfle/v2/" + organizationId + "/files/" + configCode + "/multipart";
|
||||
|
||||
// 避免重复文件覆盖,追加时间戳
|
||||
if (fileName.contains(".")) {
|
||||
String fileNameLeft = fileName.substring(0, fileName.lastIndexOf("."));
|
||||
fileName = fileNameLeft + "_" + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."));
|
||||
} else {
|
||||
fileName += "_" + System.currentTimeMillis();
|
||||
}
|
||||
String nowday = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||
// 最终 fileName = bizPath/yyyyMMdd/originalName_timestamp.ext
|
||||
final String finalFileName = DIR + bizPath + "/" + nowday + "/" + fileName;
|
||||
|
||||
return doUpload(stream, url, finalFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行实际上传请求,401 时刷新 token 重试一次
|
||||
* <p>fileName 为已处理好的完整路径,不再做二次处理</p>
|
||||
*/
|
||||
private static String doUpload(InputStream stream, String url, String fileName) {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
URI uri = new URIBuilder(url)
|
||||
.addParameter("directory", directory)
|
||||
.addParameter("fileName", fileName)
|
||||
.addParameter("docType", "0")
|
||||
.build();
|
||||
|
||||
HttpPost post = new HttpPost(uri);
|
||||
post.setConfig(buildRequestConfig());
|
||||
post.addHeader("Authorization", "Bearer " + getToken());
|
||||
post.addHeader("accept", "application/json;charset=utf-8");
|
||||
post.setEntity(MultipartEntityBuilder.create()
|
||||
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
|
||||
.setCharset(StandardCharsets.UTF_8)
|
||||
.addBinaryBody("file", stream, ContentType.APPLICATION_OCTET_STREAM, fileName)
|
||||
.build());
|
||||
|
||||
response = httpClient.execute(post);
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
|
||||
// 401 表示 token 过期,清除缓存后重试一次(fileName 已处理,不会重复拼接)
|
||||
if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
|
||||
log.warn("[XjOssUtil] 上传时 token 过期,重新鉴权后重试");
|
||||
closeQuietly(httpClient, response);
|
||||
redisClientUtil.delete(REDIS_PREFIX_NO_TTL, TOKEN_KEY);
|
||||
return doUpload(stream, url, fileName);
|
||||
}
|
||||
|
||||
if (statusCode != HttpStatus.SC_OK) {
|
||||
log.error("[XjOssUtil] 文件上传失败,HTTP状态码: {}", statusCode);
|
||||
return null;
|
||||
}
|
||||
|
||||
String fileUrl = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||
// 接口可能返回 HTTP 200 但 body 为业务错误 JSON,需单独判断
|
||||
JSONObject resultJson = null;
|
||||
try {
|
||||
resultJson = JSON.parseObject(fileUrl);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
if (resultJson != null && Boolean.TRUE.equals(resultJson.getBoolean("failed"))) {
|
||||
log.error("[XjOssUtil] 文件上传业务失败: {}", fileUrl);
|
||||
return null;
|
||||
}
|
||||
String fileKey = extractFileKey(fileUrl);
|
||||
log.info("[XjOssUtil] 文件上传成功,fileKey: {}", fileKey);
|
||||
return fileKey;
|
||||
} catch (Exception e) {
|
||||
log.error("[XjOssUtil] 文件上传异常", e);
|
||||
return null;
|
||||
} finally {
|
||||
closeQuietly(httpClient, response);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 文件下载 =====================
|
||||
|
||||
/**
|
||||
* 下载文件,返回文件输入流
|
||||
* <p>
|
||||
* 入参为短 fileKey,如 808fec572eda4428a84c8b4c4f07f511@aa.png
|
||||
* 内部自动拼接完整路径:{directory}0/{fileKey}
|
||||
* </p>
|
||||
*
|
||||
* @param fileKey 短文件 key,如 808fec572eda4428a84c8b4c4f07f511@aa.png
|
||||
* @return 文件输入流,调用方负责关闭;失败返回 null
|
||||
*/
|
||||
public static InputStream down(String fileKey) {
|
||||
String locationUrl = getLocationUrl(buildFullFileKey(fileKey));
|
||||
if (locationUrl == null) {
|
||||
return null;
|
||||
}
|
||||
CloseableHttpClient downloadClient = HttpClients.createDefault();
|
||||
try {
|
||||
HttpGet downloadGet = new HttpGet(locationUrl);
|
||||
downloadGet.setConfig(buildRequestConfig());
|
||||
CloseableHttpResponse downloadResponse = downloadClient.execute(downloadGet);
|
||||
int downloadStatus = downloadResponse.getStatusLine().getStatusCode();
|
||||
if (downloadStatus != HttpStatus.SC_OK) {
|
||||
log.error("[XjOssUtil] 获取文件流失败,HTTP状态码: {}", downloadStatus);
|
||||
closeQuietly(downloadClient, downloadResponse);
|
||||
return null;
|
||||
}
|
||||
// 返回包装流,关闭时自动释放 HTTP 资源
|
||||
return new HttpEntityInputStream(downloadResponse.getEntity().getContent(), downloadClient, downloadResponse);
|
||||
} catch (Exception e) {
|
||||
log.error("[XjOssUtil] 文件下载异常", e);
|
||||
closeQuietly(downloadClient, null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件预览地址(Location 重定向 URL)
|
||||
* <p>
|
||||
* 入参为短 fileKey,如 808fec572eda4428a84c8b4c4f07f511@aa.png
|
||||
* 内部自动拼接完整路径:{directory}0/{fileKey}
|
||||
* 返回的 URL 可直接用于浏览器预览或前端展示
|
||||
* </p>
|
||||
*
|
||||
* @param fileKey 短文件 key,如 808fec572eda4428a84c8b4c4f07f511@aa.png
|
||||
* @return 预览 URL,失败返回 null
|
||||
*/
|
||||
public static String show(String fileKey) {
|
||||
return getLocationUrl(buildFullFileKey(fileKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将短 fileKey 拼接为下载接口所需的完整路径
|
||||
* <p>规则:{directory}0/{fileKey},示例:scyx/scfz/aygc/jkgl/0/808fec...@aa.png</p>
|
||||
*/
|
||||
private static String buildFullFileKey(String fileKey) {
|
||||
String dir = directory.endsWith("/") ? directory : directory + "/";
|
||||
return dir + "0/" + fileKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用 download-by-key 接口,获取 302 重定向的 Location URL
|
||||
* <p>401 时自动刷新 token 重试一次</p>
|
||||
*
|
||||
* @param fullFileKey 完整 fileKey,如 scyx/scfz/aygc/jkgl/0/808fec...@aa.png
|
||||
* @return Location URL,失败返回 null
|
||||
*/
|
||||
private static String getLocationUrl(String fullFileKey) {
|
||||
String url = gatewayUrl + "/hfle/v2/" + organizationId + "/files/" + configCode + "/download-by-key";
|
||||
CloseableHttpClient noRedirectClient = org.apache.http.impl.client.HttpClientBuilder.create()
|
||||
.disableRedirectHandling()
|
||||
.build();
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
URI uri = new URIBuilder(url)
|
||||
.addParameter("fileKey", fullFileKey)
|
||||
.build();
|
||||
HttpGet get = new HttpGet(uri);
|
||||
get.setConfig(buildRequestConfig());
|
||||
String token = getToken();
|
||||
get.addHeader("Authorization", "Bearer " + token);
|
||||
|
||||
// 打印等效 curl 命令,便于调试
|
||||
log.info("[XjOssUtil] curl --request GET \\\n --url '{}' \\\n --header 'Authorization: Bearer {}'",
|
||||
uri.toString(), token);
|
||||
|
||||
response = noRedirectClient.execute(get);
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
|
||||
// 401 表示 token 过期,重新鉴权后重试一次
|
||||
if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
|
||||
log.warn("[XjOssUtil] 获取 Location 时 token 过期,重新鉴权后重试");
|
||||
closeQuietly(noRedirectClient, response);
|
||||
redisClientUtil.delete(REDIS_PREFIX_NO_TTL, TOKEN_KEY);
|
||||
return getLocationUrl(fullFileKey);
|
||||
}
|
||||
|
||||
if (statusCode != HttpStatus.SC_MOVED_TEMPORARILY && statusCode != HttpStatus.SC_SEE_OTHER) {
|
||||
log.error("[XjOssUtil] download-by-key 返回非预期状态码: {}", statusCode);
|
||||
return null;
|
||||
}
|
||||
|
||||
Header locationHeader = response.getFirstHeader("Location");
|
||||
if (locationHeader == null) {
|
||||
log.error("[XjOssUtil] download-by-key 未返回 Location 响应头");
|
||||
return null;
|
||||
}
|
||||
String locationUrl = locationHeader.getValue();
|
||||
log.info("[XjOssUtil] 获取到 Location: {}", locationUrl);
|
||||
return locationUrl;
|
||||
} catch (Exception e) {
|
||||
log.error("[XjOssUtil] 获取 Location 异常", e);
|
||||
return null;
|
||||
} finally {
|
||||
closeQuietly(noRedirectClient, response);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 私有工具方法 =====================
|
||||
|
||||
/**
|
||||
* 文件合法性校验(文件类型白名单)
|
||||
* <p>
|
||||
* 白名单为空时跳过校验;文件名无扩展名或扩展名不在白名单中时抛出异常。
|
||||
* </p>
|
||||
*
|
||||
* @param fileName 文件名
|
||||
*/
|
||||
private static void checkFileLegitimacy(String fileName) {
|
||||
if (StrUtil.isNotEmpty(fileTypeWhiteList)) {
|
||||
String extName = FileNameUtil.extName(fileName);
|
||||
if (StrUtil.isBlank(extName)) {
|
||||
ExceptionAssertsUtil.fail("文件上传失败:不能识别的文件类型!");
|
||||
}
|
||||
List<String> fileTypeList = Arrays.asList(fileTypeWhiteList.split(","));
|
||||
// 统一转小写比对,忽略大小写差异
|
||||
if (!fileTypeList.contains(extName.toLowerCase())) {
|
||||
ExceptionAssertsUtil.fail("文件上传失败:不支持的文件类型!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从上传接口返回的完整 URL 中截取 fileKey
|
||||
* <p>
|
||||
* 返回示例:https://wbwj.http://11.71.10.44:8060/scyx/.../0/808fec572eda4428a84c8b4c4f07f511@aa.png
|
||||
* 截取结果:808fec572eda4428a84c8b4c4f07f511@aa.png
|
||||
* 规则:取 @ 符号前最后一个 / 之后的所有内容
|
||||
* </p>
|
||||
*
|
||||
* @param fileUrl 接口返回的完整 URL
|
||||
* @return fileKey,解析失败时返回原始 URL
|
||||
*/
|
||||
private static String extractFileKey(String fileUrl) {
|
||||
int atIndex = fileUrl.indexOf("@");
|
||||
if (atIndex < 0) {
|
||||
return fileUrl;
|
||||
}
|
||||
int slashIndex = fileUrl.lastIndexOf("/", atIndex);
|
||||
if (slashIndex < 0) {
|
||||
return fileUrl;
|
||||
}
|
||||
return fileUrl.substring(slashIndex + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建统一的 HTTP 请求配置
|
||||
*/
|
||||
private static RequestConfig buildRequestConfig() {
|
||||
return RequestConfig.custom()
|
||||
.setConnectTimeout(CONNECT_TIMEOUT)
|
||||
.setSocketTimeout(SOCKET_TIMEOUT)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 静默关闭 HTTP 资源,忽略异常
|
||||
*/
|
||||
private static void closeQuietly(CloseableHttpClient client, CloseableHttpResponse response) {
|
||||
try {
|
||||
if (response != null) {
|
||||
response.close();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
try {
|
||||
if (client != null) {
|
||||
client.close();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 内部类:包装 InputStream,关闭时释放 HTTP 资源 =====================
|
||||
|
||||
/**
|
||||
* 包装 HTTP 响应的 InputStream,关闭时自动释放 HTTP 连接资源
|
||||
*/
|
||||
private static class HttpEntityInputStream extends InputStream {
|
||||
|
||||
private final InputStream delegate;
|
||||
private final CloseableHttpClient httpClient;
|
||||
private final CloseableHttpResponse httpResponse;
|
||||
|
||||
HttpEntityInputStream(InputStream delegate, CloseableHttpClient httpClient, CloseableHttpResponse httpResponse) {
|
||||
this.delegate = delegate;
|
||||
this.httpClient = httpClient;
|
||||
this.httpResponse = httpResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return delegate.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return delegate.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
delegate.close();
|
||||
} finally {
|
||||
closeQuietly(httpClient, httpResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package org.jeecg.config.oss;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.util.MyUploadUtil;
|
||||
import org.jeecg.util.GlobalManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -29,6 +31,9 @@ public class MyUploadConfig {
|
||||
@Value(value = "${jeecg.myupload.mu_file_type_white_list}")
|
||||
private String fileTypeWhiteList;
|
||||
|
||||
@Autowired
|
||||
private GlobalManager globalManager;
|
||||
|
||||
@Bean
|
||||
public void initMu() {
|
||||
MyUploadUtil.setMuUrl(muUrl);
|
||||
@@ -39,6 +44,7 @@ public class MyUploadConfig {
|
||||
MyUploadUtil.setMuFileListUploadPath(muFileListUploadPath);
|
||||
MyUploadUtil.setMuDownPath(muDownPath);
|
||||
MyUploadUtil.setFileTypeWhiteList(fileTypeWhiteList);
|
||||
MyUploadUtil.setGlobalManager(globalManager);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.jeecg.config.oss;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.util.XjOssUtil;
|
||||
import org.jeecg.util.RedisClientUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 新疆油田平台(IOSP)OSS 配置类
|
||||
* <p>
|
||||
* 从 Nacos 读取配置并注入到 XjOssUtil 静态字段中。
|
||||
* 对应 Nacos 配置前缀:xj.oss
|
||||
* </p>
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class XjOssConfig {
|
||||
|
||||
/** 网关地址,如 http://api.iosp.ydpt.tech */
|
||||
@Value(value = "${variable.anyan.host}")
|
||||
private String gatewayUrl;
|
||||
|
||||
/** OAuth2 客户端ID */
|
||||
@Value(value = "${variable.anyan.client-id}")
|
||||
private String clientId;
|
||||
|
||||
/** OAuth2 客户端密钥 */
|
||||
@Value(value = "${variable.anyan.client-secret}")
|
||||
private String clientSecret;
|
||||
|
||||
/** 组织ID(上传/下载路径参数) */
|
||||
@Value(value = "${variable.anyan.organization-id}")
|
||||
private String organizationId;
|
||||
|
||||
/** 配置Code(上传/下载路径参数) */
|
||||
@Value(value = "${variable.anyan.config-code}")
|
||||
private String configCode;
|
||||
|
||||
/**
|
||||
* 文件基础路径前缀(可选)
|
||||
* 示例:http://11.71.10.44:8060/wbwj/scyx/scfz/aygc/jkgl/0
|
||||
* 用于从完整 URL 中截取相对 fileKey
|
||||
*/
|
||||
@Value(value = "${variable.anyan.base-url-prefix:}")
|
||||
private String baseUrlPrefix;
|
||||
|
||||
/**
|
||||
* 允许上传的文件类型白名单,逗号分隔(可选)
|
||||
* 示例:jpg,jpeg,png,pdf,doc,docx
|
||||
* 为空时不做类型限制
|
||||
*/
|
||||
@Value(value = "${variable.anyan.file-type-white-list:}")
|
||||
private String fileTypeWhiteList;
|
||||
|
||||
/**
|
||||
* 授权上传目录,需与 IOSP 平台授权的目录一致
|
||||
* 示例:scyx/scfz/aygc/jkgl/
|
||||
*/
|
||||
@Value(value = "${variable.anyan.directory}")
|
||||
private String directory;
|
||||
|
||||
@Autowired
|
||||
private RedisClientUtil redisClientUtil;
|
||||
|
||||
/**
|
||||
* 初始化 XjOssUtil 静态配置
|
||||
*/
|
||||
@Bean
|
||||
public void initXjOss() {
|
||||
XjOssUtil.setGatewayUrl(gatewayUrl);
|
||||
XjOssUtil.setClientId(clientId);
|
||||
XjOssUtil.setClientSecret(clientSecret);
|
||||
XjOssUtil.setOrganizationId(organizationId);
|
||||
XjOssUtil.setConfigCode(configCode);
|
||||
XjOssUtil.setBaseUrlPrefix(baseUrlPrefix);
|
||||
XjOssUtil.setFileTypeWhiteList(fileTypeWhiteList);
|
||||
XjOssUtil.setDirectory(directory);
|
||||
XjOssUtil.setRedisClientUtil(redisClientUtil);
|
||||
log.info("[XjOssConfig] IOSP OSS 配置初始化完成,网关地址: {}", gatewayUrl);
|
||||
}
|
||||
}
|
||||
@@ -185,6 +185,8 @@ public class ShiroConfig {
|
||||
|
||||
filterChainDefinitionMap.put("/version/getSysVersionDetailByPackageName", "anon");
|
||||
filterChainDefinitionMap.put("/sys/api/notice/selectIosUpdate", "anon");
|
||||
// filterChainDefinitionMap.put("/sys/upload/down/**", "anon");
|
||||
// filterChainDefinitionMap.put("/sys/upload/show/**", "anon");
|
||||
|
||||
// 添加自己的过滤器并且取名为jwt
|
||||
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
|
||||
|
||||
+2
@@ -2,6 +2,7 @@ package org.jeecg.modules.system.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -124,6 +125,7 @@ public class HealthUserEmployeeExController extends JeecgController<HealthUserEm
|
||||
HttpServletRequest req) {
|
||||
|
||||
Page<UserEmployee> page = new Page<>(pageNo, pageSize);
|
||||
userFilter.setRealname(HtmlUtil.unescape(userFilter.getRealname()));
|
||||
IPage<UserEmployee> pageList = healthUserEmployeeExService.selectHealthUserEmployeeExList(page, userFilter, idStatus);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
+100
@@ -1,10 +1,12 @@
|
||||
package org.jeecg.modules.system.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.util.MyUploadUtil;
|
||||
import org.jeecg.common.util.XjOssUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.oss.service.IOssFileService;
|
||||
import org.jeecg.modules.system.bean.request.UploadFilsDTO;
|
||||
@@ -15,6 +17,11 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -89,6 +96,9 @@ public class SysUploadController {
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
if (StrUtil.isBlank(fileUrl)){
|
||||
return Result.error("上传失败,请检查配置信息是否正确!");
|
||||
}
|
||||
Map map = new HashMap();
|
||||
map.put("url", fileUrl);
|
||||
return Result.OK(map);
|
||||
@@ -115,4 +125,94 @@ public class SysUploadController {
|
||||
return Result.OK(urlList);
|
||||
}
|
||||
|
||||
@Operation(summary = "IOSP文件下载", description = "根据 fileKey 下载文件,如 sys/upload/down/808fec...@aa.png")
|
||||
@RequestMapping(value = "/down/**", method = RequestMethod.GET)
|
||||
public void fileDown(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String fileKey = extractPathParam(request, "/down/");
|
||||
if (isIllegalFileKey(fileKey)) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "fileKey 格式非法");
|
||||
return;
|
||||
}
|
||||
try (InputStream in = XjOssUtil.down(fileKey)) {
|
||||
if (in == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在或下载失败");
|
||||
return;
|
||||
}
|
||||
// 从 fileKey 中取文件名(@ 后面的部分)
|
||||
String fileName = fileKey.contains("@") ? fileKey.substring(fileKey.indexOf("@") + 1) : fileKey;
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
|
||||
try (OutputStream out = response.getOutputStream()) {
|
||||
byte[] buf = new byte[4096];
|
||||
int len;
|
||||
while ((len = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[SysUploadController] IOSP文件下载失败, fileKey={}", fileKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "IOSP文件预览", description = "根据 fileKey 获取预览地址,如 sys/upload/show/808fec...@aa.png")
|
||||
@RequestMapping(value = "/show/**", method = RequestMethod.GET)
|
||||
public void fileShow(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String fileKey = extractPathParam(request, "/show/");
|
||||
if (isIllegalFileKey(fileKey)) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "fileKey 格式非法");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String url = XjOssUtil.show(fileKey);
|
||||
if (url == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在或获取预览地址失败");
|
||||
return;
|
||||
}
|
||||
// 重定向到真实预览地址
|
||||
response.sendRedirect(url);
|
||||
} catch (Exception e) {
|
||||
log.error("[SysUploadController] IOSP文件预览失败, fileKey={}", fileKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求路径中提取 fileKey
|
||||
* <p>截取 marker 之后的路径部分,并做 URL 解码</p>
|
||||
*/
|
||||
private String extractPathParam(HttpServletRequest request, String marker) {
|
||||
String uri = request.getRequestURI();
|
||||
int idx = uri.indexOf(marker);
|
||||
if (idx < 0) {
|
||||
return "";
|
||||
}
|
||||
String raw = uri.substring(idx + marker.length());
|
||||
try {
|
||||
return java.net.URLDecoder.decode(raw, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 fileKey 是否存在路径穿透风险
|
||||
* <p>
|
||||
* 拦截 ../、..\ 及其 URL 编码变体(%2e%2e%2f 等),防止跳出授权目录
|
||||
* </p>
|
||||
*/
|
||||
private boolean isIllegalFileKey(String fileKey) {
|
||||
if (oConvertUtils.isEmpty(fileKey)) {
|
||||
return true;
|
||||
}
|
||||
// 先做 URL 解码,防止编码绕过
|
||||
String decoded;
|
||||
try {
|
||||
decoded = java.net.URLDecoder.decode(fileKey, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
return true;
|
||||
}
|
||||
return decoded.contains("../") || decoded.contains("..\\")
|
||||
|| decoded.startsWith("..") || decoded.contains("/..");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
@@ -2,6 +2,7 @@ package org.jeecg.modules.system.controller;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
@@ -460,6 +461,9 @@ public class SysUserController {
|
||||
@RequestMapping(value = "/listAll", method = RequestMethod.GET)
|
||||
public Result<IPage<SysUser>> queryAllPageList(SysUser user, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
|
||||
if(StrUtil.isNotBlank(user.getRealname())){
|
||||
user.setRealname(HtmlUtil.unescape(user.getRealname()));
|
||||
}
|
||||
QueryWrapper<SysUser> queryWrapper = QueryGenerator.initQueryWrapper(user, req.getParameterMap());
|
||||
return sysUserService.queryPageList(req, queryWrapper, pageSize, pageNo);
|
||||
}
|
||||
|
||||
+5
@@ -1,6 +1,8 @@
|
||||
package org.jeecg.modules.system.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
@@ -77,6 +79,9 @@ public class SysUserDepartChangeApplyController extends JeecgController<SysUserD
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||
Page<ChangeDepartVo> page = new Page<>(pageNo, pageSize);
|
||||
if(StrUtil.isNotBlank(userName)){
|
||||
userName = HtmlUtil.unescape(userName);
|
||||
}
|
||||
IPage<ChangeDepartVo> pageList = sysUserDepartChangeApplyService.pageCustom(page, userName, status,workNo, userId);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
+5
@@ -1,5 +1,6 @@
|
||||
package org.jeecg.modules.system.entity;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
@@ -11,6 +12,10 @@ import lombok.Data;
|
||||
public class SelectWrapperDTO {
|
||||
private String orgCode;
|
||||
private String realName;
|
||||
|
||||
public void setRealName(String realName) {
|
||||
this.realName = realName != null ? HtmlUtil.unescape(realName) : null;
|
||||
}
|
||||
private String workNo;
|
||||
private String sex;
|
||||
private String code;
|
||||
|
||||
+8
-2
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
@@ -192,8 +193,10 @@ public class HealthUserEmployeeExServiceImpl extends ServiceImpl<HealthUserEmplo
|
||||
}
|
||||
List<String> orgCodes = GlobalUtils.getManagedCodes(filter.getOrgCode());
|
||||
filter.setOrgCodes(orgCodes);
|
||||
// 20260330 · 名字中包含·需要转义回·
|
||||
filter.setRealname(filter.getRealname().replaceAll("·", "·"));
|
||||
// 名字中包含·(中文间隔号)时,前端可能传入 HTML 实体 ·,需转义回原字符
|
||||
if (StrUtil.isNotBlank(filter.getRealname())) {
|
||||
filter.setRealname(filter.getRealname().replaceAll("·", "·"));
|
||||
}
|
||||
IPage<UserEmployee> userEmployeeIPage = healthUserEmployeeExMapper.queryUserEmployeeList(page, filter);
|
||||
if (CollectionUtils.isEmpty(userEmployeeIPage.getRecords())) {
|
||||
return userEmployeeIPage;
|
||||
@@ -776,6 +779,9 @@ public class HealthUserEmployeeExServiceImpl extends ServiceImpl<HealthUserEmplo
|
||||
|
||||
@Override
|
||||
public IPage<UserEmployee> selectUserListByWatch(Page page, UserFilter filter) {
|
||||
if(StrUtil.isNotBlank(filter.getRealname())){
|
||||
filter.setRealname(HtmlUtil.unescape(filter.getRealname()));
|
||||
}
|
||||
List<String> bindUserIdList = watchApi.getBindUserIdList();
|
||||
if (!CollectionUtils.isEmpty(bindUserIdList)) {
|
||||
filter.setIdList(bindUserIdList);
|
||||
|
||||
+4
@@ -8,6 +8,7 @@ import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.IdcardUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
@@ -262,6 +263,9 @@ public class SysBaseApiImpl implements ISysBaseAPI {
|
||||
|
||||
@Override
|
||||
public List<String> getUserIdsByDepartAndUsername(String departCode, String username, String idCard, String workNo) {
|
||||
if(StrUtil.isNotBlank(username)){
|
||||
username = HtmlUtil.unescape(username);
|
||||
}
|
||||
if (StrUtil.isAllBlank(departCode, username, idCard, workNo)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
+2
@@ -6,6 +6,7 @@ import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.extra.pinyin.PinyinUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
@@ -2875,6 +2876,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
queryWrapper.eq(SysUser::getStatus, 1);
|
||||
queryWrapper.eq(SysUser::getPersonType, 1);
|
||||
if (StrUtil.isNotEmpty(name)) {
|
||||
name = HtmlUtil.unescape(name);
|
||||
queryWrapper.like(SysUser::getRealname, name);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(orgCode)) {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!--<plugin>
|
||||
<groupId>com.google.cloud.tools</groupId>
|
||||
<artifactId>jib-maven-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
@@ -87,7 +87,7 @@
|
||||
</container>
|
||||
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugin>-->
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
Reference in New Issue
Block a user