This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
const { Context, Vocab } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
/**
|
||||
* Context Controller - Workflow-based status management
|
||||
@@ -11,7 +12,7 @@ class ContextController {
|
||||
*/
|
||||
async createContext(req, res, next) {
|
||||
try {
|
||||
const { title, desc, grade, type, type_image , reference_id } = req.body;
|
||||
const { title, desc, grade, type, type_image, reference_id } = req.body;
|
||||
|
||||
// Validate required fields
|
||||
if (!title || !desc || !grade) {
|
||||
@@ -58,18 +59,96 @@ class ContextController {
|
||||
async getContextsByStatus(req, res, next) {
|
||||
try {
|
||||
const { status } = req.params;
|
||||
const { page = 1, limit = 50, type, grade } = req.query;
|
||||
const { page = 1, limit = 50, type, grade, title, date, from, to, sort } = req.query;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const where = { status: parseInt(status) };
|
||||
if (type) where.type = type;
|
||||
if (grade) where.grade = parseInt(grade);
|
||||
if (title) where.title = { [Op.like]: `%${title}%` };
|
||||
|
||||
// Hàm helper để chuẩn hóa và parse date cho dải thời gian
|
||||
const parseDateRange = (dStr, isEndDate = false) => {
|
||||
if (!dStr) return null;
|
||||
let s = String(dStr).replace('_', 'T').replace(' ', 'T');
|
||||
if (s.includes(':') && !s.includes('Z') && !s.match(/[+-]\d{2}:?\d{2}$/)) {
|
||||
s += 'Z';
|
||||
}
|
||||
const d = new Date(s);
|
||||
if (isNaN(d.getTime())) return null;
|
||||
if (!s.includes(':') && isEndDate) {
|
||||
d.setHours(23, 59, 59, 999);
|
||||
}
|
||||
return d;
|
||||
};
|
||||
|
||||
if (from || to) {
|
||||
where.updated_at = {};
|
||||
if (from) {
|
||||
const startDate = parseDateRange(from);
|
||||
if (startDate) where.updated_at[Op.gte] = startDate;
|
||||
}
|
||||
if (to) {
|
||||
const endDate = parseDateRange(to, true);
|
||||
if (endDate) where.updated_at[Op.lte] = endDate;
|
||||
}
|
||||
} else if (date) {
|
||||
// Chuẩn hóa chuỗi ngày tháng: thay '_' hoặc khoảng cách bằng 'T' để dễ xử lý
|
||||
let dateString = String(date).replace('_', 'T').replace(' ', 'T');
|
||||
|
||||
// Nếu chuỗi có giờ phút (có dấu :) nhưng chưa có múi giờ (Z hoặc +/-)
|
||||
// Ta mặc định là giờ UTC để khớp chính xác với những gì người dùng thấy trong DB
|
||||
if (dateString.includes(':') && !dateString.includes('Z') && !dateString.match(/[+-]\d{2}:?\d{2}$/)) {
|
||||
dateString += 'Z';
|
||||
}
|
||||
|
||||
const searchDate = new Date(dateString);
|
||||
|
||||
if (!isNaN(searchDate.getTime())) {
|
||||
// Kiểm tra xem có dấu ':' (có giờ phút) không
|
||||
const hasTime = dateString.includes(':');
|
||||
|
||||
if (hasTime) {
|
||||
// Kiểm tra xem có cung cấp đến giây không (vd: 09:08:18 -> 3 phần)
|
||||
const isSecondsProvided = dateString.split(':').length === 3;
|
||||
|
||||
if (isSecondsProvided) {
|
||||
// Tìm chính xác giây đó (dải 1000ms)
|
||||
const startTime = Math.floor(searchDate.getTime() / 1000) * 1000;
|
||||
const startRange = new Date(startTime);
|
||||
const endRange = new Date(startTime + 1000);
|
||||
where.updated_at = { [Op.gte]: startRange, [Op.lt]: endRange };
|
||||
} else {
|
||||
// Chỉ có giờ:phút, tìm trong cả phút đó
|
||||
const startTime = Math.floor(searchDate.getTime() / 60000) * 60000;
|
||||
const startRange = new Date(startTime);
|
||||
const endRange = new Date(startTime + 60000);
|
||||
where.updated_at = { [Op.gte]: startRange, [Op.lt]: endRange };
|
||||
}
|
||||
} else {
|
||||
// Nếu chỉ có ngày (vd: 2026-02-28), tìm cả ngày theo giờ server
|
||||
const startOfDay = new Date(searchDate);
|
||||
startOfDay.setHours(0, 0, 0, 0);
|
||||
const endOfDay = new Date(searchDate);
|
||||
endOfDay.setHours(23, 59, 59, 999);
|
||||
where.updated_at = { [Op.gte]: startOfDay, [Op.lte]: endOfDay };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort order
|
||||
let order = [['title', 'ASC']]; // default alphabet
|
||||
if (sort === 'created_at') {
|
||||
order = [['created_at', 'DESC']];
|
||||
} else if (sort === 'updated_at') {
|
||||
order = [['updated_at', 'DESC']];
|
||||
}
|
||||
|
||||
const { count, rows } = await Context.findAndCountAll({
|
||||
where,
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset),
|
||||
order: [['created_at', 'DESC']]
|
||||
order
|
||||
});
|
||||
|
||||
res.json({
|
||||
@@ -308,8 +387,8 @@ class ContextController {
|
||||
const updatedImagesSquare = currentVocab.image_square || [];
|
||||
updatedImagesSquare.push(context.image);
|
||||
await currentVocab.update({ image_square: updatedImagesSquare });
|
||||
} else if (context.type_image === 'normal') {
|
||||
const updatedImagesNormal = currentVocab.image_normal || [];
|
||||
} else if (context.type_image === 'normal') {
|
||||
const updatedImagesNormal = currentVocab.image_normal || [];
|
||||
updatedImagesNormal.push(context.image);
|
||||
await currentVocab.update({ image_normal: updatedImagesNormal });
|
||||
}
|
||||
@@ -416,11 +495,11 @@ class ContextController {
|
||||
type,
|
||||
status,
|
||||
grade,
|
||||
reference_id,
|
||||
page = 1,
|
||||
limit = 50
|
||||
} = req.body;
|
||||
|
||||
const { Op } = require('sequelize');
|
||||
const offset = (page - 1) * limit;
|
||||
const where = {};
|
||||
|
||||
@@ -430,7 +509,8 @@ class ContextController {
|
||||
}
|
||||
if (type) where.type = type;
|
||||
if (status !== undefined && status !== null) where.status = parseInt(status);
|
||||
if (grade !== undefined && grade !== null) where.grade = parseInt(grade);
|
||||
if (grade !== undefined && grade !== null) where.grade = parseInt(grade);
|
||||
if (reference_id) where.reference_id = reference_id;
|
||||
|
||||
// ── Text search ──────────────────────────────────────────────────────
|
||||
// `search` → title OR context (cả hai cùng lúc)
|
||||
@@ -440,7 +520,7 @@ class ContextController {
|
||||
|
||||
if (search) {
|
||||
textConditions.push(
|
||||
{ title: { [Op.like]: `%${search}%` } },
|
||||
{ title: { [Op.like]: `%${search}%` } },
|
||||
{ context: { [Op.like]: `%${search}%` } }
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user