diff --git a/src/main/java/com/hrr/backend/domain/verification/converter/VerificationConverter.java b/src/main/java/com/hrr/backend/domain/verification/converter/VerificationConverter.java index feaedd46..5b827e33 100644 --- a/src/main/java/com/hrr/backend/domain/verification/converter/VerificationConverter.java +++ b/src/main/java/com/hrr/backend/domain/verification/converter/VerificationConverter.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import com.hrr.backend.domain.user.entity.UserChallenge; +import com.hrr.backend.domain.verification.entity.enums.VerificationPostType; import com.hrr.backend.global.response.SliceResponseDto; import com.hrr.backend.domain.round.entity.RoundRecord; import org.springframework.stereotype.Component; @@ -32,9 +33,22 @@ public VerificationResponseDto.FeedDto toFeedDto(Verification verification) { boolean hasLink = verification.getTextUrl() != null && !verification.getTextUrl().isBlank(); // 이미지 URL S3 Full Path 변환 (필요 시) - String fullImageUrl = verification.getPhotoUrl() != null ? - s3UrlUtil.toFullUrl(verification.getPhotoUrl()) : null; - + String fullImageUrl = null; + + if (verification.getType() != null && verification.getType() == VerificationPostType.CAMERA) { + if (verification.getPhotoUrl() != null) { + fullImageUrl = s3UrlUtil.toFullUrl(verification.getPhotoUrl()); + } + } else { + String key = firstNonNull( + verification.getTextImage1(), + verification.getTextImage2(), + verification.getTextImage3() + ); + if (key != null) { + fullImageUrl = s3UrlUtil.toFullUrl(key); + } + } return VerificationResponseDto.FeedDto.builder() .verificationId(verification.getId()) .type(verification.getType()) @@ -68,6 +82,11 @@ public VerificationResponseDto.StatDto toStatDto( * 인증글 생성 직후 응답 DTO 변환 (단건 상세) */ public VerificationResponseDto.CreateResponseDto toResponseDto(Verification verification) { + String fullPhotoUrl = verification.getPhotoUrl() != null ? s3UrlUtil.toFullUrl(verification.getPhotoUrl()) : null; + + String fullTextImage1 = verification.getTextImage1() != null ? s3UrlUtil.toFullUrl(verification.getTextImage1()) : null; + String fullTextImage2 = verification.getTextImage2() != null ? s3UrlUtil.toFullUrl(verification.getTextImage2()) : null; + String fullTextImage3 = verification.getTextImage3() != null ? s3UrlUtil.toFullUrl(verification.getTextImage3()) : null; return VerificationResponseDto.CreateResponseDto.builder() .verificationId(verification.getId()) .roundId(verification.getRoundRecord().getRound().getId()) @@ -76,7 +95,10 @@ public VerificationResponseDto.CreateResponseDto toResponseDto(Verification veri .title(verification.getTitle()) .content(verification.getContent()) .textUrl(verification.getTextUrl()) - .photoUrl(s3UrlUtil.toFullUrl(verification.getPhotoUrl())) + .photoUrl(fullPhotoUrl) + .textImage1(fullTextImage1) + .textImage2(fullTextImage2) + .textImage3(fullTextImage3) .isQuestion(verification.getIsQuestion()) .status(verification.getStatus()) .createdAt(verification.getCreatedAt()) @@ -118,7 +140,12 @@ public VerificationDetailResponseDto toDetailDto( UserChallenge userChallenge = roundRecord.getUserChallenge(); User user = userChallenge.getUser(); - String photoUrl = s3UrlUtil.toFullUrl(verification.getPhotoUrl()); + String fullPhotoUrl = verification.getPhotoUrl() != null ? s3UrlUtil.toFullUrl(verification.getPhotoUrl()) : null; + + String fullTextImage1 = verification.getTextImage1() != null ? s3UrlUtil.toFullUrl(verification.getTextImage1()) : null; + String fullTextImage2 = verification.getTextImage2() != null ? s3UrlUtil.toFullUrl(verification.getTextImage2()) : null; + String fullTextImage3 = verification.getTextImage3() != null ? s3UrlUtil.toFullUrl(verification.getTextImage3()) : null; + VerificationDetailResponseDto.UserInfo userInfo = VerificationDetailResponseDto.UserInfo.builder() @@ -146,7 +173,10 @@ public VerificationDetailResponseDto toDetailDto( .title(verification.getTitle()) .content(verification.getContent()) .textUrl(verification.getTextUrl()) - .photoUrl(photoUrl) + .photoUrl(fullPhotoUrl) + .textImage1(fullTextImage1) + .textImage2(fullTextImage2) + .textImage3(fullTextImage3) .isQuestion(verification.getIsQuestion()) .isResolved(verification.getIsResolved()) .status(verification.getStatus()) @@ -158,7 +188,7 @@ public VerificationDetailResponseDto toDetailDto( .canEdit(canEdit) .canDelete(canDelete) .canSelectComment(canSelectComment) - .canWriteComment(true) // 정책 상 언제든 댓글 작성 가능 + .canWriteComment(true) .adoptedCommentId(adoptedCommentId) .showResolvedBadge(verification.getIsResolved()) .commentCount(comments != null && comments.getComments() != null @@ -173,6 +203,11 @@ public VerificationDetailResponseDto toDetailDto( * Verification 엔티티를 HistoryDto로 변환 */ public VerificationResponseDto.HistoryDto toHistoryDto(Verification verification) { + String fullPhotoUrl = verification.getPhotoUrl() != null ? s3UrlUtil.toFullUrl(verification.getPhotoUrl()) : null; + + String fullTextImage1 = verification.getTextImage1() != null ? s3UrlUtil.toFullUrl(verification.getTextImage1()) : null; + String fullTextImage2 = verification.getTextImage2() != null ? s3UrlUtil.toFullUrl(verification.getTextImage2()) : null; + String fullTextImage3 = verification.getTextImage3() != null ? s3UrlUtil.toFullUrl(verification.getTextImage3()) : null; return VerificationResponseDto.HistoryDto.builder() .verificationId(verification.getId()) .challengeId(verification.getRoundRecord().getUserChallenge().getChallenge().getId()) @@ -180,10 +215,18 @@ public VerificationResponseDto.HistoryDto toHistoryDto(Verification verification .type(verification.getType().name()) .title(verification.getTitle()) .content(verification.getContent()) - .photoUrl(verification.getPhotoUrl() != null ? - s3UrlUtil.toFullUrl(verification.getPhotoUrl()) : null) + .photoUrl(fullPhotoUrl) .textUrl(verification.getTextUrl()) + .textImage1(fullTextImage1) + .textImage2(fullTextImage2) + .textImage3(fullTextImage3) .verifiedAt(verification.getCreatedAt()) .build(); } + private String firstNonNull(String a, String b, String c) { + if (a != null) return a; + if (b != null) return b; + if (c != null) return c; + return null; + } } diff --git a/src/main/java/com/hrr/backend/domain/verification/dto/VerificationDetailResponseDto.java b/src/main/java/com/hrr/backend/domain/verification/dto/VerificationDetailResponseDto.java index 3d98b0e4..a07b6bbf 100644 --- a/src/main/java/com/hrr/backend/domain/verification/dto/VerificationDetailResponseDto.java +++ b/src/main/java/com/hrr/backend/domain/verification/dto/VerificationDetailResponseDto.java @@ -31,6 +31,10 @@ public class VerificationDetailResponseDto { private String textUrl; private String photoUrl; + private String textImage1; + private String textImage2; + private String textImage3; + private Boolean isQuestion; private Boolean isResolved; private VerificationStatus status; diff --git a/src/main/java/com/hrr/backend/domain/verification/dto/VerificationRequestDto.java b/src/main/java/com/hrr/backend/domain/verification/dto/VerificationRequestDto.java index 050803a8..a75432f4 100644 --- a/src/main/java/com/hrr/backend/domain/verification/dto/VerificationRequestDto.java +++ b/src/main/java/com/hrr/backend/domain/verification/dto/VerificationRequestDto.java @@ -20,7 +20,12 @@ public class VerificationRequestDto { private String textUrl; - private String photoUrl; + @Schema(description = "글 인증 첨부 이미지1(S3 Key)", nullable = true) + private String textImage1; + @Schema(description = "글 인증 첨부 이미지2(S3 Key)", nullable = true) + private String textImage2; + @Schema(description = "글 인증 첨부 이미지3(S3 Key)", nullable = true) + private String textImage3; private Boolean isQuestion; } \ No newline at end of file diff --git a/src/main/java/com/hrr/backend/domain/verification/dto/VerificationResponseDto.java b/src/main/java/com/hrr/backend/domain/verification/dto/VerificationResponseDto.java index dc2f1add..074cc673 100644 --- a/src/main/java/com/hrr/backend/domain/verification/dto/VerificationResponseDto.java +++ b/src/main/java/com/hrr/backend/domain/verification/dto/VerificationResponseDto.java @@ -118,6 +118,9 @@ public static class CreateResponseDto { private String content; private String photoUrl; private String textUrl; + private String textImage1; + private String textImage2; + private String textImage3; private Boolean isQuestion; @Schema(description = "상태 (TEMPORARY / COMPLETED)") @@ -169,6 +172,10 @@ public static class HistoryDto { @Schema(description = "글 URL (글 인증)", example = "https://blog.example.com/post/123") private String textUrl; + private String textImage1; + private String textImage2; + private String textImage3; + @Schema(description = "인증 일시", example = "2025-09-18T08:00:00") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime verifiedAt; diff --git a/src/main/java/com/hrr/backend/domain/verification/dto/VerificationUpdateRequestDto.java b/src/main/java/com/hrr/backend/domain/verification/dto/VerificationUpdateRequestDto.java index 27b7b9a4..27601082 100644 --- a/src/main/java/com/hrr/backend/domain/verification/dto/VerificationUpdateRequestDto.java +++ b/src/main/java/com/hrr/backend/domain/verification/dto/VerificationUpdateRequestDto.java @@ -1,5 +1,6 @@ package com.hrr.backend.domain.verification.dto; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Size; import lombok.Builder; import lombok.Getter; @@ -12,5 +13,11 @@ public class VerificationUpdateRequestDto { @Size(max = 200, message = "내용은 200자를 초과할 수 없습니다.") private String content; private String textUrl; - private String photoUrl; + @Schema(description = "글 인증 첨부 이미지1(S3 Key)", nullable = true) + private String textImage1; + @Schema(description = "글 인증 첨부 이미지2(S3 Key)", nullable = true) + private String textImage2; + @Schema(description = "글 인증 첨부 이미지3(S3 Key)", nullable = true) + private String textImage3; + private Boolean isQuestion; } diff --git a/src/main/java/com/hrr/backend/domain/verification/entity/Verification.java b/src/main/java/com/hrr/backend/domain/verification/entity/Verification.java index b81eb3c0..eea8c965 100644 --- a/src/main/java/com/hrr/backend/domain/verification/entity/Verification.java +++ b/src/main/java/com/hrr/backend/domain/verification/entity/Verification.java @@ -54,6 +54,9 @@ public class Verification extends BaseEntity { private String photoUrl; private String textUrl; + private String textImage1; + private String textImage2; + private String textImage3; private Boolean isQuestion; @@ -84,7 +87,9 @@ public static Verification createTextVerification( String title, String content, String textUrl, - String photoUrl, + String textImage1, + String textImage2, + String textImage3, Boolean isQuestion, Long roundId ) { @@ -96,7 +101,9 @@ public static Verification createTextVerification( .title(title) .content(content) .textUrl(textUrl) - .photoUrl(photoUrl) + .textImage1(textImage1) + .textImage2(textImage2) + .textImage3(textImage3) .isQuestion(isQuestion) .status(VerificationStatus.COMPLETED) .isResolved(false) // feat/90 필드 초기화 @@ -142,26 +149,51 @@ public void setUserChallenge(UserChallenge uc) { this.userChallenge = uc; } - public void update(String title, String content, String textUrl, String photoUrl) { + public void update( + String title, + String content, + String textUrl, + String textImage1, + String textImage2, + String textImage3, + Boolean isQuestion + ) { if (title != null) { if (title.isBlank()) { throw new GlobalException(ErrorCode.VERIFICATION_TITLE_REQUIRED); - } + } this.title = title; } + if (content != null) { if (content.isBlank()) { - throw new GlobalException(ErrorCode.VERIFICATION_TEXT_REQUIRED);} + throw new GlobalException(ErrorCode.VERIFICATION_TEXT_REQUIRED); + } this.content = content; } - if (textUrl != null) { - this.textUrl = textUrl; + if (textImage1 != null) { + this.textImage1 = normalizeOptionalImageKey(textImage1); } - if (photoUrl != null) { - this.photoUrl = photoUrl; + if (textImage2 != null) { + this.textImage2 = normalizeOptionalImageKey(textImage2); + } + if (textImage3 != null) { + this.textImage3 = normalizeOptionalImageKey(textImage3); } - } - + if (isQuestion != null) { + this.isQuestion = isQuestion; + } + } + private String normalizeOptionalImageKey(String key) { + String trimmed = key.trim(); + if (trimmed.isEmpty()) { + throw new GlobalException(ErrorCode.VERIFICATION_INVALID_IMAGE_KEY); + } + if ("null".equalsIgnoreCase(trimmed)) { + throw new GlobalException(ErrorCode.VERIFICATION_INVALID_IMAGE_KEY); + } + return trimmed; + } } diff --git a/src/main/java/com/hrr/backend/domain/verification/service/VerificationServiceImpl.java b/src/main/java/com/hrr/backend/domain/verification/service/VerificationServiceImpl.java index 3a030734..e8064b18 100644 --- a/src/main/java/com/hrr/backend/domain/verification/service/VerificationServiceImpl.java +++ b/src/main/java/com/hrr/backend/domain/verification/service/VerificationServiceImpl.java @@ -1,8 +1,13 @@ package com.hrr.backend.domain.verification.service; +import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.util.List; + +import com.hrr.backend.domain.challenge.entity.ChallengeDayJoin; +import com.hrr.backend.global.common.enums.ChallengeDays; import com.hrr.backend.domain.comment.dto.CommentListResponseDto; import com.hrr.backend.domain.comment.dto.CommentResponseDto; @@ -195,7 +200,9 @@ public VerificationResponseDto.CreateResponseDto createTextVerification( request.getTitle(), request.getContent(), request.getTextUrl(), - request.getPhotoUrl(), + request.getTextImage1(), + request.getTextImage2(), + request.getTextImage3(), request.getIsQuestion() != null ? request.getIsQuestion() : false, roundId ); @@ -376,10 +383,10 @@ public VerificationDetailResponseDto updateVerification(Long verificationId, Lon Verification verification = verificationRepository.findById(verificationId) .orElseThrow(() -> new GlobalException(ErrorCode.VERIFICATION_NOT_FOUND)); - // 차단된 게시글 접근 시 예외 발생 - if (verification.getStatus() == VerificationStatus.BLOCKED) { - throw new GlobalException(ErrorCode.ACCESS_DENIED_REPORTED_POST); - } + // 차단된 게시글 접근 시 예외 발생 + if (verification.getStatus() == VerificationStatus.BLOCKED) { + throw new GlobalException(ErrorCode.ACCESS_DENIED_REPORTED_POST); + } // 작성자 본인인지 권한 체크 User author = verification.getRoundRecord() @@ -390,12 +397,18 @@ public VerificationDetailResponseDto updateVerification(Long verificationId, Lon throw new GlobalException(ErrorCode.VERIFICATION_ACCESS_DENIED); } + // 인증 요일 + 인증 시간대 안에서만 수정 가능 + validateVerificationEditDeleteWindow(verification); + // 엔티티 업데이트 verification.update( requestDto.getTitle(), requestDto.getContent(), requestDto.getTextUrl(), - requestDto.getPhotoUrl() + requestDto.getTextImage1(), + requestDto.getTextImage2(), + requestDto.getTextImage3(), + requestDto.getIsQuestion() ); // 댓글 목록 + 상세 DTO 구성 @@ -433,12 +446,12 @@ public void deleteVerification(Long verificationId, Long currentUserId) { Verification verification = verificationRepository.findById(verificationId) .orElseThrow(() -> new GlobalException(ErrorCode.VERIFICATION_NOT_FOUND)); - // 차단된 게시글 접근 시 예외 발생 - if (verification.getStatus() == VerificationStatus.BLOCKED) { - throw new GlobalException(ErrorCode.ACCESS_DENIED_REPORTED_POST); - } + // 차단된 게시글 접근 시 예외 발생 + if (verification.getStatus() == VerificationStatus.BLOCKED) { + throw new GlobalException(ErrorCode.ACCESS_DENIED_REPORTED_POST); + } - // 작성자 본인인지 권한 체크 + // 작성자 본인인지 권한 체크 User author = verification.getRoundRecord() .getUserChallenge() .getUser(); @@ -446,6 +459,10 @@ public void deleteVerification(Long verificationId, Long currentUserId) { if (!author.getId().equals(currentUserId)) { throw new GlobalException(ErrorCode.VERIFICATION_ACCESS_DENIED); } + + // 인증 요일 + 인증 시간대 안에서만 삭제 가능 + validateVerificationEditDeleteWindow(verification); + verificationRepository.delete(verification); } @@ -480,6 +497,97 @@ public SliceResponseDto getVerificationHisto return new SliceResponseDto<>(dtoSlice); } + // 수정/삭제 시간 제한 로직 (최소 범위) + + private void validateVerificationEditDeleteWindow(Verification verification) { + Challenge challenge = verification.getRoundRecord() + .getRound() + .getChallenge(); + + LocalDateTime now = LocalDateTime.now(); + + // 1) 지금이 "인증 시간대"가 아니면 수정/삭제 불가 + if (!isWithinVerificationTime(challenge, now.toLocalTime())) { + throw new GlobalException(ErrorCode.VERIFICATION_EDIT_DELETE_NOT_ALLOWED); + } + + // 2) 지금 인증 윈도우가 어느 "인증 요일(기준 날짜)"에 속하는지 계산 + LocalDate currentWindowDate = getWindowAnchorDate(challenge, now); + + // 3) 그 날짜가 챌린지의 인증 요일에 포함되지 않으면 불가 + if (!isVerificationDay(challenge, currentWindowDate)) { + throw new GlobalException(ErrorCode.VERIFICATION_EDIT_DELETE_NOT_ALLOWED); + } + + // 4) 이 인증글이 "현재 인증 윈도우"에 속하는 글이 아니면 불가 + LocalDate postWindowDate = getWindowAnchorDate(challenge, verification.getCreatedAt()); + if (!postWindowDate.equals(currentWindowDate)) { + throw new GlobalException(ErrorCode.VERIFICATION_EDIT_DELETE_NOT_ALLOWED); + } + } + + /** + * 인증 시간대 포함 여부 (start <= end 일반 케이스 + start > end 자정 넘어가는 케이스 대응) + */ + private boolean isWithinVerificationTime(Challenge challenge, LocalTime now) { + LocalTime start = challenge.getVerifyStartTime(); + LocalTime end = challenge.getVerifyEndTime(); + + // 일반 케이스: 09:00 ~ 22:00 + if (start.isBefore(end) || start.equals(end)) { + return !now.isBefore(start) && !now.isAfter(end); + } + + // 자정 넘어가는 케이스: 22:00 ~ 02:00 + return !now.isBefore(start) || !now.isAfter(end); + } + + /** + * "인증 윈도우 기준 날짜(anchor date)" 계산 + * - 일반 케이스(start<=end): anchor = 해당 시각의 날짜 + * - 자정 넘어가는 케이스(start>end): + * - 22:00~23:59 -> anchor = 오늘 + * - 00:00~02:00 -> anchor = 어제(시작 요일 기준) + */ + private LocalDate getWindowAnchorDate(Challenge challenge, LocalDateTime dateTime) { + LocalTime start = challenge.getVerifyStartTime(); + LocalTime end = challenge.getVerifyEndTime(); + + if (start.isBefore(end) || start.equals(end)) { + return dateTime.toLocalDate(); + } + + // overnight + LocalTime t = dateTime.toLocalTime(); + if (!t.isBefore(start)) { + return dateTime.toLocalDate(); + } + return dateTime.toLocalDate().minusDays(1); + } + + /** + * 특정 날짜가 챌린지 인증 요일인지 확인 + */ + private boolean isVerificationDay(Challenge challenge, LocalDate date) { + ChallengeDays targetDay = mapToChallengeDays(date.getDayOfWeek()); + List days = challenge.getChallengeDays(); + + return days.stream() + .anyMatch(join -> join.getDayOfWeek() == targetDay); + } + + private ChallengeDays mapToChallengeDays(DayOfWeek dayOfWeek) { + return switch (dayOfWeek) { + case MONDAY -> ChallengeDays.MONDAY; + case TUESDAY -> ChallengeDays.TUESDAY; + case WEDNESDAY -> ChallengeDays.WEDNESDAY; + case THURSDAY -> ChallengeDays.THURSDAY; + case FRIDAY -> ChallengeDays.FRIDAY; + case SATURDAY -> ChallengeDays.SATURDAY; + case SUNDAY -> ChallengeDays.SUNDAY; + }; + } + @Override public VerificationResponseDto.OtherUserHistoryResponse getOtherUserVerificationHistory( Long userId, @@ -518,4 +626,5 @@ public VerificationResponseDto.OtherUserHistoryResponse getOtherUserVerification .verifications(new SliceResponseDto<>(dtoSlice)) .build(); } -} \ No newline at end of file +} + diff --git a/src/main/java/com/hrr/backend/global/response/ErrorCode.java b/src/main/java/com/hrr/backend/global/response/ErrorCode.java index fff64f65..0b8ae32c 100644 --- a/src/main/java/com/hrr/backend/global/response/ErrorCode.java +++ b/src/main/java/com/hrr/backend/global/response/ErrorCode.java @@ -123,7 +123,8 @@ public enum ErrorCode implements BaseCode{ ACCESS_DENIED_REPORTED_POST(HttpStatus.CONFLICT, "VERIFICATION40917", "신고 5회 누적으로 제한된 게시글입니다."), CANNOT_REPORT_OWN_POST(HttpStatus.CONFLICT, "VERIFICATION40918", "자기 자신의 인증 게시글은 신고할 수 없습니다."), ALREADY_REPORTED(HttpStatus.CONFLICT, "VERIFICATION40919", "이미 신고한 게시글입니다."), - + VERIFICATION_EDIT_DELETE_NOT_ALLOWED(HttpStatus.BAD_REQUEST, "VERIFICATION40020", "인증 요일 및 인증 시간 내에만 수정/삭제할 수 있습니다."), + VERIFICATION_INVALID_IMAGE_KEY(HttpStatus.BAD_REQUEST, "VERIFICATION40021", "이미지 키는 null 또는 공백이 아닌 값이어야 합니다."), // file upload FILE_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "FILE5001", "파일 업로드에 실패했습니다."), FILE_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST, "FILE4002", "파일 크기가 제한을 초과했습니다."), diff --git a/src/main/resources/db/migration/V2.25__add_text_image_to_verification.sql b/src/main/resources/db/migration/V2.25__add_text_image_to_verification.sql new file mode 100644 index 00000000..a04dd498 --- /dev/null +++ b/src/main/resources/db/migration/V2.25__add_text_image_to_verification.sql @@ -0,0 +1,44 @@ +DROP PROCEDURE IF EXISTS add_verification_text_images; + +DELIMITER $$ + +CREATE PROCEDURE add_verification_text_images() +BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'verification' + AND COLUMN_NAME = 'text_image1' + ) THEN +ALTER TABLE verification + ADD COLUMN text_image1 VARCHAR(512) NULL; +END IF; + + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'verification' + AND COLUMN_NAME = 'text_image2' + ) THEN +ALTER TABLE verification + ADD COLUMN text_image2 VARCHAR(512) NULL; +END IF; + + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'verification' + AND COLUMN_NAME = 'text_image3' + ) THEN +ALTER TABLE verification + ADD COLUMN text_image3 VARCHAR(512) NULL; +END IF; +END $$ + +DELIMITER ; + +CALL add_verification_text_images(); +DROP PROCEDURE IF EXISTS add_verification_text_images;