Skip to content

Commit 99054ba

Browse files
committed
Merge branch 'develop' into feat/#29
2 parents cd27d4f + a978afc commit 99054ba

15 files changed

Lines changed: 247 additions & 37 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,33 @@
1-
name: Deploy to Dev
1+
name: Deploy
22

33
on:
44
push:
55
branches:
66
- develop
7+
- main
78

89
env:
910
REGISTRY: ghcr.io
1011

12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: deploy-${{ github.ref_name }}
17+
cancel-in-progress: false
18+
1119
jobs:
1220
test:
1321
runs-on: ubuntu-latest
1422
steps:
1523
- uses: actions/checkout@v4
16-
1724
- name: Set up JDK 21
1825
uses: actions/setup-java@v4
1926
with:
2027
java-version: '21'
2128
distribution: 'temurin'
22-
2329
- name: Grant execute permission for gradlew
2430
run: chmod +x gradlew
25-
2631
- name: Run tests
2732
run: ./gradlew test
2833

@@ -34,33 +39,36 @@ jobs:
3439
packages: write
3540
outputs:
3641
image: ${{ steps.vars.outputs.image }}
42+
tag: ${{ steps.vars.outputs.tag }}
3743
steps:
3844
- uses: actions/checkout@v4
39-
40-
- name: Compute lowercase image name
45+
- name: Compute lowercase image name and tag
4146
id: vars
4247
run: |
4348
echo "image=${REGISTRY}/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
44-
49+
if [ "${{ github.ref_name }}" = "main" ]; then
50+
echo "tag=prod-latest" >> "$GITHUB_OUTPUT"
51+
else
52+
echo "tag=dev-latest" >> "$GITHUB_OUTPUT"
53+
fi
4554
- name: Set up Docker Buildx
4655
uses: docker/setup-buildx-action@v3
47-
4856
- name: Log in to GHCR
4957
uses: docker/login-action@v3
5058
with:
5159
registry: ${{ env.REGISTRY }}
5260
username: ${{ github.actor }}
5361
password: ${{ secrets.GITHUB_TOKEN }}
54-
5562
- name: Build and push
5663
uses: docker/build-push-action@v5
5764
with:
5865
context: .
5966
platforms: linux/amd64
6067
push: true
61-
tags: ${{ steps.vars.outputs.image }}:dev-latest
68+
tags: ${{ steps.vars.outputs.image }}:${{ steps.vars.outputs.tag }}
6269

