feat(stats): seller stat api completed
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
import manageResponse from "../../utils/manage_response";
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import catchAsync from "../../utils/catch_async";
|
||||||
|
import { statictics_service } from "./statictics.service";
|
||||||
|
|
||||||
|
const get_seller_stats = catchAsync(async (req: Request, res: Response) => {
|
||||||
|
const shopAccountId = req.user?.accountId;
|
||||||
|
if (!shopAccountId) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
message: "Unauthorized",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const range = (req.query.range as "7d" | "30d" | "all") || "7d";
|
||||||
|
|
||||||
|
const result = await statictics_service.get_seller_stats_fromDb(
|
||||||
|
shopAccountId,
|
||||||
|
range,
|
||||||
|
);
|
||||||
|
|
||||||
|
manageResponse(res, {
|
||||||
|
success: true,
|
||||||
|
statusCode: 200,
|
||||||
|
message: "All statictics fetched successfully.",
|
||||||
|
data: result,
|
||||||
|
meta: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// const get_all_statictics = catchAsync(async (req, res) => {
|
||||||
|
// const result = await statictics_service.get_all_statictics_from_db(req);
|
||||||
|
// manageResponse(res, {
|
||||||
|
// success: true,
|
||||||
|
// statusCode: 200,
|
||||||
|
// message: "All statictics fetched successfully.",
|
||||||
|
// data: result,
|
||||||
|
// meta: {},
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const get_single_statictics = catchAsync(async (req, res) => {
|
||||||
|
// const result = await statictics_service.get_single_statictics_from_db(req);
|
||||||
|
// manageResponse(res, {
|
||||||
|
// success: true,
|
||||||
|
// statusCode: 200,
|
||||||
|
// message: "Single statictics fetched successfully.",
|
||||||
|
// data: result,
|
||||||
|
// meta: {},
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const create_statictics = catchAsync(async (req, res) => {
|
||||||
|
// const result = await statictics_service.create_statictics_into_db(req);
|
||||||
|
// manageResponse(res, {
|
||||||
|
// success: true,
|
||||||
|
// statusCode: 200,
|
||||||
|
// message: "statictics created successfully.",
|
||||||
|
// data: result,
|
||||||
|
// meta: {},
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const update_statictics = catchAsync(async (req, res) => {
|
||||||
|
// const result = await statictics_service.update_statictics_into_db(req);
|
||||||
|
// manageResponse(res, {
|
||||||
|
// success: true,
|
||||||
|
// statusCode: 200,
|
||||||
|
// message: "statictics updated successfully.",
|
||||||
|
// data: result,
|
||||||
|
// meta: {},
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const delete_statictics = catchAsync(async (req, res) => {
|
||||||
|
// const result = await statictics_service.delete_statictics_from_db(req);
|
||||||
|
// manageResponse(res, {
|
||||||
|
// success: true,
|
||||||
|
// statusCode: 200,
|
||||||
|
// message: "statictics deleted successfully.",
|
||||||
|
// data: result,
|
||||||
|
// meta: {},
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
export const statictics_controller = {
|
||||||
|
get_seller_stats,
|
||||||
|
// get_all_statictics,
|
||||||
|
// get_single_statictics,
|
||||||
|
// create_statictics,
|
||||||
|
// update_statictics,
|
||||||
|
// delete_statictics,
|
||||||
|
};
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import auth from "../../middlewares/auth";
|
||||||
|
import { statictics_controller } from "./statictics.controller";
|
||||||
|
import RequestValidator from "../../middlewares/request_validator";
|
||||||
|
// import { statictics_controller } from "./statictics.controller";
|
||||||
|
// import { statictics_validations } from "./statictics.validation";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
|
||||||
|
router.get("/seller", auth("USER"), statictics_controller.get_seller_stats)
|
||||||
|
|
||||||
|
// router.post(
|
||||||
|
// "/",
|
||||||
|
// RequestValidator(statictics_validations.create_statictics),
|
||||||
|
// statictics_controller.create_statictics,
|
||||||
|
// );
|
||||||
|
// router.get("/:id", statictics_controller.get_single_statictics);
|
||||||
|
// router.patch(
|
||||||
|
// "/:id",
|
||||||
|
// RequestValidator(statictics_validations.update_statictics),
|
||||||
|
// statictics_controller.update_statictics,
|
||||||
|
// );
|
||||||
|
// router.delete("/:id", statictics_controller.delete_statictics);
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
// import { Request } from "express";
|
||||||
|
// import { prisma } from "../../lib/prisma";
|
||||||
|
|
||||||
|
import { prisma } from "../../lib/prisma";
|
||||||
|
|
||||||
|
type Range = "7d" | "30d" | "all";
|
||||||
|
|
||||||
|
const get_seller_stats_fromDb = async (shopAccountId: string, range: Range) => {
|
||||||
|
let createdAtFilter: any = {};
|
||||||
|
|
||||||
|
if (range !== "all") {
|
||||||
|
const days = range === "7d" ? 7 : 30;
|
||||||
|
const from = new Date();
|
||||||
|
from.setDate(from.getDate() - days);
|
||||||
|
|
||||||
|
createdAtFilter = { gte: from };
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseWhere: any = {
|
||||||
|
shopAccountId,
|
||||||
|
...(range !== "all" && { createdAt: createdAtFilter }),
|
||||||
|
};
|
||||||
|
|
||||||
|
const [
|
||||||
|
totalOrders,
|
||||||
|
completedOrders,
|
||||||
|
pendingOrders,
|
||||||
|
rejectedOrders,
|
||||||
|
revenueResult,
|
||||||
|
last7DaysRejected,
|
||||||
|
ordersForChart,
|
||||||
|
] = await Promise.all([
|
||||||
|
prisma.order.count({ where: baseWhere }),
|
||||||
|
|
||||||
|
prisma.order.count({
|
||||||
|
where: { ...baseWhere, status: "DELIVERED" },
|
||||||
|
}),
|
||||||
|
|
||||||
|
prisma.order.count({
|
||||||
|
where: {
|
||||||
|
...baseWhere,
|
||||||
|
status: { in: ["INITIATED", "CONFIRMED", "ONGOING"] },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
prisma.order.count({
|
||||||
|
where: { ...baseWhere, status: "CANCELLED" },
|
||||||
|
}),
|
||||||
|
|
||||||
|
prisma.order.aggregate({
|
||||||
|
where: { ...baseWhere, status: "DELIVERED" },
|
||||||
|
_sum: { productPrice: true },
|
||||||
|
}),
|
||||||
|
|
||||||
|
prisma.order.count({
|
||||||
|
where: {
|
||||||
|
shopAccountId,
|
||||||
|
status: "CANCELLED",
|
||||||
|
createdAt: {
|
||||||
|
gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
range !== "all"
|
||||||
|
? prisma.order.findMany({
|
||||||
|
where: {
|
||||||
|
shopAccountId,
|
||||||
|
status: "DELIVERED",
|
||||||
|
createdAt: createdAtFilter,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
productPrice: true,
|
||||||
|
createdAt: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
: Promise.resolve([]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const totalRevenue = revenueResult._sum.productPrice || 0;
|
||||||
|
|
||||||
|
const avgOrderValue =
|
||||||
|
completedOrders > 0 ? totalRevenue / completedOrders : 0;
|
||||||
|
|
||||||
|
const dailyMap: Record<string, number> = {};
|
||||||
|
|
||||||
|
for (const o of ordersForChart) {
|
||||||
|
const date = o.createdAt.toISOString().split("T")[0];
|
||||||
|
if (!dailyMap[date]) dailyMap[date] = 0;
|
||||||
|
dailyMap[date] += o.productPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dailyRevenue = Object.entries(dailyMap).map(([date, revenue]) => ({
|
||||||
|
date,
|
||||||
|
revenue,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalOrders,
|
||||||
|
completedOrders,
|
||||||
|
pendingOrders,
|
||||||
|
rejectedOrders,
|
||||||
|
totalRevenue,
|
||||||
|
avgOrderValue,
|
||||||
|
last7DaysRejected,
|
||||||
|
dailyRevenue,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// const get_all_statictics_from_db = async (req: Request) => {
|
||||||
|
// // define your own login here
|
||||||
|
// const result = await prisma.statictics.findMany();
|
||||||
|
// return result;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const get_single_statictics_from_db = async (req: Request) => {
|
||||||
|
// // define your own login here
|
||||||
|
// const { id } = req.params;
|
||||||
|
// const result = await prisma.statictics.findUnique({where:{id}});
|
||||||
|
// return result;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const create_statictics_into_db = async (req: Request) => {
|
||||||
|
// // define your own login here
|
||||||
|
// const result = await prisma.statictics.create({data:req.body});
|
||||||
|
// return result;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const update_statictics_into_db = async (req: Request) => {
|
||||||
|
// // define your own login here
|
||||||
|
// const { id } = req.params;
|
||||||
|
// const result = await prisma.statictics.update({where:{id},data:req.body});
|
||||||
|
// return result;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const delete_statictics_from_db = async (req: Request) => {
|
||||||
|
// // define your own login here
|
||||||
|
// const { id } = req.params;
|
||||||
|
// const result = await prisma.statictics.delete({where:{id}});
|
||||||
|
// return result;
|
||||||
|
// };
|
||||||
|
|
||||||
|
export const statictics_service = {
|
||||||
|
get_seller_stats_fromDb,
|
||||||
|
|
||||||
|
// get_all_statictics_from_db,
|
||||||
|
// get_single_statictics_from_db,
|
||||||
|
// create_statictics_into_db,
|
||||||
|
// update_statictics_into_db,
|
||||||
|
// delete_statictics_from_db,
|
||||||
|
};
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
|
||||||
|
export const staticticsSwaggerDocs = {
|
||||||
|
"/api/statictics": {
|
||||||
|
post: {
|
||||||
|
tags: ["statictics"],
|
||||||
|
summary: "Create new statictics",
|
||||||
|
description: "",
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
example: JSON.stringify({}), // put your request body
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
201: { description: "statictics created successfully" },
|
||||||
|
500: { description: "Validation error or internal server error" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
get: {
|
||||||
|
tags: ["statictics"],
|
||||||
|
summary: "Get all statictics",
|
||||||
|
description: "",
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "page",
|
||||||
|
in: "query",
|
||||||
|
required: false,
|
||||||
|
schema: { type: "number" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "limit",
|
||||||
|
in: "query",
|
||||||
|
required: false,
|
||||||
|
schema: { type: "number" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
responses: {
|
||||||
|
200: { description: "statictics fetched successfully" },
|
||||||
|
401: { description: "unauthorized" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/api/statictics/seller": {
|
||||||
|
get: {
|
||||||
|
tags: ["statistics"],
|
||||||
|
summary: "Get seller statistics",
|
||||||
|
description: "Fetch seller dashboard stats (7d, 30d, all)",
|
||||||
|
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "range",
|
||||||
|
in: "query",
|
||||||
|
required: false,
|
||||||
|
schema: {
|
||||||
|
type: "string",
|
||||||
|
enum: ["7d", "30d", "all"],
|
||||||
|
default: "7d",
|
||||||
|
},
|
||||||
|
description: "Time range for statistics",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Statistics fetched successfully",
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"/api/statictics/{id}": {
|
||||||
|
get: {
|
||||||
|
tags: ["statictics"],
|
||||||
|
summary: "Get single statictics",
|
||||||
|
description: "",
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
in: "path",
|
||||||
|
required: true,
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
responses: {
|
||||||
|
200: { description: "statictics fetched successfully" },
|
||||||
|
401: { description: "unauthorized" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
patch: {
|
||||||
|
tags: ["statictics"],
|
||||||
|
summary: "Update statictics",
|
||||||
|
description: "",
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
in: "path",
|
||||||
|
required: true,
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
example: JSON.stringify({}), // put your request body
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: { description: "statictics updated successfully" },
|
||||||
|
500: { description: "Validation error or internal server error" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
tags: ["statictics"],
|
||||||
|
summary: "Delete statictics",
|
||||||
|
description: "",
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
in: "path",
|
||||||
|
required: true,
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
responses: {
|
||||||
|
200: { description: "statictics delete successfully" },
|
||||||
|
401: { description: "unauthorized" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const create_statictics = z.object({});
|
||||||
|
const update_statictics = z.object({});
|
||||||
|
|
||||||
|
export const statictics_validations = {
|
||||||
|
create_statictics,
|
||||||
|
update_statictics,
|
||||||
|
};
|
||||||
@@ -4,10 +4,12 @@ import orderRoute from "./app/modules/order/order.route";
|
|||||||
import planRoute from "./app/modules/plan/plan.route";
|
import planRoute from "./app/modules/plan/plan.route";
|
||||||
import profileRoute from "./app/modules/profile/profile.route";
|
import profileRoute from "./app/modules/profile/profile.route";
|
||||||
import supportRoute from "./app/modules/support/support.route";
|
import supportRoute from "./app/modules/support/support.route";
|
||||||
|
import staticticsRoute from "./app/modules/statictics/statictics.route";
|
||||||
|
|
||||||
const appRouter = Router();
|
const appRouter = Router();
|
||||||
|
|
||||||
const moduleRoutes = [
|
const moduleRoutes = [
|
||||||
|
{ path: "/statictics", route: staticticsRoute },
|
||||||
{ path: "/order", route: orderRoute },
|
{ path: "/order", route: orderRoute },
|
||||||
{ path: "/support", route: supportRoute },
|
{ path: "/support", route: supportRoute },
|
||||||
{ path: "/plan", route: planRoute },
|
{ path: "/plan", route: planRoute },
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { orderSwaggerDocs } from "./app/modules/order/order.swagger";
|
|||||||
import { planSwaggerDocs } from "./app/modules/plan/plan.swagger";
|
import { planSwaggerDocs } from "./app/modules/plan/plan.swagger";
|
||||||
import { profileSwaggerDocs } from "./app/modules/profile/profile.swagger";
|
import { profileSwaggerDocs } from "./app/modules/profile/profile.swagger";
|
||||||
import { supportSwaggerDocs } from "./app/modules/support/support.swagger";
|
import { supportSwaggerDocs } from "./app/modules/support/support.swagger";
|
||||||
|
import { staticticsSwaggerDocs } from "./app/modules/statictics/statictics.swagger";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
@@ -24,6 +25,7 @@ export const swaggerOptions = {
|
|||||||
...orderSwaggerDocs,
|
...orderSwaggerDocs,
|
||||||
...profileSwaggerDocs,
|
...profileSwaggerDocs,
|
||||||
...supportSwaggerDocs,
|
...supportSwaggerDocs,
|
||||||
|
...staticticsSwaggerDocs,
|
||||||
},
|
},
|
||||||
servers:
|
servers:
|
||||||
configs.env === "production"
|
configs.env === "production"
|
||||||
|
|||||||
Reference in New Issue
Block a user