Update files

This commit is contained in:
glpshchn 2025-12-07 06:14:27 +03:00
parent aeae68b39e
commit d85ebdfe01
5 changed files with 83 additions and 1 deletions

View File

@ -56,6 +56,10 @@ const PostSchema = new mongoose.Schema({
type: Boolean, type: Boolean,
default: false default: false
}, },
isArt: {
type: Boolean,
default: false
},
likes: [{ likes: [{
type: mongoose.Schema.Types.ObjectId, type: mongoose.Schema.Types.ObjectId,
ref: 'User' ref: 'User'

View File

@ -43,6 +43,7 @@ const TicketActivitySchema = new mongoose.Schema({
type: mongoose.Schema.Types.Mixed, type: mongoose.Schema.Types.Mixed,
default: {} default: {}
}, },
artsModerated: { type: Number, default: 0 }, // Количество артов, прошедших модерацию
createdAt: { createdAt: {
type: Date, type: Date,
default: Date.now default: Date.now

View File

@ -173,6 +173,7 @@ router.get('/posts', authenticateModeration, requireModerationAccess, async (req
commentsCount: post.comments?.length || 0, commentsCount: post.comments?.length || 0,
likesCount: post.likes?.length || 0, likesCount: post.likes?.length || 0,
isNSFW: post.isNSFW, isNSFW: post.isNSFW,
isArt: post.isArt,
publishedToChannel: post.publishedToChannel, publishedToChannel: post.publishedToChannel,
adminNumber: post.adminNumber, adminNumber: post.adminNumber,
editedAt: post.editedAt, editedAt: post.editedAt,
@ -245,7 +246,7 @@ router.delete('/posts/:postId/comments/:commentId', authenticateModeration, requ
}); });
router.put('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => { router.put('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
const { content, hashtags, tags, isNSFW } = req.body; const { content, hashtags, tags, isNSFW, isArt } = req.body;
const post = await Post.findById(req.params.id).populate('author'); const post = await Post.findById(req.params.id).populate('author');
if (!post) { if (!post) {
@ -285,6 +286,18 @@ router.put('/posts/:id', authenticateModeration, requireModerationAccess, async
post.isNSFW = !!isNSFW; post.isNSFW = !!isNSFW;
} }
// Обработка метки "арт"
const wasArt = post.isArt || false;
if (isArt !== undefined) {
post.isArt = !!isArt;
// Если арт только что помечен (было false, стало true), начислить билеты
if (!wasArt && post.isArt && post.author) {
const { awardArtModeration } = require('../utils/tickets');
await awardArtModeration(post.author._id);
}
}
post.editedAt = new Date(); post.editedAt = new Date();
await post.save(); await post.save();
@ -312,6 +325,7 @@ router.put('/posts/:id', authenticateModeration, requireModerationAccess, async
tags: post.tags, tags: post.tags,
images: post.images, images: post.images,
isNSFW: post.isNSFW, isNSFW: post.isNSFW,
isArt: post.isArt,
publishedToChannel: post.publishedToChannel, publishedToChannel: post.publishedToChannel,
adminNumber: post.adminNumber, adminNumber: post.adminNumber,
editedAt: post.editedAt, editedAt: post.editedAt,

View File

@ -205,6 +205,55 @@ async function awardArtComment(authorId, commenterId, postId) {
}); });
} }
/**
* Начисляет билеты за арт, прошедший модерацию
*/
async function awardArtModeration(userId) {
// Проверяем лимиты: 1 арт в день / 5 в неделю
const { getMoscowStartOfDay } = require('./moscowTime');
const today = getMoscowStartOfDay();
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
const activity = await TicketActivity.getOrCreateToday(userId);
// Проверка лимита: 1 арт в день
const todayArts = activity.artsModerated || 0;
if (todayArts >= 1) {
console.log(`[Tickets ${formatMoscowTime()}] Лимит артов на сегодня достигнут для пользователя ${userId}`);
return { success: false, reason: 'daily_limit_reached' };
}
// Проверка лимита: 5 артов в неделю
const TicketActivityModel = require('../models/TicketActivity');
const weekActivities = await TicketActivityModel.find({
user: userId,
date: { $gte: weekAgo }
});
const weekArtsCount = weekActivities.reduce((sum, act) => {
return sum + (act.artsModerated || 0);
}, 0);
if (weekArtsCount >= 5) {
console.log(`[Tickets ${formatMoscowTime()}] Лимит артов на неделю достигнут для пользователя ${userId}`);
return { success: false, reason: 'weekly_limit_reached' };
}
// Начислить билеты
if (!activity.artsModerated) {
activity.artsModerated = 0;
}
activity.artsModerated += 1;
await activity.save();
await User.findByIdAndUpdate(userId, {
$inc: { tickets: 40 }
});
console.log(`[Tickets ${formatMoscowTime()}] Начислено 40 билетов пользователю ${userId} за арт, прошедший модерацию`);
return { success: true, points: 40 };
}
/** /**
* Списывает билеты за действие (лайк/комментарий) с удаленным постом * Списывает билеты за действие (лайк/комментарий) с удаленным постом
*/ */
@ -373,6 +422,7 @@ module.exports = {
awardReferral, awardReferral,
awardArtLike, awardArtLike,
awardArtComment, awardArtComment,
awardArtModeration,
deductPostCreation, deductPostCreation,
deductPostDeletion, deductPostDeletion,
deductAction, deductAction,

View File

@ -422,6 +422,12 @@ export default function App() {
loadUsers(); loadUsers();
}; };
const handleToggleArt = async (post) => {
const newIsArt = !post.isArt;
await updatePost(post.id, { isArt: newIsArt });
loadPosts();
};
const handleOpenComments = async (postId) => { const handleOpenComments = async (postId) => {
setCommentsLoading(true); setCommentsLoading(true);
try { try {
@ -631,6 +637,13 @@ export default function App() {
<Edit size={16} /> <Edit size={16} />
Редактировать Редактировать
</button> </button>
<button
className={`btn ${post.isArt ? 'active' : ''}`}
onClick={() => handleToggleArt(post)}
style={post.isArt ? { backgroundColor: '#4CAF50', color: 'white' } : {}}
>
🎨 Арт
</button>
<button className="btn danger" onClick={() => handlePostDelete(post.id)}> <button className="btn danger" onClick={() => handlePostDelete(post.id)}>
<Trash2 size={16} /> <Trash2 size={16} />
Удалить Удалить