update
This commit is contained in:
@ -1,31 +1,32 @@
|
|||||||
const mysql = require('mysql');
|
require("dotenv").config({ path: require("path").resolve(__dirname, "../.env") });
|
||||||
|
|
||||||
|
const mysql = require("mysql");
|
||||||
const pool = mysql.createPool({
|
const pool = mysql.createPool({
|
||||||
connectionLimit: process.env.CONNECTIONLIMIT,
|
connectionLimit: process.env.CONNECTIONLIMIT,
|
||||||
host: process.env.DBHOST,
|
host: process.env.DBHOST,
|
||||||
port: process.env.DBPORT,
|
port: process.env.DBPORT,
|
||||||
user: process.env.DBUSER,
|
user: process.env.DBUSER,
|
||||||
password: process.env.DBPASSWORD,
|
password: process.env.DBPASSWORD,
|
||||||
database: process.env.DATABASE,
|
database: process.env.DATABASE,
|
||||||
acquireTimeout: Number(process.env.ACQRTIMEOUT), // in ms
|
acquireTimeout: Number(process.env.ACQRTIMEOUT), // in ms
|
||||||
});
|
});
|
||||||
|
|
||||||
pool.getConnection((err, conn) => {
|
pool.getConnection((err, conn) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.code === 'PROTOCOL_CONNECTION_LOST') {
|
if (err.code === "PROTOCOL_CONNECTION_LOST") {
|
||||||
console.error('Koneksi database ditutup.');
|
console.error("Koneksi database ditutup.");
|
||||||
}
|
}
|
||||||
if (err.code === 'ER_CON_COUNT_ERROR') {
|
if (err.code === "ER_CON_COUNT_ERROR") {
|
||||||
console.error('Basis data memiliki terlalu banyak koneksi.');
|
console.error("Basis data memiliki terlalu banyak koneksi.");
|
||||||
}
|
}
|
||||||
if (err.code === 'ECONNREFUSED') {
|
if (err.code === "ECONNREFUSED") {
|
||||||
console.error('Koneksi database ditolak.');
|
console.error("Koneksi database ditolak.");
|
||||||
}
|
}
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
if (conn) conn.release();
|
if (conn) conn.release();
|
||||||
return;
|
return;
|
||||||
})
|
});
|
||||||
|
|
||||||
// pool.on('acquire', function (connection) {
|
// pool.on('acquire', function (connection) {
|
||||||
// console.log('Connection %d acquired', connection.threadId);
|
// console.log('Connection %d acquired', connection.threadId);
|
||||||
|
|||||||
142
cron/UpdateJobStatusWorker.js
Normal file
142
cron/UpdateJobStatusWorker.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
const path = require("path");
|
||||||
|
require("dotenv").config({ path: path.resolve(__dirname, "../.env") });
|
||||||
|
|
||||||
|
const LibWinston = require("../library/LibWinston");
|
||||||
|
const OrderStatusModels = require("../models/OrderStatusModels");
|
||||||
|
const db = require("../config/dbMysqlConn");
|
||||||
|
const moment = require("moment");
|
||||||
|
|
||||||
|
const schedulerName = "UPDATE JOB STATUS";
|
||||||
|
const Logger = LibWinston.initialize(schedulerName);
|
||||||
|
|
||||||
|
// ✅ Status constants
|
||||||
|
const STATUS = {
|
||||||
|
AWAL: 22,
|
||||||
|
DI_PICKUP: 2,
|
||||||
|
OTW: 3,
|
||||||
|
SAMPAI: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 🔍 Fungsi Haversine untuk jarak circle
|
||||||
|
function haversine(lat1, lon1, lat2, lon2) {
|
||||||
|
const toRad = (x) => (x * Math.PI) / 180;
|
||||||
|
const R = 6371 * 1000; // radius bumi meter
|
||||||
|
|
||||||
|
const dLat = toRad(lat2 - lat1);
|
||||||
|
const dLon = toRad(lon2 - lon1);
|
||||||
|
|
||||||
|
const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) ** 2;
|
||||||
|
|
||||||
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
return R * c; // hasil meter
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔍 Fungsi cek point di dalam square (bounding box)
|
||||||
|
function isPointInSquare(point, boundaryLatLngs) {
|
||||||
|
const lats = boundaryLatLngs.map((p) => parseFloat(p.lat));
|
||||||
|
const lngs = boundaryLatLngs.map((p) => parseFloat(p.lng));
|
||||||
|
const minLat = Math.min(...lats);
|
||||||
|
const maxLat = Math.max(...lats);
|
||||||
|
const minLng = Math.min(...lngs);
|
||||||
|
const maxLng = Math.max(...lngs);
|
||||||
|
|
||||||
|
return point.lat >= minLat && point.lat <= maxLat && point.lng >= minLng && point.lng <= maxLng;
|
||||||
|
}
|
||||||
|
|
||||||
|
const go = async () => {
|
||||||
|
Logger.log("info", `${schedulerName}: ${moment().format("YYYY-MM-DD HH:mm:ss")}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await OrderStatusModels.GetList();
|
||||||
|
const rows = result.rows;
|
||||||
|
if (!rows || rows.length === 0) return;
|
||||||
|
console.log("rows: ", rows);
|
||||||
|
|
||||||
|
for (let row of rows) {
|
||||||
|
if (!row.pck_latlng || !row.drp_latlng || !row.latitude || !row.longitude) continue;
|
||||||
|
|
||||||
|
const lat = parseFloat(row.latitude);
|
||||||
|
const lng = parseFloat(row.longitude);
|
||||||
|
if (isNaN(lat) || isNaN(lng)) continue;
|
||||||
|
|
||||||
|
let pckBoundary, drpBoundary;
|
||||||
|
try {
|
||||||
|
pckBoundary = JSON.parse(row.pck_latlng);
|
||||||
|
drpBoundary = JSON.parse(row.drp_latlng);
|
||||||
|
} catch (e) {
|
||||||
|
Logger.log("error", `Invalid JSON for order ${row.ord_code}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ambil center titik untuk perhitungan jarak circle
|
||||||
|
const pckCenter = pckBoundary[0];
|
||||||
|
const drpCenter = drpBoundary[0];
|
||||||
|
|
||||||
|
const distToPickup = haversine(lat, lng, parseFloat(pckCenter.lat), parseFloat(pckCenter.lng));
|
||||||
|
const distToDrop = haversine(lat, lng, parseFloat(drpCenter.lat), parseFloat(drpCenter.lng));
|
||||||
|
|
||||||
|
// Tentukan apakah di pickup zone
|
||||||
|
let inPickupZone = false;
|
||||||
|
if (row.pck_type === "circle") {
|
||||||
|
inPickupZone = distToPickup <= row.pck_radius;
|
||||||
|
} else if (row.pck_type === "square") {
|
||||||
|
inPickupZone = isPointInSquare({ lat, lng }, pckBoundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tentukan apakah di drop zone
|
||||||
|
let inDropZone = false;
|
||||||
|
if (row.drp_type === "circle") {
|
||||||
|
inDropZone = distToDrop <= row.drp_radius;
|
||||||
|
} else if (row.drp_type === "square") {
|
||||||
|
inDropZone = isPointInSquare({ lat, lng }, drpBoundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
let newStatus = row.status;
|
||||||
|
if (inPickupZone) {
|
||||||
|
newStatus = STATUS.DI_PICKUP;
|
||||||
|
} else if (!inPickupZone && !inDropZone) {
|
||||||
|
newStatus = STATUS.OTW;
|
||||||
|
} else if (inDropZone) {
|
||||||
|
newStatus = STATUS.SAMPAI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newStatus !== row.status) {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
db.query(`UPDATE t_orders SET status = ? WHERE code = ?`, [newStatus, row.ord_code], (err) => {
|
||||||
|
if (err) {
|
||||||
|
Logger.log("error", `Gagal update status order ${row.ord_code}: ${err.message}`);
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
console.log(`Order ${row.ord_code}: status updated from ${row.status} to ${newStatus}`);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Jika sudah sampai (status SAMPAI), reset data kendaraan
|
||||||
|
if (newStatus === STATUS.SAMPAI) {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
db.query(`UPDATE t_vehicles SET is_in_ord = 2, ord_id = 0, ord_code = '0' WHERE id = ?`, [row.id], (err) => {
|
||||||
|
if (err) {
|
||||||
|
Logger.log("error", `Gagal reset kendaraan ID ${row.id}: ${err.message}`);
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
console.log(`Vehicle ${row.id} reset: is_in_ord=2, ord_id=0, ord_code='0'`);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Logger.log("error", `Job error: ${error.message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const index = async () => {
|
||||||
|
while (true) {
|
||||||
|
await go();
|
||||||
|
await new Promise((r) => setTimeout(r, 3000));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
index();
|
||||||
59
models/OrderStatusModels.js
Executable file
59
models/OrderStatusModels.js
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
const db = require(`../config/dbMysqlConn`);
|
||||||
|
|
||||||
|
const OrderStatusModels = (module.exports = {
|
||||||
|
GetList: async function () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const query = `
|
||||||
|
SELECT
|
||||||
|
tv.id,
|
||||||
|
tv.name,
|
||||||
|
tv.is_in_ord,
|
||||||
|
tv.ord_id,
|
||||||
|
tv.ord_code,
|
||||||
|
tv.device_id,
|
||||||
|
tz.boundary_latlngs AS pck_latlng,
|
||||||
|
tz.boundary_type AS pck_type,
|
||||||
|
tz.boundary_radius AS pck_radius,
|
||||||
|
tz2.boundary_latlngs AS drp_latlng,
|
||||||
|
tz2.boundary_type AS drp_type,
|
||||||
|
tz2.boundary_radius AS drp_radius,
|
||||||
|
to2.status,
|
||||||
|
tgt.latitude,
|
||||||
|
tgt.longitude,
|
||||||
|
tgt.crt_format
|
||||||
|
FROM
|
||||||
|
t_vehicles tv
|
||||||
|
LEFT JOIN t_orders to2 ON to2.code = tv.ord_code
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT tg1.device_id, tg1.latitude, tg1.longitude, tg1.crt_format
|
||||||
|
FROM t_gps_tracks tg1
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT device_id, MAX(crt_format) AS max_crt
|
||||||
|
FROM t_gps_tracks
|
||||||
|
WHERE action = 'location'
|
||||||
|
GROUP BY device_id
|
||||||
|
) tg2 ON tg1.device_id = tg2.device_id AND tg1.crt_format = tg2.max_crt
|
||||||
|
WHERE tg1.action = 'location'
|
||||||
|
) tgt ON tgt.device_id = tv.device_id
|
||||||
|
LEFT JOIN t_orders_pck_drop topd ON topd.ord_code = tv.ord_code
|
||||||
|
LEFT JOIN t_zones tz ON tz.id = topd.pck_id
|
||||||
|
LEFT JOIN t_zones tz2 ON tz2.id = topd.drop_id
|
||||||
|
WHERE
|
||||||
|
tv.is_in_ord = 1
|
||||||
|
AND tv.ord_id != 0
|
||||||
|
AND tv.ord_code IS NOT NULL
|
||||||
|
ORDER BY tgt.crt_format DESC;
|
||||||
|
`;
|
||||||
|
|
||||||
|
db.query(query, (err, result) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
rowCount: result.length,
|
||||||
|
rows: result,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,121 +1,119 @@
|
|||||||
const db = require(`../config/dbMysqlConn`);
|
const db = require(`../config/dbMysqlConn`);
|
||||||
|
|
||||||
class UsersModels {
|
class UsersModels {
|
||||||
|
static ROLE_SU = 1; // unused
|
||||||
|
static ROLE_SUPERADMIN = 7;
|
||||||
|
static ROLE_ADMIN = 2;
|
||||||
|
static ROLE_VENDOR = 8;
|
||||||
|
static ROLE_CHECKER = 9;
|
||||||
|
static ROLE_USER = 3; // unused
|
||||||
|
static ROLE_CLIENT_ADMIN = 4;
|
||||||
|
static ROLE_CLIENT_STAFF = 5; // unused
|
||||||
|
static ROLE_CLIENT_USER = 6; // unused
|
||||||
|
static ROLE_SPECIAL_TRACKING = 10;
|
||||||
|
static ROLE_FINANCE = 11;
|
||||||
|
|
||||||
static ROLE_SU = 1; // unused
|
static STATUS_ACTIVE = 1;
|
||||||
static ROLE_SUPERADMIN = 7;
|
static STATUS_NOT_ACTIVE = 2;
|
||||||
static ROLE_ADMIN = 2;
|
static STATUS_SUSPEND = 3;
|
||||||
static ROLE_VENDOR = 8;
|
|
||||||
static ROLE_CHECKER = 9;
|
|
||||||
static ROLE_USER = 3; // unused
|
|
||||||
static ROLE_CLIENT_ADMIN = 4;
|
|
||||||
static ROLE_CLIENT_STAFF = 5; // unused
|
|
||||||
static ROLE_CLIENT_USER = 6; // unused
|
|
||||||
static ROLE_SPECIAL_TRACKING = 10;
|
|
||||||
static ROLE_FINANCE = 11;
|
|
||||||
|
|
||||||
static STATUS_ACTIVE = 1;
|
static CHK_TYPE_PICKUP = 1;
|
||||||
static STATUS_NOT_ACTIVE = 2;
|
static CHK_TYPE_DROP = 2;
|
||||||
static STATUS_SUSPEND = 3;
|
|
||||||
|
|
||||||
static CHK_TYPE_PICKUP = 1;
|
static DEFAULT_UID = 1; // swanusa account
|
||||||
static CHK_TYPE_DROP = 2;
|
|
||||||
|
|
||||||
static DEFAULT_UID = 1; // swanusa account
|
static DEFAULT_PHONE_CODE = 62;
|
||||||
|
|
||||||
static DEFAULT_PHONE_CODE = 62;
|
static async all() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const query = `SELECT * FROM t_users;`;
|
||||||
|
db.query(query, (err, results) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
resolve(results);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static async all() {
|
static async getUsersActiveByRole(role) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const query = `SELECT * FROM t_users;`;
|
const query = `SELECT * FROM t_users WHERE dlt is null AND status = ${UsersModels.STATUS_ACTIVE} AND role = ?;`;
|
||||||
db.query(query, (err, results) => {
|
db.query(query, [role], (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
resolve(results);
|
resolve(results);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getUsersActiveByRole(role) {
|
static async find(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const query = `SELECT * FROM t_users WHERE dlt is null AND status = ${UsersModels.STATUS_ACTIVE} AND role = ?;`;
|
const query = `SELECT * FROM t_users WHERE id = ? LIMIT 1;`;
|
||||||
db.query(query, [role], (err, results) => {
|
db.query(query, [id], (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
resolve(results);
|
resolve(results);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async find(id) {
|
static async findEmail(email) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const query = `SELECT * FROM t_users WHERE id = ? LIMIT 1;`;
|
const query = `SELECT * FROM t_users WHERE email = ? LIMIT 1;`;
|
||||||
db.query(query, [id], (err, results) => {
|
db.query(query, [email], (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
resolve(results);
|
resolve(results);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findEmail(email) {
|
static async create(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const query = `SELECT * FROM t_users WHERE email = ? LIMIT 1;`;
|
const query = `INSERT INTO t_users SET ?;`;
|
||||||
db.query(query, [email], (err, results) => {
|
db.query(query, data, (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
resolve(results);
|
resolve(results);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async create(data) {
|
static async update(data, id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const query = `INSERT INTO t_users SET ?;`;
|
const query = `UPDATE t_users SET ? WHERE id = ?;`;
|
||||||
db.query(query, data, (err, results) => {
|
db.query(query, [data, id], (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
resolve(results);
|
resolve(results);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(data, id) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const query = `UPDATE t_users SET ? WHERE id = ?;`;
|
|
||||||
db.query(query, [data, id], (err, results) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
resolve(results);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async delete(id) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const query = `DELETE FROM t_users WHERE id = ?;`;
|
|
||||||
db.query(query, [id], (err, results) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
resolve(results);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
static async delete(id) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const query = `DELETE FROM t_users WHERE id = ?;`;
|
||||||
|
db.query(query, [id], (err, results) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
resolve(results);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = UsersModels;
|
module.exports = UsersModels;
|
||||||
|
|||||||
1
package-lock.json
generated
1
package-lock.json
generated
@ -1635,6 +1635,7 @@
|
|||||||
"version": "10.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
||||||
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
|
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user