Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7d46edb
Extract public variables to config
eylu Mar 20, 2026
f5f6c43
enhancement mood
eylu Mar 20, 2026
9a24cb1
consitant mood Spectrum
eylu Mar 20, 2026
c056c99
fix group mood
eylu Mar 20, 2026
d69f44b
add mood field
eylu Mar 21, 2026
44e0c57
fix mood
eylu Mar 23, 2026
ebfa728
empty puzzle
eylu Mar 23, 2026
fa683b0
我的情绪页面,分积极和不积极的数据情况
eylu Mar 23, 2026
3d2a03f
fix MomentumCard
eylu Mar 24, 2026
29db6a5
typo
eylu Mar 24, 2026
9c17453
add animation
eylu Mar 25, 2026
e82380b
add animation
eylu Mar 25, 2026
b46b6dc
typo
eylu Mar 25, 2026
0309ea7
typo
eylu Mar 25, 2026
7ceac38
open
eylu Mar 26, 2026
74cee5d
add tilt
eylu Mar 26, 2026
8e5eee9
finish release worries animation
eylu Mar 26, 2026
12354ef
remove mood reward
eylu Mar 26, 2026
20e45d3
add sentiment to mood
eylu Mar 27, 2026
792fffe
remove unuse code
eylu Mar 27, 2026
a5af712
enhancement get stat, add dailySentiment
eylu Mar 29, 2026
cedc7e0
typo
eylu Mar 29, 2026
c86bcab
fix MoodCalendar
eylu Mar 29, 2026
6064737
fix toast style
eylu Mar 29, 2026
f66e8ec
fix mood daily date error
eylu Mar 29, 2026
30ba777
fix mood daily date error
eylu Mar 29, 2026
566452a
WorryRelease component use api data
eylu Mar 29, 2026
9edd624
worry release
eylu Mar 30, 2026
5197012
fix release worry
eylu Mar 30, 2026
a0cb3a5
use new getStat
eylu Mar 30, 2026
6eb8e4e
fix mood get list
eylu Mar 30, 2026
79057a1
enhancement worry release
eylu Apr 1, 2026
b98f141
fix worry release result show
eylu Apr 1, 2026
28b8e35
typo
eylu Apr 2, 2026
7cb5e20
add puzzle cell
eylu Apr 3, 2026
fdc0e3a
finish positive puzzle
eylu Apr 3, 2026
4dc3349
完成 积极情绪兑换
eylu Apr 4, 2026
a9bd6d5
积极情绪兑换记录
eylu Apr 4, 2026
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
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ api/src/
Packages (packages/)
packages/
├── db/ Database package for schema management, migrations, client generation, and seed workflows.
├── shared/ Shared package space for cross-package utilities/types
├── shared/ Shared package space for cross-package utilities/types


禁止使用 npm, pnpm npx, yarn
只能使用 bun
145 changes: 98 additions & 47 deletions api/src/controllers/moods.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,20 @@ export const moodsController = {
return Response.json(entries);
},

