阅读视图

发现新文章,点击刷新页面。

定时同步订单信息

有这么一个场景👇

由于网络的问题,我们创建订单并付费成功了。但是,在调用同步订单接口的时候,出现了网络的问题等不可抗拒的因素而同步不成功。这个时候,就会造成订单的状态不正确,那么,我们有什么措施解决或者减少这种情况的?

嗯~

我们可以采用下面的方案:

  1. 使用浏览器存储,将订单信息存储在 IndexedDB
  2. 将信息存储在电脑本地

使用 IndexedDB 在之前的文章中,我们已经提及 前端使用 IndexedDB 存储

这里,我们采取第二种方法 - 将信息存储在电脑磁盘。下面是我们的思路👇

这里我们使用的开发环境 👇

版本:Windows 10 专业版

版本号:22H2

电脑上拆分了多个磁盘 C, D, E, F

  1. 在同步订单的接口执行前,先将订单机器信息记录到本地
  2. 在应用的首页,每隔 * 分钟轮询本地的记录,同步到远程服务
  3. 如果同步成功,则删除本地的信息记录;同步失败的订单下次轮询

👌。思路有了,我们来实现下,这里假设你已经安装了对应的 Node 环境。

写入文件

我们将订单信息记录在 D 盘下:

export const LOCAL_DISK_ORDER_INFO_PATH = "d:\\app-name\order-info"; // 订单信息存放的路径

export interface DiskSaveOrderInfo {
  billNo: string;
  billPayTime: string;
  payWay: string
  // ... other properties
}

我们将执行下面的操作进行保存。

// 获取订单的文件路径
export const getOrderFilePath = (orderId: string) => {
  return path.join(LOCAL_DISK_ORDER_INFO_PATH, `${orderId}.json`); // 存储为 json 文件
};

// 保存订单到文件
export const saveOrderToFile = async (orderId: string, order: DiskSaveOrderInfo) => {
  const filePath = getOrderFilePath(orderId);
  // 创建文件路径
  if (!fs.existsSync(LOCAL_DISK_ORDER_INFO_PATH)) {
    try {
      // 创建对应的文件夹
      await fs.promises.mkdir(LOCAL_DISK_ORDER_INFO_PATH, { recursive: true });
    } catch (error) {
      console.error("fs.promises.mkdir", "创建目录失败", error);
    }
  } else {
    fs.writeFileSync(filePath, JSON.stringify(order, null, 2)); // 写入数据
  }
};

读取文件

我们从磁盘中读取保存的订单信息👇

// 从文件中读取订单 - 读取单个文件
export const readOrderFromFile = (orderId: string): DiskSaveOrderInfo | null => {
  const filePath = getOrderFilePath(orderId);
  try {
    const data = fs.readFileSync(filePath, "utf8");
    return JSON.parse(data);
  } catch (error) {
    console.error("fs.readFileSync", `readOrderFromFile - 读取订单 ${orderId} 失败`, error);
    return null;
  }
}

// 读取指定文件夹下的所有订单信息
export const getAllOrderFiles = (): DiskSaveOrderInfo[] => {
  try {
    const orderFiles = fs.readdirSync(LOCAL_DISK_ORDER_INFO_PATH);
    const orderFilesData = orderFiles
      .filter((file) => file.endsWith(".json"))
      .map((file) => {
        // 获取订单号
        const orderId: string = file.replace(/\.json$/, "");
        const data: DiskSaveOrderInfo | null = readOrderFromFile(orderId);
          return data;
        });
    const result = orderFilesData.filter((data) => data !== null);
    return [...result] as DiskSaveOrderInfo[];
  } catch (error) {
    console.error("getAllOrderFiles", "获取所有订单文件失败", error);
    return [];
  }
}

删除文件

我们删除磁盘上保存的订单,如下👇

// 删除订单文件
export const deleteOrderFile = (orderId: string): boolean => {
  const filePath = getOrderFilePath(orderId);
  try {
    if (fs.existsSync(filePath)) {
      fs.unlinkSync(filePath);
        return true;
    }
    return false;
  } catch (error) {
    console.error("fs.unlinkSync", `删除订单 ${orderId} 文件失败`, error);
    return false;
  }
};

执行保存

在同步订单接口之前,我们将相关的信息记录下

export function usePayDone() {
  const billNo = useStore(SelectorBillNo);
  
  const notifyPaySuccessFn = async (payWay: PayWayEnum) => {
    try {
      // 保存订单信息到本地
      await saveOrderToFile(billNo, {
        billNo,
        billPayTime: `${new Date().getTime()}`,
        payWay
      })
    } catch (error) {
      console.error("saveOrderToFile()", `保存订单 ${billNo} 信息到本地失败`, error);
    }
    // 执行同步订单到远程的代码 ...
  }
  
  return {
    notifyPaySuccessFn
  }
}

