Commit 8c9326c5 by tntxia Committed by gdj

增加操作的日志,到操作日志表

parent 3a302e23
......@@ -55,4 +55,9 @@ public class PaginationData<T> {
this.pagination = pagination;
return this;
}
public static <T> PaginationData<T> of(List<T> list, long total, int pageNum, int pageSize) {
return new PaginationData<>(list, new Pagination(pageNum, pageSize, total));
}
}
package com.dji.sample.common.util;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
/**
* @author chenshixian
**/
public class DateUtil {
public static LocalDate getLocalDateFromDateStr(String date) {
DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDate.parse(date, dayFormatter);
}
public static long getEpochSecondFromDateStr(String date) {
LocalDate localDate = getLocalDateFromDateStr(date);
return getEpochSecondFromLocalDate(localDate);
}
public static long getEpochSecondFromLocalDate(LocalDate date) {
return date.toEpochSecond(LocalTime.of(0, 0, 0), ZoneOffset.UTC);
}
public static long getTimeStampFromDateStr(String date) {
return getEpochSecondFromDateStr(date) * 1000;
}
public static long getTimeStampFromDateStrNext(String date) {
LocalDate localDate = getLocalDateFromDateStr(date);
localDate = localDate.plusDays(1);
return getEpochSecondFromLocalDate(localDate) * 1000;
}
public static String getNowStr() {
return String.valueOf(new Date().getTime());
}
}
......@@ -4,11 +4,14 @@ import com.dji.sample.control.model.enums.DroneAuthorityEnum;
import com.dji.sample.control.model.enums.RemoteDebugMethodEnum;
import com.dji.sample.control.model.param.*;
import com.dji.sample.control.service.IControlService;
import com.dji.sample.manage.model.enums.OperateRecordTypeEnum;
import com.dji.sample.manage.service.IOperateRecordService;
import com.dji.sdk.common.HttpResultResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
/**
......@@ -24,48 +27,85 @@ public class DockController {
@Autowired
private IControlService controlService;
@Autowired
private IOperateRecordService operateRecordService;
@PostMapping("/{sn}/jobs/{service_identifier}")
public HttpResultResponse createControlJob(@PathVariable String sn,
public HttpResultResponse createControlJob(
HttpServletRequest request,
@PathVariable String sn,
@PathVariable("service_identifier") String serviceIdentifier,
@Valid @RequestBody(required = false) RemoteDebugParam param) {
return controlService.controlDockDebug(sn, RemoteDebugMethodEnum.find(serviceIdentifier), param);
HttpResultResponse result = controlService.controlDockDebug(sn, RemoteDebugMethodEnum.find(serviceIdentifier), param);
OperateRecordTypeEnum type = resolveDebugType(serviceIdentifier);
operateRecordService.record(request, type, sn, param,
type.getDescription() + " [" + serviceIdentifier + "]");
return result;
}
@PostMapping("/{sn}/jobs/fly-to-point")
public HttpResultResponse flyToPoint(@PathVariable String sn, @Valid @RequestBody FlyToPointParam param) {
return controlService.flyToPoint(sn, param);
public HttpResultResponse flyToPoint(HttpServletRequest request,@PathVariable String sn, @Valid @RequestBody FlyToPointParam param) {
HttpResultResponse result = controlService.flyToPoint(sn, param);
operateRecordService.record(request, OperateRecordTypeEnum.FLY_TO_POINT, sn, param);
return result;
}
@DeleteMapping("/{sn}/jobs/fly-to-point")
public HttpResultResponse flyToPointStop(@PathVariable String sn) {
return controlService.flyToPointStop(sn);
public HttpResultResponse flyToPointStop(HttpServletRequest request, @PathVariable String sn) {
HttpResultResponse result = controlService.flyToPointStop(sn);
operateRecordService.record(request, OperateRecordTypeEnum.FLY_TO_POINT_STOP, sn, null);
return result;
}
@PostMapping("/{sn}/jobs/takeoff-to-point")
public HttpResultResponse takeoffToPoint(@PathVariable String sn, @Valid @RequestBody TakeoffToPointParam param) {
return controlService.takeoffToPoint(sn, param);
public HttpResultResponse takeoffToPoint(HttpServletRequest request,@PathVariable String sn, @Valid @RequestBody TakeoffToPointParam param) {
HttpResultResponse result = controlService.takeoffToPoint(sn, param);
operateRecordService.record(request, OperateRecordTypeEnum.TAKEOFF_TO_POINT, sn, param);
return result;
}
@PostMapping("/{sn}/authority/flight")
public HttpResultResponse seizeFlightAuthority(@PathVariable String sn) {
return controlService.seizeAuthority(sn, DroneAuthorityEnum.FLIGHT, null);
public HttpResultResponse seizeFlightAuthority(HttpServletRequest request, @PathVariable String sn) {
HttpResultResponse result = controlService.seizeAuthority(sn, DroneAuthorityEnum.FLIGHT, null);
operateRecordService.record(request, OperateRecordTypeEnum.SEIZE_FLIGHT_AUTHORITY, sn, null);
return result;
}
@PostMapping("/{sn}/authority/payload")
public HttpResultResponse seizePayloadAuthority(@PathVariable String sn, @Valid @RequestBody DronePayloadParam param) {
return controlService.seizeAuthority(sn, DroneAuthorityEnum.PAYLOAD, param);
public HttpResultResponse seizePayloadAuthority(HttpServletRequest request,@PathVariable String sn, @Valid @RequestBody DronePayloadParam param) {
HttpResultResponse result = controlService.seizeAuthority(sn, DroneAuthorityEnum.PAYLOAD, param);
operateRecordService.record(request, OperateRecordTypeEnum.SEIZE_PAYLOAD_AUTHORITY, sn, param);
return result;
}
@PostMapping("/{sn}/payload/commands")
public HttpResultResponse payloadCommands(@PathVariable String sn, @Valid @RequestBody PayloadCommandsParam param) throws Exception {
public HttpResultResponse payloadCommands(HttpServletRequest request,@PathVariable String sn, @Valid @RequestBody PayloadCommandsParam param) throws Exception {
param.setSn(sn);
return controlService.payloadCommands(param);
HttpResultResponse result = controlService.payloadCommands(param);
operateRecordService.record(request, OperateRecordTypeEnum.PAYLOAD_COMMANDS, sn, param);
return result;
}
@PostMapping("/{sn}/jobs/rtkCalibration")
public HttpResultResponse createControlJob(@PathVariable String sn,
public HttpResultResponse createControlJob(HttpServletRequest request,@PathVariable String sn,
@Valid @RequestBody RtkCalibrationParam param) {
return controlService.rtkCalibration(sn, param);
HttpResultResponse result = controlService.rtkCalibration(sn, param);
operateRecordService.record(request, OperateRecordTypeEnum.RTK_CALIBRATION, sn, param);
return result;
}
private OperateRecordTypeEnum resolveDebugType(String serviceIdentifier) {
if ("return_home".equalsIgnoreCase(serviceIdentifier)) {
return OperateRecordTypeEnum.RETURN_HOME;
}
if ("return_home_cancel".equalsIgnoreCase(serviceIdentifier)) {
return OperateRecordTypeEnum.RETURN_HOME_CANCEL;
}
return OperateRecordTypeEnum.DOCK_DEBUG;
}
}
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.OperateRecordEntity;
public interface IOperateRecordMapper extends BaseMapper<OperateRecordEntity> {
}
package com.dji.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
@Data
public class OperateRecordDTO implements Serializable {
private Integer id;
/**
* Client IP address.
*/
private String ip;
private String workspaceId;
@JsonProperty("user_id")
private String userId;
@JsonProperty("user_name")
private String userName;
/**
* Unix timestamp (ms) when the operation was performed.
*/
@JsonProperty("operating_time")
private Long operatingTime;
/**
* Operation type identifier, e.g. FLIGHT_TASK_CREATE, RETURN_HOME, TAKEOFF_TO_POINT.
*
* @see com.dji.sample.manage.model.enums.OperateRecordTypeEnum
*/
@JsonProperty("operating_type")
private String operatingType;
/**
* JSON-serialized request parameters.
*/
@JsonProperty("operating_param")
private String operatingParam;
/**
* Human-readable description of the operation.
*/
@JsonProperty("operating_content")
private String operatingContent;
/**
* Dock SN involved in the operation.
*/
@JsonProperty("dock_sn")
private String dockSn;
}
package com.dji.sample.manage.model.enums;
import lombok.Getter;
/**
* Enumeration of all user-triggered operations recorded in {@code operate_record}.
*/
@Getter
public enum OperateRecordTypeEnum {
// ---- Flight task (wayline job) ----
FLIGHT_TASK_CREATE("FLIGHT_TASK_CREATE", "创建飞行任务"),
FLIGHT_TASK_CANCEL("FLIGHT_TASK_CANCEL", "取消飞行任务"),
FLIGHT_TASK_UPDATE("FLIGHT_TASK_UPDATE", "更新飞行任务状态"),
IN_FLIGHT_TASK_CREATE("IN_FLIGHT_TASK_CREATE", "空中下发航线"),
IN_FLIGHT_TASK_CANCEL("IN_FLIGHT_TASK_CANCEL", "取消空中航线"),
// ---- One-key takeoff / direct control ----
TAKEOFF_TO_POINT("TAKEOFF_TO_POINT", "一键起飞"),
FLY_TO_POINT("FLY_TO_POINT", "飞向目标点"),
FLY_TO_POINT_STOP("FLY_TO_POINT_STOP", "停止飞向目标点"),
// ---- Return home ----
RETURN_HOME("RETURN_HOME", "强制返航"),
RETURN_HOME_CANCEL("RETURN_HOME_CANCEL", "取消返航"),
// ---- Dock debug / remote control ----
DOCK_DEBUG("DOCK_DEBUG", "机场调试指令"),
SEIZE_FLIGHT_AUTHORITY("SEIZE_FLIGHT_AUTHORITY", "夺取飞行控制权"),
SEIZE_PAYLOAD_AUTHORITY("SEIZE_PAYLOAD_AUTHORITY", "夺取负载控制权"),
PAYLOAD_COMMANDS("PAYLOAD_COMMANDS", "负载控制指令"),
RTK_CALIBRATION("RTK_CALIBRATION", "RTK标定");
private final String type;
private final String description;
OperateRecordTypeEnum(String type, String description) {
this.type = type;
this.description = description;
}
}
package com.dji.sample.manage.service;
import com.dji.sample.manage.model.dto.OperateRecordDTO;
import com.dji.sample.manage.model.enums.OperateRecordTypeEnum;
import com.dji.sdk.common.PaginationData;
import javax.servlet.http.HttpServletRequest;
/**
* Service for recording user-triggered operations into the {@code operate_record} table.
*/
public interface IOperateRecordService {
/**
* Record an operation.
*
* @param request HTTP request (used to extract IP and token claims)
* @param type operation type
* @param dockSn dock serial number involved (may be null)
* @param param JSON-serializable request parameter object (may be null)
* @param content human-readable description override; if null, uses enum description
*/
void record(HttpServletRequest request,
OperateRecordTypeEnum type,
String dockSn,
Object param,
String content);
/**
* Convenience overload — uses the enum's built-in description.
*/
void record(HttpServletRequest request,
OperateRecordTypeEnum type,
String dockSn,
Object param);
/**
* Paginated query of operation records.
*
* @param workspaceId filter by workspace (required)
* @param userId filter by user ID (null = all users, for admins only)
* @param dockSn filter by dock SN (optional)
* @param operatingType filter by operation type (optional)
* @param startTime filter by start time in ms (optional)
* @param endTime filter by end time in ms (optional)
* @param page page number (1-based)
* @param pageSize page size
*/
PaginationData<OperateRecordDTO> page(String workspaceId,
String userId,
String dockSn,
String operatingType,
Long startTime,
Long endTime,
int page,
int pageSize);
}
package com.dji.sample.manage.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.manage.dao.IOperateRecordMapper;
import com.dji.sample.manage.dao.IUserMapper;
import com.dji.sample.manage.model.dto.OperateRecordDTO;
import com.dji.sample.manage.model.entity.OperateRecordEntity;
import com.dji.sample.manage.model.entity.UserEntity;
import com.dji.sample.manage.model.enums.OperateRecordTypeEnum;
import com.dji.sample.manage.service.IOperateRecordService;
import com.dji.sdk.common.PaginationData;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
/**
* Records user-triggered operations (flight task creation, takeoff, return home, etc.)
* into the {@code operate_record} table.
*/
@Slf4j
@Service
public class OperateRecordServiceImpl implements IOperateRecordService {
@Autowired
private IOperateRecordMapper operateRecordMapper;
@Autowired
private IUserMapper userMapper;
@Autowired
private ObjectMapper objectMapper;
@Override
public void record(HttpServletRequest request,
OperateRecordTypeEnum type,
String dockSn,
Object param,
String content) {
try {
CustomClaim claim = (CustomClaim) request.getAttribute(TOKEN_CLAIM);
String userId = claim != null ? claim.getId() : null;
String workspaceId = claim != null ? claim.getWorkspaceId() : null;
String ip = getClientIp(request);
String paramJson = null;
if (param != null) {
try {
paramJson = objectMapper.writeValueAsString(param);
} catch (JsonProcessingException e) {
paramJson = param.toString();
}
}
String operatingContent = (content != null && !content.isBlank())
? content : type.getDescription();
OperateRecordEntity entity = OperateRecordEntity.builder()
.ip(ip)
.workspaceId(workspaceId)
.userId(userId)
.operatingTime(System.currentTimeMillis())
.operatingType(type.getType())
.operatingParam(paramJson)
.operatingContent(operatingContent)
.dockSn(dockSn)
.build();
operateRecordMapper.insert(entity);
} catch (Exception e) {
// Logging failure must never affect the main business flow
log.error("[OperateRecord] Failed to record operation [{}]: {}", type, e.getMessage(), e);
}
}
@Override
public void record(HttpServletRequest request,
OperateRecordTypeEnum type,
String dockSn,
Object param) {
record(request, type, dockSn, param, null);
}
private String getUserNameByUserId(String userId) {
LambdaQueryWrapper<UserEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserEntity::getUserId, userId);
UserEntity userEntity = userMapper.selectOne(queryWrapper);
if (userEntity == null) {
return null;
}
return userEntity.getUsername();
}
private OperateRecordDTO entity2DTO(OperateRecordEntity entity) {
OperateRecordDTO recordDTO = BeanUtil.copyProperties(entity, OperateRecordDTO.class);
String userName = getUserNameByUserId(entity.getUserId());
recordDTO.setUserName(userName);
return recordDTO;
}
private List<OperateRecordDTO> entity2DTO(List<OperateRecordEntity> entityList) {
return entityList.stream().map(this::entity2DTO).collect(Collectors.toList());
}
@Override
public PaginationData<OperateRecordDTO> page(String workspaceId,
String userId,
String dockSn,
String operatingType,
Long startTime,
Long endTime,
int page,
int pageSize) {
LambdaQueryWrapper<OperateRecordEntity> wrapper = new LambdaQueryWrapper<OperateRecordEntity>()
.eq(OperateRecordEntity::getWorkspaceId, workspaceId)
.eq(StringUtils.isNotBlank(userId), OperateRecordEntity::getUserId, userId)
.eq(StringUtils.isNotBlank(dockSn), OperateRecordEntity::getDockSn, dockSn)
.eq(StringUtils.isNotBlank(operatingType), OperateRecordEntity::getOperatingType, operatingType)
.ge(startTime != null, OperateRecordEntity::getOperatingTime, startTime)
.lt(endTime != null, OperateRecordEntity::getOperatingTime, endTime)
.orderByDesc(OperateRecordEntity::getOperatingTime);
Page<OperateRecordEntity> result = operateRecordMapper.selectPage(new Page<>(page, pageSize), wrapper);
List<OperateRecordEntity> entityList = result.getRecords();
List<OperateRecordDTO> dtoList = entity2DTO(entityList);
return PaginationData.of(dtoList, result.getTotal(), page, pageSize);
}
// -------------------------------------------------------------------------
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip != null && !ip.isBlank() && !"unknown".equalsIgnoreCase(ip)) {
// X-Forwarded-For may contain multiple IPs; take the first one
int comma = ip.indexOf(',');
return comma > 0 ? ip.substring(0, comma).trim() : ip.trim();
}
ip = request.getHeader("X-Real-IP");
if (ip != null && !ip.isBlank() && !"unknown".equalsIgnoreCase(ip)) {
return ip.trim();
}
return request.getRemoteAddr();
}
}
package com.dji.sample.wayline.controller;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.manage.model.enums.OperateRecordTypeEnum;
import com.dji.sample.manage.service.IOperateRecordService;
import com.dji.sample.wayline.model.dto.TaskStatisticDTO;
import com.dji.sample.wayline.model.dto.TaskStatusStatisticDTO;
import com.dji.sample.wayline.model.dto.WaylineJobDTO;
......@@ -41,6 +43,9 @@ public class WaylineJobController {
@Autowired
private IFlightTaskService flighttaskService;
@Autowired
private IOperateRecordService operateRecordService;
/**
* Create a wayline task for the Dock.
* @param request
......@@ -55,6 +60,8 @@ public class WaylineJobController {
CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
customClaim.setWorkspaceId(workspaceId);
operateRecordService.record(request, OperateRecordTypeEnum.FLIGHT_TASK_CREATE, param.getDockSn(), param);
return flighttaskService.publishFlightTask(param, customClaim);
}
......@@ -83,7 +90,16 @@ public class WaylineJobController {
*/
@DeleteMapping("/{workspace_id}/jobs")
public HttpResultResponse publishCancelJob(@RequestParam(name = "job_id") Set<String> jobIds,
@PathVariable(name = "workspace_id") String workspaceId) throws SQLException {
@PathVariable(name = "workspace_id") String workspaceId, HttpServletRequest request) throws SQLException {
for(String jobId : jobIds) {
operateRecordService.record(request, OperateRecordTypeEnum.FLIGHT_TASK_CANCEL, null,
java.util.Map.of("job_id", jobId));
}
flighttaskService.cancelFlightTask(workspaceId, jobIds);
return HttpResultResponse.success();
}
......@@ -102,10 +118,16 @@ public class WaylineJobController {
}
@PutMapping("/{workspace_id}/jobs/{job_id}")
public HttpResultResponse updateJobStatus(@PathVariable(name = "workspace_id") String workspaceId,
public HttpResultResponse updateJobStatus(HttpServletRequest request,
@PathVariable(name = "workspace_id") String workspaceId,
@PathVariable(name = "job_id") String jobId,
@Valid @RequestBody UpdateJobParam param) {
flighttaskService.updateJobStatus(workspaceId, jobId, param);
operateRecordService.record(request, OperateRecordTypeEnum.FLIGHT_TASK_UPDATE, null,
java.util.Map.of("job_id", jobId, "param", param));
return HttpResultResponse.success();
}
......@@ -157,7 +179,11 @@ public class WaylineJobController {
CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
customClaim.setWorkspaceId(workspaceId);
return flighttaskService.publishInFlightTask(param, customClaim);
HttpResultResponse result = flighttaskService.publishInFlightTask(param, customClaim);
operateRecordService.record(request, OperateRecordTypeEnum.IN_FLIGHT_TASK_CREATE, param.getDockSn(), param);
return result;
}
/**
......@@ -176,9 +202,13 @@ public class WaylineJobController {
* 取消空中航线 in_flight_wayline_cancel
*/
@DeleteMapping("/{workspace_id}/inFlightJobs")
public HttpResultResponse publishCanceInFlightlJob(@RequestParam(name = "dockeSn") String dockSn,
public HttpResultResponse publishCanceInFlightlJob(HttpServletRequest request,
@RequestParam(name = "dockeSn") String dockSn,
@PathVariable(name = "workspace_id") String workspaceId) throws SQLException {
flighttaskService.cancelInFlightTask(workspaceId, dockSn);
operateRecordService.record(request, OperateRecordTypeEnum.IN_FLIGHT_TASK_CANCEL, dockSn, null);
return HttpResultResponse.success();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment