feat(materials): Фаза 6b — раздатка материала ученикам/классу
- POST /api/materials/:id/share {classId|userId} (teacher/admin): создаёт независимую КОПИЮ
материала каждому ученику класса (source_title «Раздатка: <учитель>») + уведомление через SSE.
- /my-materials: кнопка «Раздать» на карточках (видна учителю/админу) → выбор класса.
- Хелпер LS.shareMaterial. На этом план «Мои материалы» (6 фаз) завершён.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
* A user keeps copies of items saved from live lessons; the copies are
|
||||
* independent of the session lifecycle. */
|
||||
const db = require('../db/db');
|
||||
const { emit } = require('../sse');
|
||||
|
||||
const KINDS = ['board', 'note', 'link', 'image'];
|
||||
|
||||
@@ -129,4 +130,44 @@ function deleteCollection(req, res) {
|
||||
res.json({ ok: true });
|
||||
}
|
||||
|
||||
module.exports = { list, create, update, remove, createCollection, updateCollection, deleteCollection };
|
||||
/* POST /api/materials/:id/share — teacher hands a material out to a class or
|
||||
a student. Each recipient gets an independent COPY (survives later edits/
|
||||
deletes by the teacher). Body: { classId } | { userId }. */
|
||||
function share(req, res) {
|
||||
const mat = db.prepare('SELECT user_id, kind, title, body, url FROM student_materials WHERE id = ?').get(req.params.id);
|
||||
if (!mat) return res.status(404).json({ error: 'not found' });
|
||||
if (mat.user_id !== req.user.id && req.user.role !== 'admin') return res.status(403).json({ error: 'forbidden' });
|
||||
|
||||
const b = req.body || {};
|
||||
let recipients = [];
|
||||
if (b.classId) {
|
||||
const cls = db.prepare('SELECT id, teacher_id FROM classes WHERE id = ?').get(b.classId);
|
||||
if (!cls) return res.status(404).json({ error: 'class not found' });
|
||||
if (cls.teacher_id !== req.user.id && req.user.role !== 'admin') return res.status(403).json({ error: 'not your class' });
|
||||
recipients = db.prepare('SELECT user_id FROM class_members WHERE class_id = ?').all(b.classId).map(r => r.user_id);
|
||||
} else if (b.userId) {
|
||||
const uid = Number(b.userId);
|
||||
if (Number.isFinite(uid)) recipients = [uid];
|
||||
} else {
|
||||
return res.status(400).json({ error: 'classId or userId required' });
|
||||
}
|
||||
|
||||
const teacherName = (db.prepare('SELECT name FROM users WHERE id = ?').get(req.user.id) || {}).name || 'Учитель';
|
||||
const srcTitle = 'Раздатка: ' + teacherName;
|
||||
const ins = db.prepare(`INSERT INTO student_materials (user_id, kind, title, body, url, source_session_id, source_title) VALUES (?,?,?,?,?,NULL,?)`);
|
||||
let sent = 0;
|
||||
db.transaction(() => {
|
||||
for (const uid of recipients) {
|
||||
if (!uid || uid === req.user.id) continue;
|
||||
ins.run(uid, mat.kind, mat.title, mat.body, mat.url, srcTitle);
|
||||
try {
|
||||
emit(uid, { type: 'notification', notif_type: 'material_shared',
|
||||
message: `Новый материал от ${teacherName}: «${mat.title || 'материал'}»`, link: '/my-materials' });
|
||||
} catch (e) { /* ignore notify failure */ }
|
||||
sent++;
|
||||
}
|
||||
})();
|
||||
res.json({ ok: true, sent });
|
||||
}
|
||||
|
||||
module.exports = { list, create, update, remove, createCollection, updateCollection, deleteCollection, share };
|
||||
|
||||
Reference in New Issue
Block a user