// 传统的创建 mood 接口(兼容旧版)
async create(req: Request) {
const data = (await req.json()) as {
userId: string;
mood: string;
emotion?: string;
notes?: string;
recordedAt?: string;
};
async listWithoutRedeemed(req: Request){
const url = new URL(req.url);
const userId = url.searchParams.get("userId");
const moodKind = url.searchParams.get("kind");

if (!data.userId || !data.mood) {
return Response.json({ error: "userId and mood are required" }, { status: 400 });
if (!userId) {
return Response.json({ error: "userId is required" }, { status: 400 });
}

const entry = await moods.create({
userId: data.userId,
mood: data.mood,
emotion: data.emotion,
notes: data.notes,
recordedAt: data.recordedAt ? new Date(data.recordedAt) : undefined,
const entries = await moods.listWithoutRedeemed(userId, {
sentiment: moodKind || undefined
});

return Response.json(entry, { status: 201 });
return Response.json(entries);
},

// 新的 AI 情绪分析接口
Expand Down Expand Up @@ -158,28 +149,6 @@ export const moodsController = {
return Response.json(entry);
},

async update(req: Request) {
const request = req as RequestWithParams<{ id: string }>;
const data = (await req.json()) as Partial<{
mood: string;
emotion: string;
notes: string;
recordedAt: string;
}>;

const entry = await moods.findById(request.params.id);
if (!entry) {
return Response.json({ error: "Mood not found" }, { status: 404 });
}

const updated = await moods.update(request.params.id, {
...data,
recordedAt: data.recordedAt ? new Date(data.recordedAt) : undefined,
});

return Response.json(updated);
},

async getTeamStats(req: Request) {
const url = new URL(req.url);
const userId = url.searchParams.get("userId");
Expand Down Expand Up @@ -276,15 +245,97 @@ export const moodsController = {
}
},

async delete(req: Request) {
const request = req as RequestWithParams<{ id: string }>;
// 获取兑换资格
async getRedemptionEligibility(req: Request) {
const url = new URL(req.url);
const userId = url.searchParams.get("userId");

const entry = await moods.findById(request.params.id);
if (!entry) {
return Response.json({ error: "Mood not found" }, { status: 404 });
if (!userId) {
return Response.json({ error: "userId is required" }, { status: 400 });
}

try {
const eligibility = await moods.getRedemptionEligibility(userId);
return Response.json(eligibility);
} catch (error) {
console.error("Get redemption eligibility error:", error);
return Response.json(
{ error: "Failed to fetch redemption eligibility" },
{ status: 500 }
);
}
},

// 执行情绪倾倒
async dump(req: Request) {
const data = (await req.json()) as {
userId: string;
};

if (!data.userId) {
return Response.json({ error: "userId is required" }, { status: 400 });
}

await moods.delete(request.params.id);
return new Response(null, { status: 204 });
try {
const result = await moods.redeem(data.userId, 'dump');
return Response.json(result);
} catch (error) {
console.error("Dump moods error:", error);
if (error instanceof Error) {
return Response.json({ error: error.message }, { status: 400 });
}
return Response.json(
{ error: "Failed to dump moods" },
{ status: 500 }
);
}
},

// 正向情绪兑换
async reward(req: Request) {
const data = (await req.json()) as {
userId: string;
};

if (!data.userId) {
return Response.json({ error: "userId is required" }, { status: 400 });
}

try {
const result = await moods.redeem(data.userId, 'reward');
return Response.json(result);
} catch (error) {
console.error("Reward moods error:", error);
if (error instanceof Error) {
return Response.json({ error: error.message }, { status: 400 });
}
return Response.json(
{ error: "Failed to reward moods" },
{ status: 500 }
);
}
},

// 获取兑换历史
async getRedemptionHistory(req: Request) {
const url = new URL(req.url);
const userId = url.searchParams.get("userId");
const limit = parseInt(url.searchParams.get("limit") || "10", 10);
const offset = parseInt(url.searchParams.get("offset") || "0", 10);

if (!userId) {
return Response.json({ error: "userId is required" }, { status: 400 });
}

try {
const history = await moods.getRedemptionHistory(userId, { limit, offset });
return Response.json(history);
} catch (error) {
console.error("Get redemption history error:", error);
return Response.json(
{ error: "Failed to fetch redemption history" },
{ status: 500 }
);
}
},
};
18 changes: 15 additions & 3 deletions api/src/routes/moods.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { moodsController } from "../controllers/index.js";
export const moodsRoutes = {
"/api/moods": {
GET: moodsController.list,
POST: moodsController.create,
},
"/api/moods_without_redeemed": {
GET: moodsController.listWithoutRedeemed,
},
"/api/moods/analyze": {
POST: moodsController.analyze,
Expand All @@ -29,9 +31,19 @@ export const moodsRoutes = {
"/api/moods/team-insights": {
GET: moodsController.getTeamInsights,
},
"/api/moods/redemption-eligibility": {
GET: moodsController.getRedemptionEligibility,
},
"/api/moods/dump": {
POST: moodsController.dump,
},
"/api/moods/redemption-history": {
GET: moodsController.getRedemptionHistory,
},
"/api/moods/reward": {
POST: moodsController.reward,
},
"/api/moods/:id": {
GET: moodsController.getById,
PUT: moodsController.update,
DELETE: moodsController.delete,
},
};
2 changes: 1 addition & 1 deletion api/src/services/moodAnalysis.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const emotionSpectrum = {
label: "快乐",
color: "#F97316", // orange-500
bgGradient: "from-amber-400 to-orange-600",
icon: "restaurant",
icon: "sentiment_very_satisfied",
container: "candy",
},
achievement: {
Expand Down
20 changes: 10 additions & 10 deletions api/src/services/moods.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface TeamMoodStats {
}

export interface TeamMoodDistribution {
emotion: string;
mood: string;
count: number;
percentage: number;
}
Expand Down Expand Up @@ -58,7 +58,7 @@ export async function analyzeAndCreateMood(
const analysis = await analyzeEmotion(input);

// 保存到数据库
const mood = await moods.create({
const mood = await moods.createWithSummary({
userId,
mood: analysis.spectrum, // 使用 spectrum 作为 mood 值
emotion: analysis.emotion, // 具体情绪描述
Expand Down Expand Up @@ -131,12 +131,12 @@ function getIconForSpectrum(
boredom: "hourglass_empty",
anxiety: "cloud",
anger: "local_fire_department",
joy: "restaurant",
joy: "sentiment_very_satisfied",
achievement: "star",
warmth: "lightbulb",
calm: "eco",
};
return iconMap[spectrum] || "sentiment_satisfied";
return iconMap[spectrum] || "sentiment_very_satisfied";
}

/**
Expand Down Expand Up @@ -198,7 +198,7 @@ export async function getTeamStats(

// 获取主要情绪
const emotionCounts = recentMoods.reduce((acc, m) => {
const emotion = m.emotion || m.mood;
const emotion = m.mood || m.emotion;
acc[emotion] = (acc[emotion] || 0) + 1;
return acc;
}, {} as Record<string, number>);
Expand Down Expand Up @@ -240,14 +240,14 @@ export async function getTeamDistribution(
const recentMoods = moodsList.filter(m => new Date(m.recordedAt) >= cutoffDate);

const emotionCounts = recentMoods.reduce((acc, m) => {
const emotion = m.emotion || m.mood;
acc[emotion] = (acc[emotion] || 0) + 1;
const mood = m.mood || m.emotion;
acc[mood] = (acc[mood] || 0) + 1;
return acc;
}, {} as Record<string, number>);

const total = recentMoods.length;
const distribution = Object.entries(emotionCounts).map(([emotion, count]) => ({
emotion,
const distribution = Object.entries(emotionCounts).map(([mood, count]) => ({
mood,
count,
percentage: Math.round((count / total) * 100),
}));
Expand Down Expand Up @@ -405,4 +405,4 @@ function getMoodValue(mood: string): number {
frustrated: 1,
};
return moodMap[mood.toLowerCase()] || 3;
}
}
2 changes: 1 addition & 1 deletion design/group_home.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ <h3 class="text-indigo-900 dark:text-indigo-200 font-extrabold text-xl">Mood Cor
<div class="flex gap-4 mb-8">
<div class="flex-1 bg-white/60 dark:bg-slate-900/40 backdrop-blur p-3 rounded-xl border border-white dark:border-slate-700 flex flex-col items-center gap-2 text-center group/item cursor-pointer hover:bg-white dark:hover:bg-slate-800 transition-all">
<div class="size-12 rounded-full bg-orange-100 dark:bg-orange-900/30 flex items-center justify-center text-orange-500">
<span class="material-symbols-outlined text-3xl">restaurant</span>
<span class="material-symbols-outlined text-3xl">sentiment_very_satisfied</span>
</div>
<span class="text-[10px] font-bold uppercase text-orange-600 dark:text-orange-400">Candy Jar</span>
<p class="text-[9px] text-slate-500 dark:text-slate-400">Store joy</p>
Expand Down
Loading