You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MEAN栈单API路由返回多维度销售数据的实现优化咨询

Refactoring Your /sales_chart Endpoint for Functional Programming & Clean Data Fetching

Hey there! It sounds like you're trying to build a clean, functional endpoint that delivers all the sales aggregates your Chart.js needs in one go—smart move, since cutting down on API calls boosts performance. Let's refactor your code to align with functional programming principles (pure functions, immutability, separation of concerns) while making it easier to maintain and test.

Core Approach: Split Logic into Pure & Impure Functions

Functional programming shines when you separate pure logic (no side effects, predictable output) from impure logic (database calls, external APIs). We'll break your endpoint into reusable, focused pieces:

1. Pure Functions for Date Range Calculations

First, create pure functions to generate all the time ranges you need—these take a date and return start/end dates with zero side effects:

// Pure function: Get YTD (Year-to-Date) range for a given date
const getYTDRange = (date) => {
  const year = date.getFullYear();
  return {
    start: new Date(year, 0, 1),
    end: new Date(date)
  };
};

// Pure function: Get MTD (Month-to-Date) range for a given date
const getMTDRange = (date) => {
  const year = date.getFullYear();
  const month = date.getMonth();
  return {
    start: new Date(year, month, 1),
    end: new Date(date)
  };
};

// Pure function: Get last year's YTD range matching the input date
const getLastYearYTDRange = (date) => {
  const lastYear = date.getFullYear() - 1;
  return {
    start: new Date(lastYear, 0, 1),
    end: new Date(lastYear, date.getMonth(), date.getDate())
  };
};

// Pure function: Get last year's MTD range matching the input date
const getLastYearMTDRange = (date) => {
  const lastYear = date.getFullYear() - 1;
  const month = date.getMonth();
  return {
    start: new Date(lastYear, month, 1),
    end: new Date(lastYear, month, date.getDate())
  };
};

2. Pure Function for Data Aggregation

Next, a pure function to calculate total sales from a set of records—this takes an array and returns a sum, no external dependencies:

// Pure function: Calculate total sales from an array of sales records
const calculateTotalSales = (salesRecords) => {
  return salesRecords.reduce((total, record) => total + record.amount, 0);
};

3. (Optional) Optimized Aggregation via Database

If you're using Mongoose, you can offload the sum calculation to the database (better for performance with large datasets) with a focused async function:

// Impure but focused function: Fetch total sales from DB for a store and range
const fetchTotalSalesFromDB = async (store, range) => {
  const aggregationResult = await Sales.aggregate([
    { $match: { store, saleDate: { $gte: range.start, $lte: range.end } } },
    { $group: { _id: null, total: { $sum: '$amount' } } }
  ]);
  return aggregationResult[0]?.total || 0;
};

4. Main Endpoint Logic

Now combine these pieces in your route handler, keeping async logic clean with async/await and separating input validation from data fetching:

router.post('/sales_chart', async (req, res, next) => {
  try {
    const { store, date: dateStr } = req.body;

    // Validate input (pure logic wrapped in error handling)
    if (!store || typeof store !== 'string') {
      throw new Error('A valid store identifier is required');
    }
    const currentDate = new Date(dateStr);
    if (isNaN(currentDate.getTime())) {
      throw new Error('A valid date string is required');
    }

    // Generate all needed time ranges using pure functions
    const dateRanges = {
      ytd: getYTDRange(currentDate),
      mtd: getMTDRange(currentDate),
      lastYearYtd: getLastYearYTDRange(currentDate),
      lastYearMtd: getLastYearMTDRange(currentDate)
    };

    // Parallelize database queries for better performance
    const aggregatePromises = Object.entries(dateRanges).map(async ([key, range]) => {
      const total = await fetchTotalSalesFromDB(store, range);
      return { [key]: total };
    });

    // Wait for all queries to complete and merge results
    const results = await Promise.all(aggregatePromises);
    const responseData = results.reduce((acc, result) => ({ ...acc, ...result }), {});

    res.status(200).json(responseData);
  } catch (error) {
    next(error); // Pass errors to your Express error-handling middleware
  }
});

Why This Works for Functional Programming

  • Pure Functions: Date range and sales calculation logic are completely reusable, testable, and free of side effects. You can test these functions independently without hitting the database.
  • Immutability: All date objects are newly created—we never modify the original input date or external state.
  • Separation of Concerns: Input validation, date logic, data fetching, and response formatting are all distinct parts, making it easy to update one without breaking others.
  • Parallel Execution: Using Promise.all runs all database queries at the same time, reducing the total time the endpoint takes to respond.

Example Response

Your frontend will get a clean object ready for Chart.js:

{
  "ytd": 156000,
  "mtd": 28000,
  "lastYearYtd": 142000,
  "lastYearMtd": 25000
}

内容的提问来源于stack exchange,提问作者John Marquez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:25:12