Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
custom_order.class
custom_order.class
node_modules
uploads
npm-debug.log*
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
# Introduction to Java

This your copy of the code for the java-course. The course itself is located in the issues of this repository.

## Love Story Backend (Node + TypeScript)

A lightweight Express API that powers the couple-themed front-end shown in the mockups. It includes photo uploads, chat history with playful bot replies, preset hero/banner text, love-letter storage, and simple game card metadata.

### Getting started
1. Install dependencies:
```bash
npm install
```
2. Start the development server (watch mode with TypeScript):
```bash
npm run dev
```
3. Or build and run the compiled server:
```bash
npm run build
npm start
```

The API listens on `http://localhost:8080` by default and serves uploaded photos from `/uploads`.

### Key endpoints
- `GET /api/health` – health probe.
- `GET /api/hero` – banner names, subtitle, and badge text.
- `GET /api/stats` – summary counts for love days, photos, chats, and letter date.
- `GET /api/game-cards` – metadata for the three game cards.
- `GET /api/memories` – list of uploaded photos (in memory during runtime).
- `POST /api/memories` – upload a photo (`photo` field) with optional `caption`.
- `GET /api/chat/history` – conversation history.
- `POST /api/chat/message` – send a message and receive a contextual bot reply.
- `GET /api/love-letter` / `PUT /api/love-letter` – retrieve or update the shared letter.

Uploads are stored on disk under `uploads/` (created automatically) and served as static files. The service keeps data in memory for simplicity; restart the server to reset state.
23 changes: 23 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "java-course-backend",
"version": "1.0.0",
"description": "Backend service to support the TypeScript front-end experience",
"main": "dist/server.js",
"scripts": {
"build": "tsc",
"start": "node dist/server.js",
"dev": "ts-node src/server.ts"
},
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"multer": "^1.4.5-lts.1"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/multer": "^1.4.7",
"@types/node": "^20.10.5",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
}
214 changes: 214 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import cors from 'cors';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add missing CORS type definitions

TypeScript imports cors but package.json does not declare @types/cors, so running npm run build (tsc) or npm run dev (ts-node) will error with TS7016 that it cannot find declarations for cors, blocking both the compiled and dev server entrypoints; include the type package or adjust the import to restore builds.

Useful? React with 👍 / 👎.

import express, { Request, Response } from 'express';
import fs from 'fs';
import multer from 'multer';
import path from 'path';

// Simple in-memory stores that mimic persistent data.
const memories: Memory[] = [];
const chatHistory: ChatMessage[] = [];
const loveLetter: LoveLetter = {
author: '宗睿',
recipient: '代琪',
title: '致 代琪',
content:
'亲爱的:\n\n感谢你成为我生活中的“专属温暖”。无论平静的午后,还是喧嚣的夜晚,我都想第一时间把喜悦和烦恼告诉你。\n\n这份网站想记录我们“回忆、美食、游戏、聊天”的每一个瞬间,它不只是一份作业,更是我们的数字小窝。希望未来有更多的故事被放进这里。\n\n愿你开心、健康,也愿我始终有勇气守护你。\n\n爱你的,宗睿',
date: new Date().toISOString()
};

interface Memory {
id: string;
fileName: string;
caption?: string;
createdAt: string;
}

interface ChatMessage {
id: string;
sender: 'user' | 'bot';
text: string;
createdAt: string;
}

interface LoveLetter {
author: string;
recipient: string;
title: string;
content: string;
date: string;
}

interface GameCard {
id: string;
title: string;
description: string;
action: string;
}

const app = express();
const port = process.env.PORT || 8080;
const uploadDir = path.join(__dirname, '..', 'uploads');

if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}

