feat: implement submission repository and service layer with caching support
All checks were successful
Build and Release / release (push) Successful in 1m32s

This commit is contained in:
2026-05-24 13:11:40 +07:00
parent 9dacfd036d
commit 5a75798609
4 changed files with 117 additions and 30 deletions

View File

@@ -159,6 +159,50 @@ func (q *Queries) DeleteSubmission(ctx context.Context, id pgtype.UUID) error {
return err
}
const getLatestApprovedSubmission = `-- name: GetLatestApprovedSubmission :one
SELECT
id, project_id, commit_id, user_id, created_at, status, reviewed_by, reviewed_at, review_note, content, is_deleted
FROM submissions
WHERE project_id = $1
AND status = 2
AND is_deleted = false
ORDER BY reviewed_at DESC, created_at DESC
LIMIT 1
`
type GetLatestApprovedSubmissionRow struct {
ID pgtype.UUID `json:"id"`
ProjectID pgtype.UUID `json:"project_id"`
CommitID pgtype.UUID `json:"commit_id"`
UserID pgtype.UUID `json:"user_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
Status int16 `json:"status"`
ReviewedBy pgtype.UUID `json:"reviewed_by"`
ReviewedAt pgtype.Timestamptz `json:"reviewed_at"`
ReviewNote pgtype.Text `json:"review_note"`
Content pgtype.Text `json:"content"`
IsDeleted bool `json:"is_deleted"`
}
func (q *Queries) GetLatestApprovedSubmission(ctx context.Context, projectID pgtype.UUID) (GetLatestApprovedSubmissionRow, error) {
row := q.db.QueryRow(ctx, getLatestApprovedSubmission, projectID)
var i GetLatestApprovedSubmissionRow
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.CommitID,
&i.UserID,
&i.CreatedAt,
&i.Status,
&i.ReviewedBy,
&i.ReviewedAt,
&i.ReviewNote,
&i.Content,
&i.IsDeleted,
)
return i, err
}
const getLatestApprovedSubmissionExcluding = `-- name: GetLatestApprovedSubmissionExcluding :one
SELECT
id, project_id, commit_id, user_id, created_at, status, reviewed_by, reviewed_at, review_note, content, is_deleted

View File

@@ -24,6 +24,7 @@ type SubmissionRepository interface {
Update(ctx context.Context, params sqlc.UpdateSubmissionParams) (*models.SubmissionEntity, error)
Delete(ctx context.Context, id pgtype.UUID) error
GetLatestApprovedSubmissionExcluding(ctx context.Context, projectID pgtype.UUID, id pgtype.UUID) (*models.SubmissionEntity, error)
GetLatestApprovedSubmission(ctx context.Context, projectID pgtype.UUID) (*models.SubmissionEntity, error)
WithTx(tx pgx.Tx) SubmissionRepository
}
@@ -349,3 +350,25 @@ func (r *submissionRepository) GetLatestApprovedSubmissionExcluding(ctx context.
}
return entity, nil
}
func (r *submissionRepository) GetLatestApprovedSubmission(ctx context.Context, projectID pgtype.UUID) (*models.SubmissionEntity, error) {
row, err := r.q.GetLatestApprovedSubmission(ctx, projectID)
if err != nil {
return nil, err
}
entity := &models.SubmissionEntity{
ID: convert.UUIDToString(row.ID),
ProjectID: convert.UUIDToString(row.ProjectID),
CommitID: convert.UUIDToString(row.CommitID),
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
Status: constants.ParseStatusType(row.Status),
ReviewedBy: convert.UUIDToStringPtr(row.ReviewedBy),
ReviewedAt: convert.TimeToPtr(row.ReviewedAt),
ReviewNote: convert.TextToPtr(row.ReviewNote),
Content: convert.TextToPtr(row.Content),
IsDeleted: row.IsDeleted,
}
return entity, nil
}

View File

@@ -421,44 +421,54 @@ func (s *submissionService) DeleteSubmission(ctx context.Context, userID string,
submissionRepo := s.submissionRepo.WithTx(tx)
isLatestApprovedDeleted := false
if submission.Status == constants.StatusTypeApproved {
projectUUID, err := convert.StringToUUID(submission.ProjectID)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid project ID")
}
prevSubmission, err := s.submissionRepo.GetLatestApprovedSubmissionExcluding(ctx, projectUUID, submissionUUID)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
if err := s.clearProjectItems(ctx, tx, projectUUID); err != nil {
latestApproved, err := s.submissionRepo.GetLatestApprovedSubmission(ctx, projectUUID)
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check latest approved submission: "+err.Error())
}
if err == nil && latestApproved != nil && latestApproved.ID == submission.ID {
isLatestApprovedDeleted = true
prevSubmission, err := s.submissionRepo.GetLatestApprovedSubmissionExcluding(ctx, projectUUID, submissionUUID)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
if err := s.clearProjectItems(ctx, tx, projectUUID); err != nil {
return err
}
} else {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get previous approved submission: "+err.Error())
}
} else if prevSubmission != nil {
prevCommitUUID, err := convert.StringToUUID(prevSubmission.CommitID)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid previous commit ID")
}
prevCommit, err := s.commitRepo.GetByID(ctx, prevCommitUUID)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, "Previous commit not found")
}
var prevSnapshotData request.CommitSnapshot
err = json.Unmarshal(prevCommit.SnapshotJson, &prevSnapshotData)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to parse previous commit snapshot")
}
if err := s.applySnapshot(ctx, tx, projectUUID, prevCommitUUID, &prevSnapshotData); err != nil {
return err
}
} else {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get previous approved submission: "+err.Error())
}
} else if prevSubmission != nil {
prevCommitUUID, err := convert.StringToUUID(prevSubmission.CommitID)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid previous commit ID")
}
prevCommit, err := s.commitRepo.GetByID(ctx, prevCommitUUID)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, "Previous commit not found")
}
var prevSnapshotData request.CommitSnapshot
err = json.Unmarshal(prevCommit.SnapshotJson, &prevSnapshotData)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to parse previous commit snapshot")
}
if err := s.applySnapshot(ctx, tx, projectUUID, prevCommitUUID, &prevSnapshotData); err != nil {
return err
}
} else {
if err := s.clearProjectItems(ctx, tx, projectUUID); err != nil {
return err
if err := s.clearProjectItems(ctx, tx, projectUUID); err != nil {
return err
}
}
}
}
@@ -473,7 +483,7 @@ func (s *submissionService) DeleteSubmission(ctx context.Context, userID string,
return fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction: "+err.Error())
}
if submission.Status == constants.StatusTypeApproved {
if isLatestApprovedDeleted {
go func() {
bgCtx := context.Background()
_ = s.c.DelByPattern(bgCtx, "entity:search*")