feat: add modification creation and deletion endpoints, including batch operations and request DTOs
This commit is contained in:
@@ -1,18 +1,26 @@
|
|||||||
package com.onixbyte.deltaforceguide.controller;
|
package com.onixbyte.deltaforceguide.controller;
|
||||||
|
|
||||||
|
import com.onixbyte.deltaforceguide.domain.dto.ModificationBatchDeleteRequest;
|
||||||
|
import com.onixbyte.deltaforceguide.domain.dto.ModificationBatchCreateRequest;
|
||||||
|
import com.onixbyte.deltaforceguide.domain.dto.ModificationRequest;
|
||||||
import com.onixbyte.deltaforceguide.domain.dto.ModificationResponse;
|
import com.onixbyte.deltaforceguide.domain.dto.ModificationResponse;
|
||||||
import com.onixbyte.deltaforceguide.domain.dto.PageResponse;
|
import com.onixbyte.deltaforceguide.domain.dto.PageResponse;
|
||||||
import com.onixbyte.deltaforceguide.service.ModificationService;
|
import com.onixbyte.deltaforceguide.service.ModificationService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import jakarta.validation.constraints.Max;
|
import jakarta.validation.constraints.Max;
|
||||||
import jakarta.validation.constraints.Min;
|
import jakarta.validation.constraints.Min;
|
||||||
import jakarta.validation.constraints.Positive;
|
import jakarta.validation.constraints.Positive;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@@ -49,4 +57,34 @@ public class ModificationController {
|
|||||||
public ModificationResponse queryById(@PathVariable Long id) {
|
public ModificationResponse queryById(@PathVariable Long id) {
|
||||||
return modificationService.queryById(id);
|
return modificationService.queryById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(description = "创建改装")
|
||||||
|
@PostMapping
|
||||||
|
public ModificationResponse create(@Valid @RequestBody ModificationRequest request) {
|
||||||
|
return modificationService.create(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(description = "批量创建改装")
|
||||||
|
@PostMapping("/batch")
|
||||||
|
public List<ModificationResponse> batchCreate(@Valid @RequestBody ModificationBatchCreateRequest request) {
|
||||||
|
return modificationService.batchCreate(request.modifications());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(description = "修改指定改装")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ModificationResponse update(@PathVariable Long id, @Valid @RequestBody ModificationRequest request) {
|
||||||
|
return modificationService.update(id, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(description = "删除指定改装")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void delete(@PathVariable Long id) {
|
||||||
|
modificationService.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(description = "批量删除改装")
|
||||||
|
@DeleteMapping("/batch-delete")
|
||||||
|
public void batchDelete(@Valid @RequestBody ModificationBatchDeleteRequest request) {
|
||||||
|
modificationService.batchDelete(request.ids());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.domain.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record AccessoryRequest(
|
||||||
|
@NotBlank(message = "插槽名称不能为空")
|
||||||
|
String slotName,
|
||||||
|
@NotBlank(message = "配件名称不能为空")
|
||||||
|
String accessoryName,
|
||||||
|
List<@Valid TuningRequest> tunings
|
||||||
|
) {
|
||||||
|
public List<TuningRequest> tunings() {
|
||||||
|
return tunings == null ? new ArrayList<>() : tunings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.domain.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record ModificationBatchCreateRequest(
|
||||||
|
@NotEmpty(message = "批量创建列表不能为空")
|
||||||
|
List<@Valid ModificationRequest> modifications
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.domain.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.Positive;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record ModificationBatchDeleteRequest(
|
||||||
|
@NotEmpty(message = "批量删除ID列表不能为空")
|
||||||
|
List<@Positive(message = "ID必须为正数") Long> ids
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.domain.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Positive;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record ModificationRequest(
|
||||||
|
@NotNull(message = "武器ID不能为空")
|
||||||
|
@Positive(message = "武器ID必须为正数")
|
||||||
|
Long firearmId,
|
||||||
|
@NotBlank(message = "改装名称不能为空")
|
||||||
|
String name,
|
||||||
|
@NotBlank(message = "改装代码不能为空")
|
||||||
|
String code,
|
||||||
|
List<@NotBlank(message = "标签不能为空") String> tags,
|
||||||
|
List<@Valid AccessoryRequest> accessories,
|
||||||
|
String note,
|
||||||
|
String author,
|
||||||
|
String videoUrl
|
||||||
|
) {
|
||||||
|
public List<String> tags() {
|
||||||
|
return tags == null ? new ArrayList<>() : tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AccessoryRequest> accessories() {
|
||||||
|
return accessories == null ? new ArrayList<>() : accessories;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.domain.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public record TuningRequest(
|
||||||
|
@NotBlank(message = "调校项名称不能为空")
|
||||||
|
String tuningName,
|
||||||
|
@NotNull(message = "调校值不能为空")
|
||||||
|
Double tuningValue
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
package com.onixbyte.deltaforceguide.service;
|
package com.onixbyte.deltaforceguide.service;
|
||||||
|
|
||||||
|
import com.onixbyte.deltaforceguide.domain.dto.AccessoryRequest;
|
||||||
|
import com.onixbyte.deltaforceguide.domain.dto.ModificationRequest;
|
||||||
import com.onixbyte.deltaforceguide.domain.dto.ModificationResponse;
|
import com.onixbyte.deltaforceguide.domain.dto.ModificationResponse;
|
||||||
import com.onixbyte.deltaforceguide.domain.dto.PageResponse;
|
import com.onixbyte.deltaforceguide.domain.dto.PageResponse;
|
||||||
|
import com.onixbyte.deltaforceguide.domain.dto.TuningRequest;
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.Accessory;
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.Firearm;
|
||||||
import com.onixbyte.deltaforceguide.domain.entity.Modification;
|
import com.onixbyte.deltaforceguide.domain.entity.Modification;
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.Tuning;
|
||||||
|
import com.onixbyte.deltaforceguide.repository.FirearmRepository;
|
||||||
import com.onixbyte.deltaforceguide.repository.ModificationRepository;
|
import com.onixbyte.deltaforceguide.repository.ModificationRepository;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
@@ -13,16 +20,27 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ModificationService {
|
public class ModificationService {
|
||||||
|
|
||||||
private final ModificationRepository modificationRepository;
|
private final ModificationRepository modificationRepository;
|
||||||
|
private final FirearmRepository firearmRepository;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
public ModificationService(ModificationRepository modificationRepository, ObjectMapper objectMapper) {
|
public ModificationService(
|
||||||
|
ModificationRepository modificationRepository,
|
||||||
|
FirearmRepository firearmRepository,
|
||||||
|
ObjectMapper objectMapper
|
||||||
|
) {
|
||||||
this.modificationRepository = modificationRepository;
|
this.modificationRepository = modificationRepository;
|
||||||
|
this.firearmRepository = firearmRepository;
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,4 +76,134 @@ public class ModificationService {
|
|||||||
public List<String> findAllTags(Long firearmId) {
|
public List<String> findAllTags(Long firearmId) {
|
||||||
return modificationRepository.findAllTags(firearmId);
|
return modificationRepository.findAllTags(firearmId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public ModificationResponse create(ModificationRequest request) {
|
||||||
|
Firearm firearm = firearmRepository.findById(request.firearmId())
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Firearm not found: " + request.firearmId()));
|
||||||
|
|
||||||
|
Modification modification = toEntity(request, firearm);
|
||||||
|
return ModificationResponse.from(modificationRepository.save(modification));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public List<ModificationResponse> batchCreate(List<ModificationRequest> requests) {
|
||||||
|
Set<Long> firearmIds = requests.stream()
|
||||||
|
.map(ModificationRequest::firearmId)
|
||||||
|
.collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new));
|
||||||
|
|
||||||
|
Map<Long, Firearm> firearmMap = new HashMap<>();
|
||||||
|
firearmRepository.findAllById(firearmIds).forEach(firearm -> firearmMap.put(firearm.getId(), firearm));
|
||||||
|
|
||||||
|
if (firearmMap.size() != firearmIds.size()) {
|
||||||
|
List<Long> missingFirearmIds = firearmIds.stream()
|
||||||
|
.filter(id -> !firearmMap.containsKey(id))
|
||||||
|
.toList();
|
||||||
|
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Firearm not found: " + missingFirearmIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Modification> modifications = requests.stream()
|
||||||
|
.map(request -> toEntity(request, firearmMap.get(request.firearmId())))
|
||||||
|
.toList();
|
||||||
|
return modificationRepository.saveAll(modifications)
|
||||||
|
.stream()
|
||||||
|
.map(ModificationResponse::from)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public ModificationResponse update(Long id, ModificationRequest request) {
|
||||||
|
Modification modification = modificationRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Modification not found: " + id));
|
||||||
|
Firearm firearm = firearmRepository.findById(request.firearmId())
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Firearm not found: " + request.firearmId()));
|
||||||
|
|
||||||
|
modification.setFirearm(firearm);
|
||||||
|
modification.setName(request.name());
|
||||||
|
modification.setCode(request.code());
|
||||||
|
modification.setTags(safeTags(request.tags()));
|
||||||
|
modification.setAccessories(toAccessories(request.accessories()));
|
||||||
|
modification.setNote(request.note());
|
||||||
|
modification.setAuthor(request.author());
|
||||||
|
modification.setVideoUrl(request.videoUrl());
|
||||||
|
|
||||||
|
return ModificationResponse.from(modificationRepository.save(modification));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void delete(Long id) {
|
||||||
|
Modification modification = modificationRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Modification not found: " + id));
|
||||||
|
modificationRepository.delete(modification);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void batchDelete(List<Long> ids) {
|
||||||
|
Set<Long> uniqueIds = new LinkedHashSet<>(ids);
|
||||||
|
List<Modification> modifications = modificationRepository.findAllById(uniqueIds);
|
||||||
|
|
||||||
|
if (modifications.size() != uniqueIds.size()) {
|
||||||
|
Set<Long> foundIds = modifications.stream()
|
||||||
|
.map(Modification::getId)
|
||||||
|
.collect(java.util.stream.Collectors.toSet());
|
||||||
|
List<Long> missingIds = uniqueIds.stream()
|
||||||
|
.filter(id -> !foundIds.contains(id))
|
||||||
|
.toList();
|
||||||
|
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Modification not found: " + missingIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
modificationRepository.deleteAllInBatch(modifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Modification toEntity(ModificationRequest request, Firearm firearm) {
|
||||||
|
return Modification.builder()
|
||||||
|
.firearm(firearm)
|
||||||
|
.name(request.name())
|
||||||
|
.code(request.code())
|
||||||
|
.tags(safeTags(request.tags()))
|
||||||
|
.accessories(toAccessories(request.accessories()))
|
||||||
|
.note(request.note())
|
||||||
|
.author(request.author())
|
||||||
|
.videoUrl(request.videoUrl())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> safeTags(List<String> tags) {
|
||||||
|
return tags == null ? new ArrayList<>() : tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Accessory> toAccessories(List<AccessoryRequest> accessoryRequests) {
|
||||||
|
if (accessoryRequests == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessoryRequests.stream()
|
||||||
|
.map(this::toAccessory)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Accessory toAccessory(AccessoryRequest request) {
|
||||||
|
Accessory accessory = new Accessory();
|
||||||
|
accessory.setSlotName(request.slotName());
|
||||||
|
accessory.setAccessoryName(request.accessoryName());
|
||||||
|
accessory.setTunings(toTunings(request.tunings()));
|
||||||
|
return accessory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Tuning> toTunings(List<TuningRequest> tuningRequests) {
|
||||||
|
if (tuningRequests == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tuningRequests.stream()
|
||||||
|
.map(this::toTuning)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tuning toTuning(TuningRequest request) {
|
||||||
|
Tuning tuning = new Tuning();
|
||||||
|
tuning.setTuningName(request.tuningName());
|
||||||
|
tuning.setTuningValue(request.tuningValue());
|
||||||
|
return tuning;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user