feat(mod): add status and creator tracking
- Introduce ModificationStatus enum with integer codes and a JPA converter - Record the creating user on new modifications - Rebuild the modification table with status, creator and audit columns - Simplify user_role to a single-column primary key
This commit is contained in:
11 files changed
+272
-146
No files matched your search
@@ -4,7 +4,9 @@ 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.PageResponse;
|
||||
import com.onixbyte.deltaforceguide.domain.entity.User;
|
||||
import com.onixbyte.deltaforceguide.security.annotation.RequiresAuth;
|
||||
import com.onixbyte.deltaforceguide.security.resolver.CurrentUser;
|
||||
import com.onixbyte.deltaforceguide.service.ModificationService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -66,15 +68,21 @@ public class ModificationController {
|
||||
@RequiresAuth
|
||||
@Operation(description = "创建改装")
|
||||
@PostMapping
|
||||
public ModificationResponse create(@Valid @RequestBody ModificationRequest request) {
|
||||
return modificationService.create(request);
|
||||
public ModificationResponse create(
|
||||
@CurrentUser User user,
|
||||
@Valid @RequestBody ModificationRequest request
|
||||
) {
|
||||
return modificationService.create(request, user);
|
||||
}
|
||||
|
||||
@RequiresAuth
|
||||
@Operation(description = "批量创建改装")
|
||||
@PostMapping("/batch")
|
||||
public List<ModificationResponse> batchCreate(@Valid @RequestBody ModificationBatchCreateRequest request) {
|
||||
return modificationService.batchCreate(request.modifications());
|
||||
public List<ModificationResponse> batchCreate(
|
||||
@CurrentUser User user,
|
||||
@Valid @RequestBody ModificationBatchCreateRequest request
|
||||
) {
|
||||
return modificationService.batchCreate(request.modifications(), user);
|
||||
}
|
||||
|
||||
@RequiresAuth
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.onixbyte.deltaforceguide.domain.converter;
|
||||
|
||||
import com.onixbyte.deltaforceguide.enumeration.ModificationStatus;
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
|
||||
/**
|
||||
* JPA attribute converter that maps {@link ModificationStatus} enum to/from its integer database representation.
|
||||
*
|
||||
* @author zihluwang
|
||||
*/
|
||||
@Converter
|
||||
public class ModificationStatusConverter implements AttributeConverter<ModificationStatus, Integer> {
|
||||
|
||||
@Override
|
||||
public Integer convertToDatabaseColumn(ModificationStatus attribute) {
|
||||
return attribute == null ? null : attribute.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModificationStatus convertToEntityAttribute(Integer dbData) {
|
||||
return ModificationStatus.fromCode(dbData);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.onixbyte.deltaforceguide.domain.entity;
|
||||
|
||||
import com.onixbyte.deltaforceguide.domain.converter.ModificationStatusConverter;
|
||||
import com.onixbyte.deltaforceguide.enumeration.ModificationStatus;
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Entity representing a firearm modification or build configuration.
|
||||
@@ -25,6 +29,10 @@ public class Modification {
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "firearm_id", nullable = false, foreignKey = @ForeignKey(name = "fk_modification_firearm"))
|
||||
private Firearm firearm;
|
||||
@@ -36,11 +44,11 @@ public class Modification {
|
||||
private String code;
|
||||
|
||||
@Type(JsonType.class)
|
||||
@Column(name = "tags", columnDefinition = "jsonb")
|
||||
@Column(name = "tags", columnDefinition = "jsonb", nullable = false)
|
||||
private List<String> tags = new ArrayList<>();
|
||||
|
||||
@Type(JsonType.class)
|
||||
@Column(name = "accessories", columnDefinition = "jsonb")
|
||||
@Column(name = "accessories", columnDefinition = "jsonb", nullable = false)
|
||||
private List<Accessory> accessories = new ArrayList<>();
|
||||
|
||||
@Column(name = "note", columnDefinition = "TEXT")
|
||||
@@ -52,6 +60,16 @@ public class Modification {
|
||||
@Column(name = "video_url", length = 512)
|
||||
private String videoUrl;
|
||||
|
||||
@Column(name = "status", nullable = false)
|
||||
@Convert(converter = ModificationStatusConverter.class)
|
||||
private ModificationStatus status = ModificationStatus.DRAFT;
|
||||
|
||||
@Column(name = "create_by")
|
||||
private Long createBy;
|
||||
|
||||
@Column(name = "create_time", nullable = false)
|
||||
private OffsetDateTime createTime;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -132,6 +150,38 @@ public class Modification {
|
||||
this.videoUrl = videoUrl;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Long getCreateBy() {
|
||||
return createBy;
|
||||
}
|
||||
|
||||
public void setCreateBy(Long createBy) {
|
||||
this.createBy = createBy;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(OffsetDateTime createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public ModificationStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(ModificationStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
@@ -139,6 +189,7 @@ public class Modification {
|
||||
public static class Builder {
|
||||
|
||||
private Long id;
|
||||
private User user;
|
||||
private Firearm firearm;
|
||||
private String name;
|
||||
private String code;
|
||||
@@ -147,12 +198,20 @@ public class Modification {
|
||||
private String note;
|
||||
private String author;
|
||||
private String videoUrl;
|
||||
private Long createBy;
|
||||
private OffsetDateTime createTime;
|
||||
private ModificationStatus status;
|
||||
|
||||
public Builder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder user(User user) {
|
||||
this.user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder firearm(Firearm firearm) {
|
||||
this.firearm = firearm;
|
||||
return this;
|
||||
@@ -193,17 +252,36 @@ public class Modification {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder createBy(Long createBy) {
|
||||
this.createBy = createBy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder createTime(OffsetDateTime createTime) {
|
||||
this.createTime = createTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder status(ModificationStatus status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Modification build() {
|
||||
Modification modification = new Modification();
|
||||
modification.id = this.id;
|
||||
modification.user = this.user;
|
||||
modification.firearm = this.firearm;
|
||||
modification.name = this.name;
|
||||
modification.code = this.code;
|
||||
modification.tags = this.tags == null ? new ArrayList<>() : this.tags;
|
||||
modification.accessories = this.accessories == null ? new ArrayList<>() : this.accessories;
|
||||
modification.tags = Optional.ofNullable(this.tags).orElseGet(ArrayList::new);
|
||||
modification.accessories = Optional.ofNullable(this.accessories).orElseGet(ArrayList::new);
|
||||
modification.note = this.note;
|
||||
modification.author = this.author;
|
||||
modification.videoUrl = this.videoUrl;
|
||||
modification.createBy = this.createBy;
|
||||
modification.createTime = Optional.ofNullable(this.createTime).orElseGet(OffsetDateTime::now);
|
||||
modification.status = Optional.ofNullable(this.status).orElse(ModificationStatus.DRAFT);
|
||||
return modification;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package com.onixbyte.deltaforceguide.domain.entity;
|
||||
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
import jakarta.persistence.AttributeOverrides;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.MapsId;
|
||||
@@ -14,6 +12,8 @@ import jakarta.persistence.Table;
|
||||
|
||||
/**
|
||||
* Entity representing a role assignment linking a user to a security role.
|
||||
* <p>
|
||||
* A user owns at most one role assignment, so the user ID is the primary key.
|
||||
*
|
||||
* @author zihluwang
|
||||
*/
|
||||
@@ -21,24 +21,24 @@ import jakarta.persistence.Table;
|
||||
@Table(name = "user_role")
|
||||
public class UserRole {
|
||||
|
||||
@EmbeddedId
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "userId", column = @Column(name = "user_id")),
|
||||
@AttributeOverride(name = "role", column = @Column(name = "role"))
|
||||
})
|
||||
private UserRoleId id = new UserRoleId();
|
||||
@Id
|
||||
@Column(name = "user_id")
|
||||
private Long userId;
|
||||
|
||||
@MapsId("userId")
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(name = "fk_user_role_user"))
|
||||
private User user;
|
||||
|
||||
public UserRoleId getId() {
|
||||
return id;
|
||||
@Column(name = "role", nullable = false, length = 32)
|
||||
private String role;
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setId(UserRoleId id) {
|
||||
this.id = id;
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
@@ -47,32 +47,17 @@ public class UserRole {
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
if (this.id == null) {
|
||||
this.id = new UserRoleId();
|
||||
if (user != null) {
|
||||
this.userId = user.getId();
|
||||
}
|
||||
this.id.setUserId(user == null ? null : user.getId());
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return id == null ? null : id.getUserId();
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
if (this.id == null) {
|
||||
this.id = new UserRoleId();
|
||||
}
|
||||
this.id.setUserId(userId);
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return id == null ? null : id.getRole();
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
if (this.id == null) {
|
||||
this.id = new UserRoleId();
|
||||
}
|
||||
this.id.setRole(role);
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
@@ -81,16 +66,10 @@ public class UserRole {
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private UserRoleId id;
|
||||
private User user;
|
||||
private Long userId;
|
||||
private String role;
|
||||
|
||||
public Builder id(UserRoleId id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder user(User user) {
|
||||
this.user = user;
|
||||
return this;
|
||||
@@ -108,16 +87,13 @@ public class UserRole {
|
||||
|
||||
public UserRole build() {
|
||||
UserRole userRole = new UserRole();
|
||||
userRole.id = this.id == null ? new UserRoleId() : this.id;
|
||||
userRole.user = this.user;
|
||||
userRole.role = this.role;
|
||||
if (this.user != null) {
|
||||
userRole.id.setUserId(this.user.getId());
|
||||
userRole.userId = this.user.getId();
|
||||
}
|
||||
if (this.userId != null) {
|
||||
userRole.id.setUserId(this.userId);
|
||||
}
|
||||
if (this.role != null) {
|
||||
userRole.id.setRole(this.role);
|
||||
userRole.userId = this.userId;
|
||||
}
|
||||
return userRole;
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.onixbyte.deltaforceguide.domain.entity;
|
||||
|
||||
import jakarta.persistence.Embeddable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Composite key for the UserRole entity, combining user ID and role name.
|
||||
*
|
||||
* @author zihluwang
|
||||
*/
|
||||
@Embeddable
|
||||
public class UserRoleId implements Serializable {
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String role;
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
UserRoleId that = (UserRoleId) o;
|
||||
return Objects.equals(userId, that.userId) && Objects.equals(role, that.role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userId, role);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private Long userId;
|
||||
private String role;
|
||||
|
||||
public Builder userId(Long userId) {
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder role(String role) {
|
||||
this.role = role;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserRoleId build() {
|
||||
UserRoleId id = new UserRoleId();
|
||||
id.userId = this.userId;
|
||||
id.role = this.role;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.onixbyte.deltaforceguide.enumeration;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Enumeration of modification statuses.
|
||||
* <p>
|
||||
* Each status is associated with an integer code used for database persistence.
|
||||
*
|
||||
* @author zihluwang
|
||||
*/
|
||||
public enum ModificationStatus {
|
||||
|
||||
DRAFT(0),
|
||||
PENDING_APPROVAL(1),
|
||||
PUBLISHED(2);
|
||||
|
||||
private final int code;
|
||||
|
||||
ModificationStatus(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static final Map<Integer, ModificationStatus> CODE_MAP = Arrays.stream(values())
|
||||
.collect(Collectors.toMap(ModificationStatus::getCode, Function.identity()));
|
||||
|
||||
/**
|
||||
* Resolve a ModificationStatus from its integer code.
|
||||
*
|
||||
* @param code the integer code, may be null
|
||||
* @return the corresponding ModificationStatus, or null if the code is null
|
||||
* @throws IllegalArgumentException if the code does not match any known status
|
||||
*/
|
||||
public static ModificationStatus fromCode(Integer code) {
|
||||
return CODE_MAP.get(code);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ 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.Tuning;
|
||||
import com.onixbyte.deltaforceguide.domain.entity.User;
|
||||
import com.onixbyte.deltaforceguide.domain.dto.AccessoryRequest;
|
||||
import com.onixbyte.deltaforceguide.domain.dto.ModificationRequest;
|
||||
import com.onixbyte.deltaforceguide.domain.dto.ModificationResponse;
|
||||
@@ -36,16 +37,16 @@ public class ModificationManager {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ModificationResponse create(ModificationRequest request) {
|
||||
public ModificationResponse create(ModificationRequest request, User user) {
|
||||
var firearm = firearmRepository.findById(request.firearmId())
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
|
||||
"Firearm not found: " + request.firearmId()));
|
||||
var modification = toEntity(request, firearm);
|
||||
var modification = toEntity(request, firearm, user);
|
||||
return ModificationResponse.from(modificationRepository.save(modification));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<ModificationResponse> batchCreate(List<ModificationRequest> requests) {
|
||||
public List<ModificationResponse> batchCreate(List<ModificationRequest> requests, User user) {
|
||||
var firearmIds = requests.stream()
|
||||
.map(ModificationRequest::firearmId)
|
||||
.collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new));
|
||||
@@ -63,7 +64,7 @@ public class ModificationManager {
|
||||
}
|
||||
|
||||
var modifications = requests.stream()
|
||||
.map(req -> toEntity(req, firearmMap.get(req.firearmId())))
|
||||
.map(req -> toEntity(req, firearmMap.get(req.firearmId()), user))
|
||||
.toList();
|
||||
return modificationRepository.saveAll(modifications)
|
||||
.stream()
|
||||
@@ -86,7 +87,7 @@ public class ModificationManager {
|
||||
return matches.getFirst().getId();
|
||||
}
|
||||
|
||||
private Modification toEntity(ModificationRequest request, Firearm firearm) {
|
||||
private Modification toEntity(ModificationRequest request, Firearm firearm, User user) {
|
||||
return Modification.builder()
|
||||
.firearm(firearm)
|
||||
.name(request.name())
|
||||
@@ -96,6 +97,7 @@ public class ModificationManager {
|
||||
.note(request.note())
|
||||
.author(request.author())
|
||||
.videoUrl(request.videoUrl())
|
||||
.createBy(user == null ? null : user.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.onixbyte.deltaforceguide.repository;
|
||||
|
||||
import com.onixbyte.deltaforceguide.domain.entity.UserRole;
|
||||
import com.onixbyte.deltaforceguide.domain.entity.UserRoleId;
|
||||
import org.springframework.data.jpa.repository.*;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
@@ -14,7 +13,7 @@ import java.util.List;
|
||||
* @author zihluwang
|
||||
*/
|
||||
@Repository
|
||||
public interface UserRoleRepository extends JpaRepository<UserRole, UserRoleId>, JpaSpecificationExecutor<UserRole> {
|
||||
public interface UserRoleRepository extends JpaRepository<UserRole, Long>, JpaSpecificationExecutor<UserRole> {
|
||||
|
||||
/**
|
||||
* Find all roles assigned to a given user.
|
||||
@@ -41,7 +40,7 @@ public interface UserRoleRepository extends JpaRepository<UserRole, UserRoleId>,
|
||||
select count(ur) > 0
|
||||
from UserRole ur
|
||||
where ur.user.id = :userId
|
||||
and ur.id.role = :role
|
||||
and ur.role = :role
|
||||
""")
|
||||
boolean existsByUserIdAndRole(@Param("userId") Long userId, @Param("role") String role);
|
||||
|
||||
@@ -55,7 +54,7 @@ public interface UserRoleRepository extends JpaRepository<UserRole, UserRoleId>,
|
||||
@Query("""
|
||||
delete from UserRole ur
|
||||
where ur.user.id = :userId
|
||||
and ur.id.role = :role
|
||||
and ur.role = :role
|
||||
""")
|
||||
void deleteByUserIdAndRole(@Param("userId") Long userId, @Param("role") String role);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.User;
|
||||
import com.onixbyte.deltaforceguide.domain.entity.Tuning;
|
||||
import com.onixbyte.deltaforceguide.manager.ModificationManager;
|
||||
import com.onixbyte.deltaforceguide.repository.FirearmRepository;
|
||||
@@ -104,20 +105,22 @@ public class ModificationService {
|
||||
* Creates a new modification for a given firearm.
|
||||
*
|
||||
* @param request the modification creation request
|
||||
* @param user the authenticated user creating the modification
|
||||
* @return the created modification response
|
||||
*/
|
||||
public ModificationResponse create(ModificationRequest request) {
|
||||
return modificationManager.create(request);
|
||||
public ModificationResponse create(ModificationRequest request, User user) {
|
||||
return modificationManager.create(request, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates multiple modifications in a single batch operation.
|
||||
*
|
||||
* @param requests list of modification creation requests
|
||||
* @param user the authenticated user creating the modifications
|
||||
* @return list of created modification responses
|
||||
*/
|
||||
public List<ModificationResponse> batchCreate(List<ModificationRequest> requests) {
|
||||
return modificationManager.batchCreate(requests);
|
||||
public List<ModificationResponse> batchCreate(List<ModificationRequest> requests, User user) {
|
||||
return modificationManager.batchCreate(requests, user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -88,7 +88,7 @@ public class WebhookService {
|
||||
private void processSingle(Long issueNumber, Map<String, Object> data) {
|
||||
var request = mapToRequest(data);
|
||||
log.info("Creating modification from issue #{}: name={}", issueNumber, request.name());
|
||||
modificationManager.create(request);
|
||||
modificationManager.create(request, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -102,7 +102,7 @@ public class WebhookService {
|
||||
.map(this::mapToRequest)
|
||||
.toList();
|
||||
log.info("Batch creating {} modifications from issue #{}", requests.size(), issueNumber);
|
||||
modificationManager.batchCreate(requests);
|
||||
modificationManager.batchCreate(requests, null);
|
||||
}
|
||||
|
||||
private ModificationRequest mapToRequest(Map<String, Object> data) {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS modification_new
|
||||
(
|
||||
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||
user_id BIGINT NULL REFERENCES app_user (id),
|
||||
firearm_id BIGINT NOT NULL REFERENCES firearm (id),
|
||||
name VARCHAR(64) NOT NULL,
|
||||
code VARCHAR(64) NOT NULL,
|
||||
tags JSONB NOT NULL DEFAULT '[]'::JSONB,
|
||||
note TEXT NULL,
|
||||
author VARCHAR(64) NULL,
|
||||
video_url VARCHAR(512) NULL,
|
||||
accessories JSONB NOT NULL DEFAULT '[]'::JSONB,
|
||||
status INTEGER NOT NULL DEFAULT 0,
|
||||
create_by BIGINT NULL,
|
||||
create_time TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT tags_must_be_string_array CHECK (
|
||||
jsonb_typeof(tags) = 'array' AND
|
||||
NOT jsonb_path_exists(tags, '$[*] ? (@.type() != "string")')
|
||||
)
|
||||
);
|
||||
|
||||
DO
|
||||
$$
|
||||
DECLARE
|
||||
v_super_user_id BIGINT;
|
||||
BEGIN
|
||||
-- Added explicit table reference and LIMIT 1 to prevent multiple-row errors
|
||||
SELECT app_user.id
|
||||
INTO v_super_user_id
|
||||
FROM app_user
|
||||
LEFT JOIN user_role ur ON app_user.id = ur.user_id
|
||||
WHERE ur.role = 'SUPER_USER'
|
||||
LIMIT 1;
|
||||
|
||||
-- Explicitly check for NULL rather than evaluating the integer
|
||||
IF v_super_user_id IS NOT NULL THEN
|
||||
INSERT INTO modification_new(id, user_id, firearm_id, name, code, tags, note, author,
|
||||
video_url, accessories, status, create_by, create_time)
|
||||
SELECT m.id,
|
||||
NULL,
|
||||
m.firearm_id,
|
||||
m.name,
|
||||
m.code,
|
||||
COALESCE(m.tags, '[]'::JSONB),
|
||||
m.note,
|
||||
m.author,
|
||||
m.video_url,
|
||||
COALESCE(m.accessories, '[]'::JSONB),
|
||||
2,
|
||||
v_super_user_id,
|
||||
now()
|
||||
FROM modification m;
|
||||
ELSE
|
||||
-- Fixed RAISE NOTICE parameter formatting
|
||||
RAISE NOTICE 'Super user not found. Please execute the manual SQL commands after adding a super user.';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
ALTER INDEX idx_modification_firearm_id RENAME TO idx_modification_lagacy_firearm_id;
|
||||
|
||||
CREATE INDEX idx_modification_firearm_id ON modification_new (firearm_id);
|
||||
|
||||
ALTER TABLE modification
|
||||
RENAME TO modification_legacy;
|
||||
ALTER TABLE modification_new
|
||||
RENAME TO modification;
|
||||
|
||||
COMMIT;
|
||||
Reference in new issue
Block a user