const storage = multer.diskStorage({
destination: (_req, _file, cb) => cb(null, uploadDir),
filename: (_req, file, cb) => {
const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e6)}`;
cb(null, `${uniqueSuffix}${path.extname(file.originalname)}`);
}
});

const upload = multer({ storage });

app.use(cors());
app.use(express.json());
app.use('/uploads', express.static(uploadDir));

app.get('/api/health', (_req: Request, res: Response) => {
res.json({ status: 'ok' });
});

app.get('/api/game-cards', (_req: Request, res: Response) => {
const cards: GameCard[] = [
{
id: 'hearts',
title: '爱情默契卡',
description: '10道甜蜜小问答,测试彼此的默契值并生成专属徽章。',
action: '开始'
},
{
id: 'puzzle',
title: '爱的拼图游戏',
description: '上传一张照片,自动生成 3x3 拼图,限时完成挑战。',
action: '组拼'
},
{
id: 'memory',
title: '甜蜜照片墙',
description: '把我们的照片、合影、旅行纪念都上传成相册墙。',
action: '看回忆'
}
];

res.json(cards);
});

app.get('/api/memories', (_req: Request, res: Response) => {
res.json(memories);
});

app.post('/api/memories', upload.single('photo'), (req: Request, res: Response) => {
if (!req.file) {
return res.status(400).json({ error: '缺少图片文件' });
}

const memory: Memory = {
id: `${Date.now()}`,
fileName: `/uploads/${req.file.filename}`,
caption: req.body.caption,
createdAt: new Date().toISOString()
};

memories.unshift(memory);
res.status(201).json(memory);
});

app.get('/api/chat/history', (_req: Request, res: Response) => {
res.json(chatHistory);
});

app.post('/api/chat/message', (req: Request, res: Response) => {
const text: string | undefined = req.body?.text;

if (!text || !text.trim()) {
return res.status(400).json({ error: '消息内容不能为空' });
}

const userMessage: ChatMessage = {
id: `${Date.now()}-user`,
sender: 'user',
text: text.trim(),
createdAt: new Date().toISOString()
};

chatHistory.push(userMessage);

const botMessage: ChatMessage = {
id: `${Date.now()}-bot`,
sender: 'bot',
text: buildBotReply(text),
createdAt: new Date().toISOString()
};

chatHistory.push(botMessage);

res.status(201).json({ userMessage, botMessage });
});

app.get('/api/love-letter', (_req: Request, res: Response) => {
res.json(loveLetter);
});

app.put('/api/love-letter', (req: Request, res: Response) => {
const { author, recipient, title, content } = req.body;

if (!author || !recipient || !title || !content) {
return res.status(400).json({ error: '请填写完整的信件信息' });
}

loveLetter.author = author;
loveLetter.recipient = recipient;
loveLetter.title = title;
loveLetter.content = content;
loveLetter.date = new Date().toISOString();

res.json(loveLetter);
});

app.get('/api/hero', (_req: Request, res: Response) => {
res.json({
primaryName: '樊宗睿',
secondaryName: '代琪',
subtitle: '成为你的可爱,是我学会坚强的魔法',
badge: '恋爱日记'
});
});

app.get('/api/stats', (_req: Request, res: Response) => {
res.json({
loveDays: 288,
photosCount: memories.length,
chatCount: chatHistory.length,
letterDate: loveLetter.date
});
});

app.use((err: Error, _req: Request, res: Response, _next: () => void) => {
console.error(err);
res.status(500).json({ error: '服务器开小差了,请稍后再试' });
});

app.listen(port, () => {
console.log(`Love story backend is running on http://localhost:${port}`);
});

function buildBotReply(message: string): string {
const normalized = message.toLowerCase();

if (normalized.includes('开心') || normalized.includes('快乐')) {
return '听到你开心我也很开心,我们去吃好吃的庆祝一下吧!';
}

if (normalized.includes('难过') || normalized.includes('累')) {
return '抱抱你,累了就歇一会儿,我来给你煮奶茶,一起慢慢聊~';
}

if (normalized.includes('晚安')) {
return '晚安好梦,做个甜甜的梦,梦到我!';
}

return '收到!我一直在这儿陪你,告诉我更多细节,我们一起想办法。';
}
13 changes: 13 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"rootDir": "src",
"outDir": "dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}