63-
deploy:
70+
deploy-dev:
71+
if: github.ref_name == 'develop'
6472
needs: build-and-push
6573
runs-on: ubuntu-latest
6674
environment: development
@@ -74,15 +82,13 @@ jobs:
7482
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
7583
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
7684
aws-region: ${{ env.AWS_REGION }}
77-
7885
- name: Whitelist runner IP for SSH
7986
run: |
8087
RUNNER_IP=$(curl -s https://api.ipify.org)
8188
echo "RUNNER_IP=$RUNNER_IP" >> "$GITHUB_ENV"
8289
aws ec2 authorize-security-group-ingress \
8390
--group-id "$SG_ID" \
8491
--protocol tcp --port 22 --cidr "${RUNNER_IP}/32"
85-
8692
- name: Deploy to EC2 (dev)
8793
uses: appleboy/ssh-action@v1.0.3
8894
with:
@@ -93,10 +99,48 @@ jobs:
9399
cd /srv/jobis-dev
94100
docker compose pull
95101
docker compose up -d
96-
97102
- name: Revoke runner IP from SSH
98103
if: always()
99104
run: |
100105
aws ec2 revoke-security-group-ingress \
101106
--group-id "$SG_ID" \
102107
--protocol tcp --port 22 --cidr "${RUNNER_IP}/32"
108+
109+
deploy-prod:
110+
if: github.ref_name == 'main'
111+
needs: build-and-push
112+
runs-on: ubuntu-latest
113+
environment: production
114+
env:
115+
AWS_REGION: us-east-1
116+
SG_ID: sg-0a99d63a691fd07c8
117+
steps:
118+
- name: Configure AWS credentials
119+
uses: aws-actions/configure-aws-credentials@v4
120+
with:
121+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
122+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
123+
aws-region: ${{ env.AWS_REGION }}
124+
- name: Whitelist runner IP for SSH
125+
run: |
126+
RUNNER_IP=$(curl -s https://api.ipify.org)
127+
echo "RUNNER_IP=$RUNNER_IP" >> "$GITHUB_ENV"
128+
aws ec2 authorize-security-group-ingress \
129+
--group-id "$SG_ID" \
130+
--protocol tcp --port 22 --cidr "${RUNNER_IP}/32"
131+
- name: Deploy to EC2 (prod)
132+
uses: appleboy/ssh-action@v1.0.3
133+
with:
134+
host: ${{ secrets.EC2_HOST }}
135+
username: ubuntu
136+
key: ${{ secrets.EC2_SSH_KEY }}
137+
script: |
138+
cd /srv/jobis-prod
139+
docker compose pull
140+
docker compose up -d
141+
- name: Revoke runner IP from SSH
142+
if: always()
143+
run: |
144+
aws ec2 revoke-security-group-ingress \
145+
--group-id "$SG_ID" \
146+
--protocol tcp --port 22 --cidr "${RUNNER_IP}/32"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.leets7th.job_is_be.domain.job.controller;
2+
3+
import com.leets7th.job_is_be.domain.job.service.JobService;
4+
import com.leets7th.job_is_be.global.response.ApiResponse;
5+
import com.leets7th.job_is_be.global.status.SuccessStatus;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.*;
9+
10+
@RestController
11+
@RequestMapping("/api/jobs")
12+
@RequiredArgsConstructor
13+
public class JobController {
14+
15+
private final JobService jobService;
16+
17+
// TODO 인증 세팅 후 @AuthenticationPrincipal로 교체
18+
@PostMapping("/{jobId}/save")
19+
public ResponseEntity<ApiResponse<Void>> saveJob(
20+
@PathVariable Long jobId,
21+
@RequestParam Long userId
22+
) {
23+
jobService.saveJob(userId, jobId);
24+
return ApiResponse.success(SuccessStatus.JOB_SAVE_SUCCESS);
25+
}
26+
27+
// TODO 인증 세팅 후 @AuthenticationPrincipal로 교체
28+
@DeleteMapping("/{jobId}/save")
29+
public ResponseEntity<ApiResponse<Void>> unsaveJob(
30+
@PathVariable Long jobId,
31+
@RequestParam Long userId
32+
) {
33+
jobService.unsaveJob(userId, jobId);
34+
return ApiResponse.success(SuccessStatus.JOB_UNSAVE_SUCCESS);
35+
}
36+
}

src/main/java/com/leets7th/job_is_be/domain/job/converter/JobPostingConverter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ public Job createFrom(JobPosting posting) {
2525
.sourceUrl(posting.getSourceUrl())
2626
.postedAt(posting.getConfirmTime())
2727
.deadlineAt(posting.getDueTime())
28+
.locationFull(posting.getLocationFull())
29+
.mainTasks(posting.getMainTasks())
30+
.requirements(posting.getRequirements())
31+
.preferredPoints(posting.getPreferredPoints())
32+
.skillTags(posting.getSkillTags())
33+
.skillsInferred(posting.getSkillsInferred())
2834
.build();
2935
applyStatus(job, resolveStatus(posting));
3036
return job;
@@ -34,7 +40,9 @@ public void updateFrom(Job job, JobPosting posting) {
3440
job.syncFrom(posting.getCompany(), posting.getPosition(), resolveCareerLevel(posting),
3541
posting.getEmploymentType(), Boolean.TRUE.equals(posting.getIsRemote()),
3642
posting.getSourceUrl(), posting.getConfirmTime(), posting.getDueTime(),
37-
resolveStatus(posting));
43+
resolveStatus(posting),
44+
posting.getLocationFull(), posting.getMainTasks(), posting.getRequirements(),
45+
posting.getPreferredPoints(), posting.getSkillTags(), posting.getSkillsInferred());
3846
}
3947

4048
private void applyStatus(Job job, JobStatus status) {

src/main/java/com/leets7th/job_is_be/domain/job/entity/Job.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
import lombok.Builder;
99
import lombok.Getter;
1010
import lombok.NoArgsConstructor;
11+
import org.hibernate.annotations.JdbcTypeCode;
12+
import org.hibernate.type.SqlTypes;
1113

1214
import java.time.OffsetDateTime;
15+
import java.util.List;
1316

1417
/**
1518
* 채용 공고
@@ -73,11 +76,32 @@ public class Job extends BaseEntity {
7376
@Column(name = "editor_note", length = 1000)
7477
private String editorNote; // Editor's Note (DET-01)
7578

79+
@Column(name = "location_full", length = 500)
80+
private String locationFull;
81+
82+
@Column(name = "main_tasks", columnDefinition = "TEXT")
83+
private String mainTasks;
84+
85+
@Column(columnDefinition = "TEXT")
86+
private String requirements;
87+
88+
@Column(name = "preferred_points", columnDefinition = "TEXT")
89+
private String preferredPoints;
90+
91+
@JdbcTypeCode(SqlTypes.ARRAY)
92+
@Column(name = "skill_tags")
93+
private List<String> skillTags;
94+
95+
@Column(name = "skills_inferred")
96+
private Boolean skillsInferred;
97+
7698
@Builder
7799
public Job(Company company, JobCategory jobCategory, Region region, String title,
78100
String careerLevel, String employmentType, boolean remoteAvailable,
79101
boolean salaryDisclosed, String source, Long externalId, String sourceUrl,
80-
OffsetDateTime postedAt, OffsetDateTime deadlineAt, String editorNote) {
102+
OffsetDateTime postedAt, OffsetDateTime deadlineAt, String editorNote,
103+
String locationFull, String mainTasks, String requirements,
104+
String preferredPoints, List<String> skillTags, Boolean skillsInferred) {
81105
this.company = company;
82106
this.jobCategory = jobCategory;
83107
this.region = region;
@@ -92,6 +116,12 @@ public Job(Company company, JobCategory jobCategory, Region region, String title
92116
this.postedAt = postedAt;
93117
this.deadlineAt = deadlineAt;
94118
this.editorNote = editorNote;
119+
this.locationFull = locationFull;
120+
this.mainTasks = mainTasks;
121+
this.requirements = requirements;
122+
this.preferredPoints = preferredPoints;
123+
this.skillTags = skillTags;
124+
this.skillsInferred = skillsInferred;
95125
this.status = JobStatus.ACTIVE;
96126
}
97127

@@ -110,7 +140,9 @@ public boolean isOpenEnded() {
110140
// 크롤링 재수집 시 (source, externalId)로 매칭된 기존 공고에 최신 원문 내용을 반영
111141
public void syncFrom(Company company, String title, String careerLevel, String employmentType,
112142
boolean remoteAvailable, String sourceUrl,
113-
OffsetDateTime postedAt, OffsetDateTime deadlineAt, JobStatus status) {
143+
OffsetDateTime postedAt, OffsetDateTime deadlineAt, JobStatus status,
144+
String locationFull, String mainTasks, String requirements,
145+
String preferredPoints, List<String> skillTags, Boolean skillsInferred) {
114146
this.company = company;
115147
this.title = title;
116148
this.careerLevel = careerLevel;
@@ -120,5 +152,11 @@ public void syncFrom(Company company, String title, String careerLevel, String e
120152
this.postedAt = postedAt;
121153
this.deadlineAt = deadlineAt;
122154
this.status = status;
155+
this.locationFull = locationFull;
156+
this.mainTasks = mainTasks;
157+
this.requirements = requirements;
158+
this.preferredPoints = preferredPoints;
159+
this.skillTags = skillTags;
160+
this.skillsInferred = skillsInferred;
123161
}
124162
}

src/main/java/com/leets7th/job_is_be/domain/job/entity/SavedJob.java

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88
import lombok.Getter;
99
import lombok.NoArgsConstructor;
1010

11-
import java.time.LocalDateTime;
11+
import java.time.OffsetDateTime;
12+
1213

13-
/**
14-
* 저장 목록
15-
*/
1614
@Entity
1715
@Table(
1816
name = "saved_jobs",
@@ -38,16 +36,14 @@ public class SavedJob extends BaseEntity {
3836
private Job job;
3937

4038
@Column(name = "saved_at", nullable = false)
41-
private LocalDateTime savedAt;
39+
private OffsetDateTime savedAt;
4240

41+
// 지원함 등 자기보고 상태 — 추후 지원 현황 트래킹에 활용
4342
@Column(name = "self_reported_status", length = 20)
44-
private String selfReportedStatus; // 지원함 등 자기보고 상태 (§6.2)
45-
46-
@Column(name = "unsaved_at")
47-
private LocalDateTime unsavedAt; // 해제 시각 (undo 5초)
43+
private String selfReportedStatus;
4844

4945
@Builder
50-
public SavedJob(User user, Job job, LocalDateTime savedAt) {
46+
public SavedJob(User user, Job job, OffsetDateTime savedAt) {
5147
this.user = user;
5248
this.job = job;
5349
this.savedAt = savedAt;
@@ -56,9 +52,5 @@ public SavedJob(User user, Job job, LocalDateTime savedAt) {
5652
public void markSelfReportedStatus(String status) {
5753
this.selfReportedStatus = status;
5854
}
59-
60-
public void unsave(LocalDateTime now) {
61-
this.unsavedAt = now;
62-
}
6355
}
6456

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.leets7th.job_is_be.domain.job.repository;
2+
3+
import com.leets7th.job_is_be.domain.job.entity.SavedJob;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
import java.util.Optional;
7+
8+
public interface SavedJobRepository extends JpaRepository<SavedJob, Long> {
9+
10+
boolean existsByUserIdAndJobId(Long userId, Long jobId);
11+
12+
Optional<SavedJob> findByUserIdAndJobId(Long userId, Long jobId);
13+
14+
void deleteByUserIdAndJobId(Long userId, Long jobId);
15+
}

0 commit comments

Comments
 (0)