Skip to content

Commit 4fe3931

Browse files
authored
Merge pull request #117 from umc-commit/feature/badge
Feature/badge
2 parents 374d2f8 + 7772619 commit 4fe3931

File tree

10 files changed

+492
-11
lines changed

10 files changed

+492
-11
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
Warnings:
3+
4+
- A unique constraint covering the columns `[type,threshold]` on the table `badges` will be added. If there are existing duplicate values, this will fail.
5+
- Added the required column `threshold` to the `badges` table without a default value. This is not possible if the table is not empty.
6+
- Added the required column `type` to the `badges` table without a default value. This is not possible if the table is not empty.
7+
8+
*/
9+
-- AlterTable
10+
ALTER TABLE `badges` ADD COLUMN `badge_image` VARCHAR(255) NULL,
11+
ADD COLUMN `threshold` INTEGER NOT NULL,
12+
ADD COLUMN `type` VARCHAR(100) NOT NULL;
13+
14+
-- CreateTable
15+
CREATE TABLE `user_badges` (
16+
`id` BIGINT NOT NULL AUTO_INCREMENT,
17+
`account_id` BIGINT NOT NULL,
18+
`badge_id` BIGINT NOT NULL,
19+
`earned_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
20+
21+
UNIQUE INDEX `user_badges_account_id_badge_id_key`(`account_id`, `badge_id`),
22+
PRIMARY KEY (`id`)
23+
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
24+
25+
-- CreateIndex
26+
CREATE UNIQUE INDEX `badges_type_threshold_key` ON `badges`(`type`, `threshold`);
27+
28+
-- AddForeignKey
29+
ALTER TABLE `user_badges` ADD CONSTRAINT `user_badges_account_id_fkey` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
30+
31+
-- AddForeignKey
32+
ALTER TABLE `user_badges` ADD CONSTRAINT `user_badges_badge_id_fkey` FOREIGN KEY (`badge_id`) REFERENCES `badges`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;

prisma/schema.prisma

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ model Account {
2424
userAgreements UserAgreement[]
2525
userCategories UserCategory[]
2626
follows Follow[]
27+
userBadges UserBadge[]
2728
2829
@@unique([provider, oauthId])
2930
@@map("accounts")
@@ -348,12 +349,31 @@ model UserAgreement {
348349
}
349350

350351
model Badge {
351-
id BigInt @id @default(autoincrement())
352-
name String @db.VarChar(100)
352+
id BigInt @id @default(autoincrement())
353+
type String @db.VarChar(100)
354+
threshold Int
355+
name String @db.VarChar(100)
356+
badgeImage String? @map("badge_image") @db.VarChar(255)
353357
358+
userBadges UserBadge[]
359+
360+
@@unique([type, threshold])
354361
@@map("badges")
355362
}
356363

364+
model UserBadge {
365+
id BigInt @id @default(autoincrement())
366+
accountId BigInt @map("account_id")
367+
badgeId BigInt @map("badge_id")
368+
earnedAt DateTime @default(now()) @map("earned_at")
369+
370+
account Account @relation(fields: [accountId], references: [id])
371+
badge Badge @relation(fields:[badgeId], references:[id])
372+
373+
@@unique([accountId, badgeId])
374+
@@map("user_badges")
375+
}
376+
357377
model Session {
358378
id String @id
359379
sid String @unique

prisma/seed.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,113 @@ async function main() {
191191
},
192192
});
193193

194+
const badges = await prisma.badge.createMany({
195+
data:[
196+
{
197+
name: "첫 커미션 완료!",
198+
type: "comm_finish",
199+
threshold: 1,
200+
badgeImage: "https://example.com/badge_comm1.png",
201+
},
202+
{
203+
name: "5회 커미션 완료!",
204+
type: "comm_finish",
205+
threshold: 5,
206+
badgeImage: "https://example.com/badge_comm5.png",
207+
},
208+
{
209+
name: "15회 커미션 완료!",
210+
type: "comm_finish",
211+
threshold: 15,
212+
badgeImage: "https://example.com/badge_comm15.png",
213+
},
214+
{
215+
name: "50회 커미션 완료!",
216+
type: "comm_finish",
217+
threshold: 50,
218+
badgeImage: "https://example.com/badge_com50.png",
219+
},
220+
{
221+
name: "첫 팔로우 완료!",
222+
type: "follow",
223+
threshold: 1,
224+
badgeImage: "https://example.com/badge_follow1.png",
225+
},
226+
{
227+
name: "팔로우 5회!",
228+
type: "follow",
229+
threshold: 5,
230+
badgeImage: "https://example.com/badge_follow5.png",
231+
},
232+
{
233+
name: "팔로우 15회!",
234+
type: "follow",
235+
threshold: 15,
236+
badgeImage: "https://example.com/badge_follow15.png",
237+
},
238+
{
239+
name: "팔로우 50회!",
240+
type: "follow",
241+
threshold: 50,
242+
badgeImage: "https://example.com/badge_follow50.png",
243+
},
244+
{
245+
name: "첫 후기 작성 완료!",
246+
type: "review",
247+
threshold: 1,
248+
badgeImage: "https://example.com/badge_review1.png",
249+
},
250+
{
251+
name: "5회 후기 작성!",
252+
type: "review",
253+
threshold: 5,
254+
badgeImage: "https://example.com/badge_review5.png",
255+
},
256+
{
257+
name: "15회 후기 작성!",
258+
type: "review",
259+
threshold: 15,
260+
badgeImage: "https://example.com/badge_review15.png",
261+
},
262+
{
263+
name: "50회 후기 작성!",
264+
type: "review",
265+
threshold: 50,
266+
badgeImage: "https://example.com/badge_review50.png",
267+
},
268+
{
269+
name: "첫 커미션 신청 완료!",
270+
type: "comm_request",
271+
threshold: 1,
272+
badgeImage: "https://example.com/badge_request1.png",
273+
},
274+
{
275+
name: "5회 커미션 신청 완료!",
276+
type: "comm_request",
277+
threshold: 5,
278+
badgeImage: "https://example.com/badge_request5.png",
279+
},
280+
{
281+
name: "15회 커미션 신청 완료!",
282+
type: "comm_request",
283+
threshold: 15,
284+
badgeImage: "https://example.com/badge_request15.png",
285+
},
286+
{
287+
name: "50회 커미션 신청 완료!",
288+
type: "comm_request",
289+
threshold: 50,
290+
badgeImage: "https://example.com/badge_request50.png",
291+
},
292+
{
293+
name: "가입 1주년!",
294+
type: "signup_1year",
295+
threshold: 50,
296+
badgeImage: "https://example.com/badge_signup_1year.png",
297+
},
298+
]
299+
})
300+
194301
console.log("✅ Seed completed successfully.");
195302
}
196303

src/routes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import paymentRouter from "./payment/payment.routes.js"
1010
import pointRouter from "./point/point.routes.js"
1111
import requestRouter from "./request/request.routes.js"
1212
import homeRouter from "./home/home.routes.js"
13+
import artistRouter from "./user/artist.routes.js"
1314
import tokenRouter from "./token.routes.js"
1415

1516
const router = express.Router();
@@ -19,6 +20,7 @@ router.use("/", bookmarkRouter);
1920
router.use("/search", searchRouter)
2021
router.use("/commissions", commissionRouter);
2122
router.use("/users", userRouter);
23+
router.use("/artists", artistRouter);
2224
router.use("/reviews", reviewRouter);
2325
router.use("/notifications", notificationRouter);
2426
router.use("/payments", paymentRouter);

src/user/artist.routes.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import express from "express";
2+
import { AccessArtistProfile } from "./controller/user.controller.js";
3+
import { authenticate } from "../middlewares/auth.middleware.js";
4+
5+
const router = express.Router();
6+
7+
8+
// 작가 프로필 조회
9+
router.get("/:artistId", authenticate,AccessArtistProfile );
10+
11+
12+
export default router;

src/user/controller/user.controller.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,35 @@ export const LookUserFollow = async(req, res, next) => {
158158
}catch(err) {
159159
next(err);
160160
}
161-
}
161+
}
162+
163+
// 사용자의 뱃지 조회하기
164+
export const LookUserBadge = async(req, res, next) => {
165+
try{
166+
console.log("🎖️Decoded JWT from req.user:", req.user);
167+
168+
const accountId = req.user.accountId.toString();
169+
console.log("사용자의 뱃지 조회 accountId -> ", accountId);
170+
171+
const result = await UserService.ViewUserBadge(accountId);
172+
173+
res.status(StatusCodes.OK).success(result);
174+
}catch(err) {
175+
next(err);
176+
}
177+
}
178+
179+
// 작가 프로필 조회하기
180+
export const AccessArtistProfile = async(req, res, next) => {
181+
try{
182+
const artistId = req.params.artistId;
183+
const accountId = req.user.accountId;
184+
185+
const result = await UserService.AccessArtistProfile(artistId, accountId);
186+
187+
res.status(StatusCodes.OK).success(result);
188+
} catch(err) {
189+
next(err);
190+
}
191+
}
192+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { prisma } from "../../db.config.js"
2+
3+
export const BadgeRepository = {
4+
// type과 progress를 기준으로 발급 가능한 뱃지를 조회하기
5+
async findEligibleBadgesByProgress(type, progress) {
6+
return await prisma.badge.findMany({
7+
where: {
8+
type,
9+
threshold:{
10+
lte:progress,
11+
},
12+
},
13+
orderBy:{
14+
threshold:"asc"
15+
}
16+
});
17+
},
18+
// 여러개의 뱃지를 사용자에게 한 번에 발급하기
19+
async createManyUserBadges(accountId, badgeIds){
20+
if(!badgeIds.length) return;
21+
22+
const data=badgeIds.map((badgeId)=> ({
23+
accountId,
24+
badgeId,
25+
earnedAt: new Date(),
26+
}));
27+
28+
return await prisma.userBadge.createMany({
29+
data,
30+
skipDuplicates:true, // 같은 뱃지 중복 발급 방지
31+
})
32+
},
33+
// 사용자의 뱃지 조회하기
34+
async ViewUserBadges(accountId){
35+
return await prisma.userBadge.findMany({
36+
where:{
37+
accountId,
38+
},
39+
include:{
40+
badge:true,
41+
},
42+
orderBy:{
43+
earnedAt:'desc',
44+
}
45+
});
46+
},
47+
// 작가 뱃지 조회하기
48+
async ViewArtistBadges(accountId){
49+
return await prisma.userBadge.findMany({
50+
where:{
51+
accountId,
52+
},
53+
include:{
54+
badge:true,
55+
},
56+
orderBy:{
57+
earnedAt:'desc',
58+
}
59+
});
60+
}
61+
62+
};
63+

0 commit comments

Comments
 (0)