以下命令用于克隆本教程中使用的应用程序的源代码
git clone https://github.com/redis-developer/redis-real-time-inventory-solutions
在 零售库存系统 中,主要需求是向购物者和门店员工提供准确的实时库存视图,从而实现线上购买、门店取货 (BOPIS)。优化来自多个库存地点的履行。
可承诺库存 (ATP) 是指预计可供出售的库存数量,不包括已分配的库存。它允许企业控制对客户的配送并预测库存。ATP 模型有助于零售商降低库存成本,例如订购成本、持有成本和缺货成本。只要消费者购买预测保持准确,ATP 就会有所帮助。对零售商而言,有效实施 ATP 流程可以决定持续增长与库存反复出现缺货(导致错失销售机会并损害客户体验)之间的差异。
计算可承诺库存相对简单。请完成以下公式,以准确分析可承诺库存能力
Available-to-promise = QuantityOnHand + Supply - Demand
此公式包含以下元素
使用 Redis,系统可以实现跨门店、在途和仓库的实时库存同步。为零售商提供其整个门店网络中最准确、最及时的库存数据,并为消费者提供积极的搜索和定位库存的客户体验。
Redis 数据集成 (RDI) 功能可以实现准确的实时库存管理和系统记录同步。Redis 高级库存搜索和查询功能可以向多渠道和全渠道客户以及门店员工提供准确的可用库存信息。
此解决方案提高了库存周转率,从而降低了库存成本,提高了收入和利润。它还减少了客户搜索对系统记录和库存管理系统 (IMS) 的影响。
以下命令用于克隆本教程中使用的应用程序的源代码
git clone https://github.com/redis-developer/redis-real-time-inventory-solutions
管理库存或 SKU(库存单位) 流程包含一些活动,例如
以下代码显示了 retrieveSKU 活动的 API 请求和响应示例。
retrieveSKU API 请求
GET http://localhost:3000/api/retrieveSKU?sku=1019688
retrieveSKU API 响应
{
"data": {
"sku": 1019688,
"name": "5-Year Protection Plan - Geek Squad",
"type": "BlackTie",
"totalQuantity": 10
},
"error": null
}
当您发出请求时,它会通过 API 网关传递到库存服务。最终,它会调用一个 retrieveSKU
函数,如下所示
代码
static async retrieveSKU(_productId: number): Promise<IProduct> {
/**
Get current Quantity of a Product.
:param _productId: Product Id
:return: Product with Quantity
*/
const repository = ProductRepo.getRepository();
let retItem: IProduct = {};
if (repository && _productId) {
//fetch product by ID (using redis om library)
const product = <IProduct>await repository.fetch(_productId.toString());
if (product) {
retItem = {
sku: product.sku,
name: product.name,
type: product.type,
totalQuantity: product.totalQuantity
}
}
else {
throw `Product with Id ${_productId} not found`;
}
}
else {
throw `Input params failed !`;
}
return retItem;
}
以下代码显示了 updateSKU 活动的 API 请求和响应示例。
updateSKU API 请求
POST http://localhost:3000/api/updateSKU
{
"sku":1019688,
"quantity":25
}
updateSKU API 响应
{
"data": {
"sku": 1019688,
"name": "5-Year Protection Plan - Geek Squad",
"type": "BlackTie",
"totalQuantity": 25 //updated value
},
"error": null
}
当您发出请求时,它会通过 API 网关传递到库存服务。最终,它会调用一个 updateSKU
函数,如下所示
static async updateSKU(_productId: number, _quantity: number): Promise<IProduct> {
/**
Set Quantity of a Product.
:param _productId: Product Id
:param _quantity: new quantity
:return: Product with Quantity
*/
const repository = ProductRepo.getRepository();
let retItem: IProduct = {};
if (repository && _productId && _quantity >= 0) {
//fetch product by ID (using redis om library)
const product = <IProduct>await repository.fetch(_productId.toString());
if (product) {
//update the product fields
product.totalQuantity = _quantity;
// save the modified product
const savedItem = <IProduct>await repository.save(<RedisEntity>product);
retItem = {
sku: savedItem.sku,
name: savedItem.name,
type: savedItem.type,
totalQuantity: savedItem.totalQuantity
}
}
else {
throw `Product with Id ${_productId} not found`;
}
}
else {
throw `Input params failed !`;
}
return retItem;
}
以下代码显示了 incrementSKU 活动的 API 请求和响应示例。
incrementSKU API 请求
POST http://localhost:3000/api/incrementSKU
{
"sku":1019688,
"quantity":2
}
incrementSKU API 响应
{
"data": {
"sku": 1019688,
"name": "5-Year Protection Plan - Geek Squad",
"type": "BlackTie",
"totalQuantity": 12 //previous value 10
},
"error": null
}
当您发出请求时,它会通过 API 网关传递到库存服务。最终,它会调用一个 incrementSKU
函数,如下所示
static async incrementSKU(_productId: number, _incrQuantity: number, _isDecrement: boolean, _isReturnProduct: boolean): Promise<IProduct> {
/**
increment quantity of a Product.
:param _productId: Product Id
:param _incrQuantity: new increment quantity
:return: Product with Quantity
*/
const redisOmClient = getRedisOmClient();
let retItem: IProduct = {};
if (!_incrQuantity) {
_incrQuantity = 1;
}
if (_isDecrement) {
_incrQuantity = _incrQuantity * -1;
}
if (redisOmClient && _productId && _incrQuantity) {
const updateKey = `${ProductRepo.PRODUCT_KEY_PREFIX}:${_productId}`;
//increment json number field by specific (positive/ negative) value
await redisOmClient.redis?.json.numIncrBy(updateKey, '$.totalQuantity', _incrQuantity);
if (_isReturnProduct) {
retItem = await InventoryServiceCls.retrieveSKU(_productId);
}
}
else {
throw `Input params failed !`;
}
return retItem;
}
以下代码显示了 decrementSKU 活动的 API 请求和响应示例。
decrementSKU API 请求
POST http://localhost:3000/api/decrementSKU
{
"sku":1019688,
"quantity":4
}
decrementSKU API 响应
{
"data": {
"sku": 1019688,
"name": "5-Year Protection Plan - Geek Squad",
"type": "BlackTie",
"totalQuantity": 16 //previous value 20
},
"error": null
}
当您发出请求时,它会通过 API 网关传递到库存服务。最终,它会调用一个 decrementSKU
函数,如下所示
static async decrementSKU(_productId: number, _decrQuantity: number): Promise<IProduct> {
/**
decrement quantity of a Product.
:param _productId: Product Id
:param _decrQuantity: new decrement quantity
:return: Product with Quantity
*/
let retItem: IProduct = {};
//validating if product in stock
let isValid = await InventoryServiceCls.validateQuantityOnDecrementSKU(_productId, _decrQuantity);
if (isValid) {
const isDecrement = true; //increments with negative value
const isReturnProduct = true;
retItem = await InventoryServiceCls.incrementSKU(_productId, _decrQuantity, isDecrement, isReturnProduct);
}
return retItem;
}
static async validateQuantityOnDecrementSKU(_productId: number, _decrQuantity?: number): Promise<boolean> {
let isValid = false;
if (!_decrQuantity) {
_decrQuantity = 1;
}
if (_productId) {
const product = await InventoryServiceCls.retrieveSKU(_productId);
if (product && product.totalQuantity && product.totalQuantity > 0
&& (product.totalQuantity - _decrQuantity >= 0)) {
isValid = true;
}
else {
throw `For product with Id ${_productId}, available quantity(${product.totalQuantity}) is lesser than decrement quantity(${_decrQuantity})`;
}
}
return isValid;
}
以下代码显示了 retrieveManySKUs 活动的 API 请求和响应示例。
检索多个 SKU 的 API 请求
POST http://localhost:3000/api/retrieveManySKUs
[{
"sku":1019688
},{
"sku":1003622
},{
"sku":1006702
}]
检索多个 SKU 的 API 响应
{
"data": [
{
"sku": 1019688,
"name": "5-Year Protection Plan - Geek Squad",
"type": "BlackTie",
"totalQuantity": 24
},
{
"sku": 1003622,
"name": "Aquarius - Fender Stratocaster 1,000-Piece Jigsaw Puzzle - Black/Red/White/Yellow/Green/Orange/Blue",
"type": "HardGood",
"totalQuantity": 10
},
{
"sku": 1006702,
"name": "Clash of the Titans [DVD] [2010]",
"type": "Movie",
"totalQuantity": 10
}
],
"error": null
}
当您发出请求时,它会通过 API 网关到达库存服务。最终,它会调用一个名为 retrieveManySKUs
的函数,如下所示
static async retrieveManySKUs(_productWithIds: IProductBodyFilter[]): Promise<IProduct[]> {
/**
Get current Quantity of specific Products.
:param _productWithIds: Product list with Id
:return: Product list
*/
const repository = ProductRepo.getRepository();
let retItems: IProduct[] = [];
if (repository && _productWithIds && _productWithIds.length) {
//string id array
const idArr = _productWithIds.map((product) => {
return product.sku?.toString() || ""
});
//fetch products by IDs (using redis om library)
const result = await repository.fetch(...idArr);
let productsArr: IProduct[] = [];
if (idArr.length == 1) {
productsArr = [<IProduct>result];
}
else {
productsArr = <IProduct[]>result;
}
if (productsArr && productsArr.length) {
retItems = productsArr.map((product) => {
return {
sku: product.sku,
name: product.name,
type: product.type,
totalQuantity: product.totalQuantity
}
});
}
else {
throw `No products found !`;
}
}
else {
throw `Input params failed !`;
}
return retItems;
}
以下代码展示了 DecrementManySKUs 活动的 API 请求和响应示例。
DecrementManySKUs API 请求
POST http://localhost:3000/api/decrementManySKUs
[{
"sku":1019688,
"quantity":4
},{
"sku":1003622,
"quantity":2
},{
"sku":1006702,
"quantity":2
}]
DecrementManySKUs API 响应
{
"data": [
{
"sku": 1019688,
"name": "5-Year Protection Plan - Geek Squad",
"type": "BlackTie",
"totalQuantity": 28 //previous value 32
},
{
"sku": 1003622,
"name": "Aquarius - Fender Stratocaster 1,000-Piece Jigsaw Puzzle - Black/Red/White/Yellow/Green/Orange/Blue",
"type": "HardGood",
"totalQuantity": 8 //previous value 10
},
{
"sku": 1006702,
"name": "Clash of the Titans [DVD] [2010]",
"type": "Movie",
"totalQuantity": 8 //previous value 10
}
],
"error": null
}
当您发出请求时,它会通过 API 网关到达库存服务。最终,它会调用一个名为 decrementManySKUs
的函数,如下所示
static async decrementManySKUs(_productsFilter: IProductBodyFilter[]): Promise<IProduct[]> {
/**
decrement quantity of specific Products.
:param _productWithIds: Product list with Id
:return: Product list
*/
let retItems: IProduct[] = [];
if (_productsFilter && _productsFilter.length) {
//validation only
const promArr: Promise<boolean>[] = [];
for (let p of _productsFilter) {
if (p.sku) {
//validating if all products in stock
const promObj = InventoryServiceCls.validateQuantityOnDecrementSKU(p.sku, p.quantity);
promArr.push(promObj)
}
}
await Promise.all(promArr);
//decrement only
const promArr2: Promise<IProduct>[] = [];
for (let p of _productsFilter) {
if (p.sku && p.quantity) {
const isDecrement = true; //increments with negative value
const isReturnProduct = false;
const promObj2 = InventoryServiceCls.incrementSKU(p.sku, p.quantity, isDecrement, isReturnProduct);
promArr2.push(promObj2)
}
}
await Promise.all(promArr2);
//retrieve updated products
retItems = await InventoryServiceCls.retrieveManySKUs(_productsFilter);
}
else {
throw `Input params failed !`;
}
return retItems;
}
希望本教程能帮助您了解如何在实时库存系统中使用 Redis 来实现不同门店的产品可用性。有关此主题的更多资源,请查看以下链接