同步到远程

我们将本地的订单信息读取出来,然后依次向远程发起请求同步👇

// 定时同步本地订单的信息 - 这里可以自行实现是间隔多久触发下面的代码
export const useSyncOrders = () => {
  const handleSyncOrderFn = async () => {
    // 获取本地磁盘所有的订单信息
    const orderFiles = getAllOrderFiles();
    
    if (orderFiles.length === 0) {
      return Promise.resolve();
    }
    
    const syncPromises = orderFiles.map((file) => {
      return new Promise(async (resolve, reject) => {
        try {
          // 同步订单信息接口
          await postNotifyPaySuccess({
            billNo: file.billNo,
            payWay: file.payWay,
          });
          // 如果同步成功,则删除订单
          deleteOrderFile(file.billNo);
          resolve(true);
        } catch (error){
          console.error("deleteOrderFile()", `删除本地订单 ${file.billNo} 失败`, error);
          reject(error);
        }
      });
    });
    
    return Promise.all(sysnPromises);
  }
  
  return {
    hanleSyncOrderFn,
  };
}

至此,我们已经完成了订单信息的存储,订单的读取和订单的同步。

拜拜,Happy Coding!

参考

React 简洁代码 - 使用绝对路径导入

译文原文链接 React Clean Code with Absolute Imports - 作者 Tasawar Hussain

React 是一个很受欢迎的 JavaScript 的库,用于构建用户界面。当我们使用 React 工作的时候,会发现我们需要从不同的文件中引入不同的模块。其中一个管理我们引入的方式是使用绝对路径引入。在本文中,我们将探讨在 React 中(无论你是用 Typescript 还是 JavaScript 组织代码)使用绝对路径导入的好处。

什么是绝对路径导入

JavaScript 中,当我们导入一个模块,我们需要指定需要导入的模块的文件路径。这个路径可以是相对路径或者绝对路径。相对路径是以一个点或者多个点开始,后面带个前斜线。

嗯,我们假设有下面一个 TODO 的应用,有下面的结构:

src/
  components/
    TodoList.tsx
    TodoItem.tsx
  screens/
    TodoScreen.tsx

为了在 TodoScreen 组件中引入 TodoList 组件,我们可以如下引入:

import TodoList from "../../components/TodoList";

上面的声明使用了相对的路径从 components 文件夹中导入了 TodoList 模块。使用相对路径很容易出错,特别是当我们编写一个大型的项目,该项目有很多层级的目录。

为了避免这个问题,我们可以使用绝对路径来导入模块。绝对路径是从我们项目根目录开始,并指定我们想要导入的模块路径。以下是个案例👇

import TodoList from "components/TodoList";

该声明使用绝对路径从 src/components 目录中导入 TodoList 模块。使用绝对可以让我们避免错误和使得代码更容易维护。

TypeScript 中怎么使用绝对路径导入

如果我们在 React 中使用的是 TypeScript,我们可以在文件 tsconfig.json 中使用 baseUrlpaths 选项来配置绝对路径导入。下面是个例子👇

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "src/*": ["src/*"]
    }
  }
}

JavaScript 中怎么使用绝对路径导入

如果我们在 React 项目中使用 JavaScript,我们可以在根目录下的文件 jsconfig.json 中添加下面的内容:

{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}

上面的配置设定 baseUrl 是你项目的根目录并且定义了一个 src/* 的路径映射。这意味着我们可以从 src 目录中使用绝对路径。

React 中使用绝对路径导入的好处

1. 简化导入

React 中使用绝对路径导入的最主要的好处就是简化我们的引入。相比于使用复杂的相对路径导入模块,我们可以使用简短的绝对路径导入。这让我们的代码更加容易读和维护。

比如,下面的相对路径:

import MyComponent from '../../../components/MyComponent';

上面使用了比较长和复杂的路径来引入 MyComponent 组件。相反的,使用绝对路径改写如下👇

import MyComponent from 'src/components/MyComponent';

上面这个声明使用了简洁的绝对路径导入 MyComponent 组件。

2. 避免文件路径错误

当我们使用相对路径时候,很容易错误导入文件。绝对路径通过指定我们想要导入的文件的精准位置来避免这个问题。这意味着我们可以避免写常见的错误,比如导入错误的文件或者文件丢失。

3. 重构更容易

使用绝对路径让我们重构代码更加容易。当我们在项目中移动文件或者目录,我们可以很简单地更新导入的路径。这为我们节约了不少时间并降低了引入错误的风险。

❌