24cd9cef53
- uni-app Vue3 frontend with dark glassmorphism theme - 5 AI dimensions: Origin / Development / Current / Learning / Trend - AI Chat system prompt updated from geometry to AI - 23 AI knowledge articles initialized in DB - Trend API + cron job for daily news - Product pricing (Pro ¥19.9, VIP ¥39.9) - Pinia stores + API utilities - AGENTS.md documentation
289 lines
6.5 KiB
JavaScript
Executable File
289 lines
6.5 KiB
JavaScript
Executable File
const { PinyinAchievement, PinyinProgress } = require('../../models/pinyin');
|
|
|
|
/**
|
|
* 获取成就列表
|
|
* GET /api/pinyin/achievements
|
|
*/
|
|
exports.getAchievements = async (req, res) => {
|
|
try {
|
|
const { type } = req.query;
|
|
|
|
const query = { isActive: true };
|
|
if (type) query.type = type;
|
|
|
|
const achievements = await PinyinAchievement.find(query)
|
|
.sort({ order: 1, createdAt: 1 })
|
|
.select('-__v');
|
|
|
|
res.json({
|
|
code: 0,
|
|
message: 'success',
|
|
data: {
|
|
list: achievements
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('获取成就列表失败:', error);
|
|
res.status(500).json({
|
|
code: 500,
|
|
message: '获取成就列表失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 获取用户已获得的成就
|
|
* GET /api/pinyin/achievements/my
|
|
*/
|
|
exports.getMyAchievements = async (req, res) => {
|
|
try {
|
|
const userId = req.user._id;
|
|
|
|
const progress = await PinyinProgress.findOne({ userId });
|
|
|
|
if (!progress || !progress.achievements || progress.achievements.length === 0) {
|
|
return res.json({
|
|
code: 0,
|
|
message: 'success',
|
|
data: {
|
|
total: 0,
|
|
list: []
|
|
}
|
|
});
|
|
}
|
|
|
|
// 获取成就详情
|
|
const achievementCodes = progress.achievements.map(a => a.code);
|
|
const achievements = await PinyinAchievement.find({
|
|
code: { $in: achievementCodes }
|
|
}).select('-__v');
|
|
|
|
// 合并获得时间
|
|
const achievementsWithTime = achievements.map(ach => {
|
|
const userAch = progress.achievements.find(a => a.code === ach.code);
|
|
return {
|
|
...ach.toObject(),
|
|
obtainedAt: userAch ? userAch.obtainedAt : null
|
|
};
|
|
});
|
|
|
|
res.json({
|
|
code: 0,
|
|
message: 'success',
|
|
data: {
|
|
total: achievementsWithTime.length,
|
|
list: achievementsWithTime
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('获取用户成就失败:', error);
|
|
res.status(500).json({
|
|
code: 500,
|
|
message: '获取用户成就失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 检查成就达成
|
|
* POST /api/pinyin/achievements/check
|
|
*/
|
|
exports.checkAchievements = async (req, res) => {
|
|
try {
|
|
const userId = req.user._id;
|
|
|
|
const progress = await PinyinProgress.findOne({ userId });
|
|
if (!progress) {
|
|
return res.json({
|
|
code: 0,
|
|
message: 'success',
|
|
data: {
|
|
newAchievements: []
|
|
}
|
|
});
|
|
}
|
|
|
|
// 获取所有启用的成就
|
|
const achievements = await PinyinAchievement.find({ isActive: true });
|
|
|
|
const newAchievements = [];
|
|
|
|
for (const achievement of achievements) {
|
|
// 检查是否已获得
|
|
if (progress.achievements.some(a => a.code === achievement.code)) {
|
|
continue;
|
|
}
|
|
|
|
// 检查条件
|
|
const isAchieved = checkCondition(progress, achievement.condition);
|
|
|
|
if (isAchieved) {
|
|
progress.achievements.push({
|
|
code: achievement.code,
|
|
obtainedAt: new Date()
|
|
});
|
|
|
|
newAchievements.push({
|
|
code: achievement.code,
|
|
name: achievement.name,
|
|
description: achievement.description,
|
|
icon: achievement.icon,
|
|
reward: achievement.reward
|
|
});
|
|
}
|
|
}
|
|
|
|
if (newAchievements.length > 0) {
|
|
await progress.save();
|
|
}
|
|
|
|
res.json({
|
|
code: 0,
|
|
message: 'success',
|
|
data: {
|
|
newAchievements
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('检查成就失败:', error);
|
|
res.status(500).json({
|
|
code: 500,
|
|
message: '检查成就失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 创建成就(管理员)
|
|
* POST /api/pinyin/achievements
|
|
*/
|
|
exports.createAchievement = async (req, res) => {
|
|
try {
|
|
const achievementData = req.body;
|
|
|
|
// 检查code是否已存在
|
|
const existing = await PinyinAchievement.findOne({ code: achievementData.code });
|
|
if (existing) {
|
|
return res.status(400).json({
|
|
code: 400,
|
|
message: '该成就代码已存在'
|
|
});
|
|
}
|
|
|
|
const achievement = new PinyinAchievement(achievementData);
|
|
await achievement.save();
|
|
|
|
res.status(201).json({
|
|
code: 0,
|
|
message: '创建成功',
|
|
data: achievement
|
|
});
|
|
} catch (error) {
|
|
console.error('创建成就失败:', error);
|
|
res.status(500).json({
|
|
code: 500,
|
|
message: '创建成就失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 更新成就(管理员)
|
|
* PUT /api/pinyin/achievements/:id
|
|
*/
|
|
exports.updateAchievement = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const updateData = req.body;
|
|
|
|
// 不允许修改code
|
|
delete updateData.code;
|
|
|
|
const achievement = await PinyinAchievement.findByIdAndUpdate(
|
|
id,
|
|
{ $set: updateData },
|
|
{ new: true, runValidators: true }
|
|
);
|
|
|
|
if (!achievement) {
|
|
return res.status(404).json({
|
|
code: 404,
|
|
message: '成就不存在'
|
|
});
|
|
}
|
|
|
|
res.json({
|
|
code: 0,
|
|
message: '更新成功',
|
|
data: achievement
|
|
});
|
|
} catch (error) {
|
|
console.error('更新成就失败:', error);
|
|
res.status(500).json({
|
|
code: 500,
|
|
message: '更新成就失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 删除成就(管理员)
|
|
* DELETE /api/pinyin/achievements/:id
|
|
*/
|
|
exports.deleteAchievement = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const achievement = await PinyinAchievement.findByIdAndDelete(id);
|
|
|
|
if (!achievement) {
|
|
return res.status(404).json({
|
|
code: 404,
|
|
message: '成就不存在'
|
|
});
|
|
}
|
|
|
|
res.json({
|
|
code: 0,
|
|
message: '删除成功'
|
|
});
|
|
} catch (error) {
|
|
console.error('删除成就失败:', error);
|
|
res.status(500).json({
|
|
code: 500,
|
|
message: '删除成就失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 检查成就条件
|
|
* @param {Object} progress - 用户进度
|
|
* @param {Object} condition - 条件
|
|
* @returns {Boolean}
|
|
*/
|
|
function checkCondition(progress, condition) {
|
|
switch (condition.type) {
|
|
case 'explore_count':
|
|
return progress.totalExplored >= condition.value;
|
|
|
|
case 'collect_count':
|
|
return progress.totalStones >= condition.value;
|
|
|
|
case 'streak_days':
|
|
return progress.streakDays >= condition.value;
|
|
|
|
case 'complete_symbol':
|
|
return progress.isSymbolCompleted(condition.symbol);
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|