Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions capturecat-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ dependencies {
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
implementation 'org.flywaydb:flyway-core'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.capturecat.core.domain.user;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import com.capturecat.core.domain.BaseTimeEntity;
import com.capturecat.core.domain.tag.Tag;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Confirm BaseTimeEntity column names match DDL (created_date/last_modified_date).

If BaseTimeEntity uses camelCase without @column overrides, naming strategy must map to snake_case. Otherwise inserts will fail.

Run:


🏁 Script executed:

#!/bin/bash
# Verify BaseTimeEntity column mappings
fd -t f -a 'BaseTimeEntity.java' | xargs -I{} sh -c '
  echo "== {} ==";
  rg -n "createdDate|lastModifiedDate|@Column" -n {} -C2
'
# Check application naming strategy
rg -n "spring.jpa.hibernate.naming" -C2 **/application*.yml **/application*.properties || true

Length of output: 678


Explicitly define snake_case column names for audit fields
In BaseTimeEntity.java, annotate the fields with name overrides to match the DDL:

  • Line 21:
    @Column(name = "created_date", nullable = false, updatable = false)
  • Line 25:
    @Column(name = "last_modified_date", nullable = false)
🤖 Prompt for AI Agents
In capturecat-core/src/main/java/com/capturecat/core/domain/BaseTimeEntity.java
around lines 21 to 25, the audit timestamp fields must map to snake_case column
names; add @Column(name = "created_date", nullable = false, updatable = false)
to the createdDate field and @Column(name = "last_modified_date", nullable =
false) to the lastModifiedDate field, and ensure jakarta.persistence.Column is
imported so the JPA column name overrides compile.

@Entity
@Table(name = "user_tag",
uniqueConstraints = @UniqueConstraint(name = "uk_user_tag_user_tag", columnNames = {"user_id", "tag_id"})
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserTag extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tag_id")
private Tag tag;
}
87 changes: 87 additions & 0 deletions capturecat-core/src/main/resources/db/migration/V1__init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
create table if not exists users
(
id bigint not null primary key,
nickname varchar(50) not null,
email varchar(50) not null,
username varchar(50) not null unique,
password varchar(70),
role varchar(255) not null constraint users_role_check check (role in ('ADMIN', 'PREMIUM_USER', 'USER')),
tutorial_completed boolean not null,
provider varchar(255),
social_id varchar(255),
created_date timestamp(6) not null,
last_modified_date timestamp(6) not null
);

create table if not exists user_social_account
(
id bigint not null primary key,
created_date timestamp(6) not null,
last_modified_date timestamp(6) not null,
provider varchar(30) not null,
social_id varchar(100) not null,
unlink_key varchar(512),
user_id bigint not null constraint fk998rgv7jn090iyc77f8e1xsnq references users,
constraint uksj2lqxj8h0xuqf9v1dvtlkegt unique (provider, social_id)
);

create table if not exists refresh_token
(
id bigint not null primary key,
refresh_token_expiration bigint not null,
expiration varchar(255),
refresh_token varchar(255),
username varchar(255)
);
Comment on lines +28 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

refresh_token: ambiguous expirations and missing FK to user.

  • Two expiration columns conflict; keep one.
  • Add FK to users(username) to prevent orphans.
  • Consider identity PK.
-create table if not exists refresh_token
+create table if not exists refresh_token
 (
-    id                       bigint not null primary key,
-    refresh_token_expiration bigint not null,
-    expiration               varchar(255),
+    id                       bigint generated by default as identity primary key,
+    refresh_token_expiration bigint not null,
     refresh_token            varchar(255),
-    username                 varchar(255)
+    username                 varchar(50) not null,
+    constraint fk_refresh_token_user_username foreign key (username) references users(username) on delete cascade
 );

If you truly need two expirations, rename with clear semantics.

🤖 Prompt for AI Agents
In capturecat-core/src/main/resources/db/migration/V1__init.sql around lines
28–35, the refresh_token table currently has two ambiguous expiration columns
and lacks a foreign key to users; remove the redundant expiration varchar column
and keep a single clearly typed expiration (use refresh_token_expiration
bigint), change id to an identity/auto-increment column (e.g., BIGINT GENERATED
ALWAYS AS IDENTITY) instead of a plain bigint PK, ensure username is NOT NULL if
appropriate, and add an explicit foreign key constraint referencing
users(username) (optionally with ON DELETE CASCADE or RESTRICT based on desired
behavior); if you truly need two expirations, rename them to explicit semantic
names (e.g., issued_at_ts and expires_at_ts) instead of keeping generic
"expiration".


create table if not exists withdraw_log
(
id bigint generated by default as identity primary key,
created_date timestamp(6) not null,
last_modified_date timestamp(6) not null,
image_cleanup_status varchar(255) not null constraint withdraw_log_image_cleanup_status_check check (image_cleanup_status in ('PENDING', 'DONE', 'FAILED')),
reason text,
user_id bigint not null
);
Comment on lines +37 to +45
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

withdraw_log: add FK for user_id.

Currently no FK; risks orphaned rows.

 create table if not exists withdraw_log
 (
     id                   bigint generated by default as identity primary key,
     created_date         timestamp(6) not null,
     last_modified_date   timestamp(6) not null,
     image_cleanup_status varchar(255) not null constraint withdraw_log_image_cleanup_status_check check (image_cleanup_status in ('PENDING', 'DONE', 'FAILED')),
     reason               text,
-    user_id              bigint       not null
+    user_id              bigint       not null constraint fk_withdraw_log_user references users
 );

Optionally add on delete cascade depending on business rules.

🤖 Prompt for AI Agents
In capturecat-core/src/main/resources/db/migration/V1__init.sql around lines 37
to 45, the withdraw_log table lacks a foreign key on user_id which can lead to
orphaned rows; add a foreign key constraint like "constraint
withdraw_log_user_id_fkey foreign key (user_id) references users(id)" and decide
whether to append "on delete cascade" per business rules (or use "on delete
restrict" if cascading deletes are undesired), ensuring the constraint name and
referenced table/column match your schema.


create index if not exists idx_withdraw_log_user_id on withdraw_log (user_id);

create index if not exists idx_withdraw_log_created_date on withdraw_log (created_date);

create index if not exists idx_withdraw_log_cleanup_status on withdraw_log (image_cleanup_status, created_date);

create table if not exists images
(
id bigint generated by default as identity primary key,
size bigint,
file_name varchar(255),
file_url varchar(255),
capture_date date,
created_date timestamp(6) not null,
last_modified_date timestamp(6) not null,
user_id bigint constraint fk13ljqfrfwbyvnsdhihwta8cpr references users
);

create table if not exists tag
(
id bigint generated by default as identity primary key,
name varchar(255),
created_date timestamp(6) not null,
last_modified_date timestamp(6) not null
);

create table if not exists image_tag
(
id bigint generated by default as identity primary key,
image_id bigint constraint fk6q9wuvp5j846qtqod6xu3gma1 references images,
tag_id bigint constraint fk28yowgjl7oksr7dc0wj7f5il references tag,
created_date timestamp(6) not null,
last_modified_date timestamp(6) not null
);
Comment on lines +73 to +80
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

image_tag: prevent duplicates and add indexes.

  • Add unique (image_id, tag_id).
  • Index both FKs for joins.
  • Make FKs NOT NULL.
 create table if not exists image_tag
 (
     id                 bigint generated by default as identity primary key,
-    image_id           bigint constraint fk6q9wuvp5j846qtqod6xu3gma1 references images,
-    tag_id             bigint constraint fk28yowgjl7oksr7dc0wj7f5il references tag,
+    image_id           bigint not null constraint fk6q9wuvp5j846qtqod6xu3gma1 references images,
+    tag_id             bigint not null constraint fk28yowgjl7oksr7dc0wj7f5il references tag,
     created_date       timestamp(6) not null,
     last_modified_date timestamp(6) not null
 );
+
+create unique index if not exists uk_image_tag_image_tag on image_tag (image_id, tag_id);
+create index if not exists idx_image_tag_image_id on image_tag (image_id);
+create index if not exists idx_image_tag_tag_id on image_tag (tag_id);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
create table if not exists image_tag
(
id bigint generated by default as identity primary key,
image_id bigint constraint fk6q9wuvp5j846qtqod6xu3gma1 references images,
tag_id bigint constraint fk28yowgjl7oksr7dc0wj7f5il references tag,
created_date timestamp(6) not null,
last_modified_date timestamp(6) not null
);
create table if not exists image_tag
(
id bigint generated by default as identity primary key,
image_id bigint not null constraint fk6q9wuvp5j846qtqod6xu3gma1 references images,
tag_id bigint not null constraint fk28yowgjl7oksr7dc0wj7f5il references tag,
created_date timestamp(6) not null,
last_modified_date timestamp(6) not null
);
create unique index if not exists uk_image_tag_image_tag on image_tag (image_id, tag_id);
create index if not exists idx_image_tag_image_id on image_tag (image_id);
create index if not exists idx_image_tag_tag_id on image_tag (tag_id);
🤖 Prompt for AI Agents
In capturecat-core/src/main/resources/db/migration/V1__init.sql around lines 73
to 80, modify the image_tag table definition to make image_id and tag_id NOT
NULL, add a unique constraint on (image_id, tag_id) to prevent duplicate
mappings, and add separate indexes on image_id and tag_id to speed joins;
implement this by altering the CREATE TABLE to declare image_id and tag_id as
NOT NULL, adding UNIQUE(image_id, tag_id) (or a named constraint), and adding
CREATE INDEX statements for image_id and tag_id after the table creation.


create table if not exists bookmark
(
id bigint generated by default as identity primary key,
image_id bigint constraint fkpowbsxsu0qwcon1yoxbsqkw4w references images,
user_id bigint constraint fko4vbqvq5trl11d85bqu5kl870 references users
);
Comment on lines +82 to +87
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

bookmark: prevent duplicates and add indexes.

Enforce one bookmark per (user, image) and speed lookups.

 create table if not exists bookmark
 (
     id       bigint generated by default as identity primary key,
     image_id bigint constraint fkpowbsxsu0qwcon1yoxbsqkw4w references images,
     user_id  bigint constraint fko4vbqvq5trl11d85bqu5kl870 references users
 );
+
+create unique index if not exists uk_bookmark_user_image on bookmark (user_id, image_id);
+create index if not exists idx_bookmark_image_id on bookmark (image_id);
+create index if not exists idx_bookmark_user_id on bookmark (user_id);
🤖 Prompt for AI Agents
In capturecat-core/src/main/resources/db/migration/V1__init.sql around lines 82
to 87, the bookmark table allows duplicate (user_id, image_id) pairs and lacks
indexes for fast lookups; add a UNIQUE constraint on (user_id, image_id) to
enforce one bookmark per user-image and create B-tree indexes on user_id and
image_id (or a composite index on (user_id, image_id) if preferred) to speed
queries; update the migration by altering the CREATE TABLE to include the
UNIQUE(user_id, image_id) clause and add CREATE INDEX statements for user_id and
image_id (or a single composite index) after the table creation, ensuring
constraint and index names follow project naming conventions.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
create table if not exists user_tag
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid IF NOT EXISTS in versioned migrations.

Flyway migrations should be deterministic; IF NOT EXISTS can hide drift and let incompatible schemas slip through.

-create table if not exists user_tag
+create table user_tag

If the table might already exist in some envs, prefer a separate repeatable migration or conditional precheck script, not IF NOT EXISTS in V2.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
create table if not exists user_tag
create table user_tag
🤖 Prompt for AI Agents
In capturecat-core/src/main/resources/db/migration/V2__create_user_tag.sql
around line 1, the migration uses "IF NOT EXISTS" which hides schema drift and
breaks Flyway determinism; remove the "IF NOT EXISTS" clause so the CREATE TABLE
fails if the table already exists, and instead handle environments where the
table may already exist by adding either (a) a separate repeatable migration
that safely creates/maintains the table, or (b) a one-time precheck script that
conditionally creates the table before running versioned migrations; ensure the
V2 migration contains a plain CREATE TABLE statement and update migration
tracking accordingly.

(
id bigint generated by default as identity primary key,
user_id bigint constraint fk_user_tag_user references users,
tag_id bigint constraint fk_user_tag_tag references tag,
Comment on lines +4 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

FKs should be NOT NULL and consider ON DELETE CASCADE.

A user_tag without user/tag is invalid. Also consider cascading deletes to avoid orphans when a user/tag is removed.

Option A (edit V2 before merge):

-    user_id            bigint constraint fk_user_tag_user references users,
-    tag_id             bigint constraint fk_user_tag_tag references tag,
+    user_id            bigint not null
+                       constraint fk_user_tag_user references users(id) on delete cascade,
+    tag_id             bigint not null
+                       constraint fk_user_tag_tag references tag(id) on delete cascade,

If V2 may already be applied locally, create V3 with:

alter table user_tag
  alter column user_id set not null,
  alter column tag_id set not null;
alter table user_tag
  drop constraint if exists fk_user_tag_user,
  add constraint fk_user_tag_user foreign key (user_id) references users(id) on delete cascade;
alter table user_tag
  drop constraint if exists fk_user_tag_tag,
  add constraint fk_user_tag_tag foreign key (tag_id) references tag(id) on delete cascade;
🤖 Prompt for AI Agents
In capturecat-core/src/main/resources/db/migration/V2__create_user_tag.sql
around lines 4-5, the user_id and tag_id foreign keys are nullable and lack
cascade behavior; make both columns NOT NULL and update the fk_user_tag_user and
fk_user_tag_tag constraints to reference the correct parent id columns and
include ON DELETE CASCADE. If you can edit V2 before merging, change the column
definitions to NOT NULL and alter the FK definitions to add ON DELETE CASCADE;
if V2 may already be applied, add a new migration (V3) that alters the user_tag
table to set both columns NOT NULL and replaces the existing fk constraints with
versions that reference users(id) and tag(id) and include ON DELETE CASCADE.

created_date timestamp(6) not null,
last_modified_date timestamp(6) not null,
constraint uk_user_tag_user_tag unique (user_id, tag_id)
);
3 changes: 3 additions & 0 deletions capturecat-core/src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ spring:
host: localhost
port: 6379

flyway:
enabled: false

image:
local:
base-path: ${user.home}/Desktop/capturecat/upload
Expand Down
Loading