Skip to content

Commit

Permalink
バックエンドで成功回数と失敗回数を取得する機能を実装
Browse files Browse the repository at this point in the history
  • Loading branch information
MurakawaTakuya committed Dec 28, 2024
1 parent b414e46 commit b99e7a5
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 28 deletions.
28 changes: 20 additions & 8 deletions Documents/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ API is provided by Firebase Cloud Functions. Database is provided by Firestore.
```json
{
"message":"User created successfully",
"userId":"VikWgycxDM4FM7Z0IlSs"
"userId":"VikWgycxDM4FM7Z0IlSs",
"completed": 4,
"failed": 1,
"streak": 2
}
```
UserId is hash string generated by Firebase and saved as its document id.
Expand All @@ -39,7 +42,9 @@ API is provided by Firebase Cloud Functions. Database is provided by Firestore.
{
"id":"krf6rGH3avZSJXU1nJs5",
"name":"testUser",
"streak":10
"completed": 4,
"failed": 1,
"streak": 2
}
```

Expand Down Expand Up @@ -103,7 +108,7 @@ API is provided by Firebase Cloud Functions. Database is provided by Firestore.
"text": "hoge fuga",
"post": {
"userId": "Vlx6GCtq90ag3lxgh0pcCKGp5ba0",
"storedURL": "ointtq9NT5TPgEnKS4tb",
"storedURL": "hogehoge URL",
"text": "数学の勉強したよ^^",
"submittedAt": {
"_seconds": 1735603199,
Expand All @@ -115,7 +120,8 @@ API is provided by Firebase Cloud Functions. Database is provided by Firestore.
"goalId": "iU2YbeHYWzTOv6THwKBS",
"deadline": "2025-12-01T23:59:59.000Z",
"userId": "Vlx6GCtq90ag3lxgh0pcCKGp5ba0",
"text": "hoge fuga"
"text": "hoge fuga",
"post": null
}
]
```
Expand Down Expand Up @@ -165,7 +171,7 @@ API is provided by Firebase Cloud Functions. Database is provided by Firestore.
}
```
imageUrl is the URL of the uploaded image.

### Get Post
- URL: /post/:userId
- Method: GET
Expand Down Expand Up @@ -214,7 +220,9 @@ Use Create Post API to update post.
},
"userData": {
"name": "Hoge Fuga",
"streak": 0
"completed": 4,
"failed": 1,
"streak": 2
}
},
],
Expand All @@ -226,7 +234,9 @@ Use Create Post API to update post.
"text": "世界一周する",
"userData": {
"name": "Hoge Fuga",
"streak": 0
"completed": 4,
"failed": 1,
"streak": 2
}
}
],
Expand All @@ -238,7 +248,9 @@ Use Create Post API to update post.
"text": "「機械学習からマルチモーダル情報処理へ」を読む",
"userData": {
"name": "Hoge Fuga",
"streak": 0
"completed": 4,
"failed": 1,
"streak": 2
}
}
]
Expand Down
1 change: 1 addition & 0 deletions functions/src/routers/goalRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ router.post("/", async (req: Request, res: Response) => {
userId: userId,
deadline: admin.firestore.Timestamp.fromDate(new Date(deadline)),
text: text,
post: null,
});

return res
Expand Down
8 changes: 7 additions & 1 deletion functions/src/routers/resultRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import express, { Request, Response } from "express";
import admin from "firebase-admin";
import { logger } from "firebase-functions";
import { GoalWithIdAndUserData, User } from "./types";
import { countCompletedGoals, countFailedGoals } from "./userRouter";

const router = express.Router();
const db = admin.firestore();
Expand Down Expand Up @@ -69,10 +70,15 @@ const getResults = async (
let userData = userList.get(goal.userId);
if (!userData) {
const userDoc = await db.collection("user").doc(goal.userId).get();
const totalCompletedGoals = await countCompletedGoals(goal.userId);
const totalFailedGoals = await countFailedGoals(goal.userId);

userData = userDoc.data() as User;
userData = {
name: userData?.name || "Unknown user",
streak: userData?.streak || 0,
completed: totalCompletedGoals,
failed: totalFailedGoals,
streak: 0,
};
userList.set(goal.userId, userData);
}
Expand Down
6 changes: 4 additions & 2 deletions functions/src/routers/types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
export interface User {
name: string;
streak: number;
completed?: number;
failed?: number;
streak?: number;
fcmToken?: string;
}

export interface Goal {
userId: string;
deadline: Date;
text: string;
post?: Post;
post: Post | null;
}

export interface GoalWithId extends Goal {
Expand Down
94 changes: 78 additions & 16 deletions functions/src/routers/userRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ router.get("/", async (req: Request, res: Response) => {
return res.status(404).json({ message: "No users found" });
}

const userData: User[] = userSnapshot.docs.map((doc) => {
const data = doc.data();
return {
userId: doc.id,
name: data.name,
streak: data.streak,
};
});
const userData: User[] = await Promise.all(
userSnapshot.docs.map(async (doc) => {
const data = doc.data();
const totalCompletedGoals = await countCompletedGoals(doc.id);
const totalFailedGoals = await countFailedGoals(doc.id);
return {
userId: doc.id,
name: data.name,
completed: totalCompletedGoals,
failed: totalFailedGoals,
streak: data.streak,
};
})
);

return res.json(userData);
} catch (error) {
Expand All @@ -47,10 +53,14 @@ router.get("/id/:userId", async (req: Request, res: Response) => {
}

const data = userDoc.data();
const totalCompletedGoals = await countCompletedGoals(userDoc.id);
const totalFailedGoals = await countFailedGoals(userDoc.id);

const userData: User & { userId: string } = {
userId: userDoc.id,
name: data?.name || "",
completed: totalCompletedGoals,
failed: totalFailedGoals,
streak: data?.streak || 0,
};

Expand All @@ -76,14 +86,21 @@ router.get("/name/:userName", async (req: Request, res: Response) => {
return res.status(404).json({ message: "User not found" });
}

const userData: User[] = userSnapshot.docs.map((doc) => {
const data = doc.data();
return {
userId: doc.id,
name: data.name,
streak: data.streak,
};
});
const userData: User[] = await Promise.all(
userSnapshot.docs.map(async (doc) => {
const data = doc.data();
const totalCompletedGoals = await countCompletedGoals(doc.id);
const totalFailedGoals = await countFailedGoals(doc.id);

return {
userId: doc.id,
name: data.name,
completed: totalCompletedGoals,
failed: totalFailedGoals,
streak: data.streak,
};
})
);

return res.json(userData);
} catch (error) {
Expand Down Expand Up @@ -180,3 +197,48 @@ const getUserFromName = async (userName: string) => {
const getUserFromId = async (userId: string) => {
return await db.collection("user").doc(userId).get();
};

// 完了した目標をカウント(postがnullでないものをカウント)
export const countCompletedGoals = async (userId: string): Promise<number> => {
if (!userId) {
throw new Error("User ID is required");
}

try {
const countSnapshot = await db
.collection("goal")
.where("userId", "==", userId)
.where("post", "!=", null)
.count()
.get();

return countSnapshot.data().count;
} catch (error) {
logger.error("Error counting completed goals:", error);
return 0;
}
};

// 失敗した目標をカウント(今より前の`deadline`を持ち、postがnullの目標の数をカウント)
export const countFailedGoals = async (userId: string): Promise<number> => {
if (!userId) {
throw new Error("User ID is required");
}

try {
const now = admin.firestore.Timestamp.now();

const countSnapshot = await db
.collection("goal")
.where("userId", "==", userId)
.where("deadline", "<", now)
.where("post", "==", null)
.count()
.get();

return countSnapshot.data().count;
} catch (error) {
logger.error("Error counting expired goals with null post:", error);
return 0;
}
};
8 changes: 7 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"functions/src/batch.js"
],
"exclude": ["node_modules", "functions"]
}

0 comments on commit b99e7a5

Please sign in to comment.