Untitled
unknown
plain_text
a year ago
6.9 kB
11
Indexable
"use strict";
const httpStatus = require("http-status");
const ApiError = require("../../utils/ApiError");
const ExcelJS = require("exceljs");
const RoadSourceBottomUpPollutantModel = require("../models/roadSourceBottomUpPollutant.model");
class RoadSourceBottomUpPollutantService {
/**
* Service: Get list of Road Source Bottom-Up Pollutants with pagination
*
* @version: 1.0
* @param {Object} params
* @param {number} params.page
* @param {number} params.limit
* @returns {Promise<Object>}
*/
async getListRoadSourceBottomUpPollutant({ page, limit }) {
const skip = (page - 1) * limit;
const totalRecords = await RoadSourceBottomUpPollutantModel.countDocuments();
const totalPages = Math.ceil(totalRecords / limit);
const records = await RoadSourceBottomUpPollutantModel.find({})
.skip(skip)
.limit(limit)
.sort({ createdAt: -1 })
.lean();
const data = records.map((r) => ({
_id: r._id,
name: r.name,
createdAt: r.createdAt,
updatedAt: r.updatedAt,
}));
return { currentPage: page, totalPages, totalRecords, limit, data };
}
/**
* Service: Create a new pollutant
*
* @version: 1.0
* @param {{ name: string }} payload
* @returns {Promise<Object>}
*/
async createRoadSourceBottomUpPollutant(payload) {
const name = (payload.name || "").trim();
if (!name) {
throw new ApiError(httpStatus.BAD_REQUEST, "Name is required.");
}
const existing = await RoadSourceBottomUpPollutantModel.findOne({
name: new RegExp(`^${name}$`, "i"),
}).lean();
if (existing) {
throw new ApiError(httpStatus.CONFLICT, "Pollutant already exists.");
}
const doc = await RoadSourceBottomUpPollutantModel.create({ name });
return {
_id: doc._id,
name: doc.name,
createdAt: doc.createdAt,
updatedAt: doc.updatedAt,
};
}
/**
* Service: Get pollutant by ID
*
* @version: 1.0
* @param {string} id
* @returns {Promise<Object>}
*/
async getRoadSourceBottomUpPollutantById(id) {
const record = await RoadSourceBottomUpPollutantModel.findById(id).lean();
if (!record) {
throw new ApiError(httpStatus.NOT_FOUND, "Pollutant not found.");
}
return {
_id: record._id,
name: record.name,
createdAt: record.createdAt,
updatedAt: record.updatedAt,
};
}
/**
* Service: Update pollutant name by ID
*
* @version: 1.0
* @param {string} id
* @param {string} name
* @returns {Promise<Object>}
*/
async updateRoadSourceBottomUpPollutantById(id, name) {
const newName = (name || "").trim();
if (!newName) {
throw new ApiError(httpStatus.BAD_REQUEST, "Name is required.");
}
const record = await RoadSourceBottomUpPollutantModel.findById(id).lean();
if (!record) {
throw new ApiError(httpStatus.NOT_FOUND, "Pollutant not found.");
}
const clash = await RoadSourceBottomUpPollutantModel.findOne({
_id: { $ne: id },
name: new RegExp(`^${newName}$`, "i"),
}).lean();
if (clash) {
throw new ApiError(httpStatus.CONFLICT, "Another pollutant with this name already exists.");
}
const updated = await RoadSourceBottomUpPollutantModel.findByIdAndUpdate(
id,
{ name: newName },
{ new: true, runValidators: true }
).lean();
return {
_id: updated._id,
name: updated.name,
createdAt: updated.createdAt,
updatedAt: updated.updatedAt,
};
}
/**
* Service: Delete pollutant by ID
*
* @version: 1.0
* @param {string} id
* @returns {Promise<void>}
*/
async deleteRoadSourceBottomUpPollutantById(id) {
const deleted = await RoadSourceBottomUpPollutantModel.findByIdAndDelete(id);
if (!deleted) {
throw new ApiError(httpStatus.NOT_FOUND, "Pollutant not found.");
}
}
/**
* Service: Import pollutants from Excel
* - Read sheet 1
* - Column A = name
* - Skip header (start from row 2)
* - Stop at first completely empty row
* - Case-insensitive dedupe; unordered bulk upsert
*
* @version: 1.0
* @param {object} file - The uploaded Excel file (with `buffer`)
* @returns {Promise<{added:number, skipped:number, processed:number}>}
*/
async importRoadSourceBottomUpPollutant(file) {
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.load(file.buffer);
const worksheet = workbook.getWorksheet(1);
if (!worksheet) {
throw new ApiError(httpStatus.BAD_REQUEST, "Invalid Excel file: No worksheet found.");
}
const existing = await RoadSourceBottomUpPollutantModel.find()
.select("_id name")
.lean();
const existingSet = new Set(
existing.map((d) => String(d.name || "").trim().toLowerCase())
);
const operations = [];
const batchSize = 200;
let added = 0;
let skipped = 0;
let processed = 0;
for (let i = 2; i <= worksheet.rowCount; i++) {
const row = worksheet.getRow(i);
const nameRaw = row.getCell(1).value; // column A
const isEmpty = nameRaw == null || String(nameRaw).trim() === "";
if (isEmpty) break;
processed++;
const name = String(nameRaw).trim();
const key = name.toLowerCase();
if (!name) {
skipped++;
continue;
}
if (existingSet.has(key)) {
skipped++;
continue;
}
operations.push({
updateOne: {
filter: { name: new RegExp(`^${name}$`, "i") },
update: { $setOnInsert: { name } },
upsert: true,
},
});
existingSet.add(key);
added++;
if (operations.length >= batchSize) {
await RoadSourceBottomUpPollutantModel.bulkWrite(operations, { ordered: false });
operations.length = 0;
}
}
if (operations.length > 0) {
await RoadSourceBottomUpPollutantModel.bulkWrite(operations, { ordered: false });
}
return { added, skipped, processed };
}
}
module.exports = new RoadSourceBottomUpPollutantService();Editor is loading...
Leave a Comment