728x90
0. Directory Structure
kiosk
├─ .prettierrc.cjs
├─ README.md
├─ migrations
├─ package-lock.json
├─ package.json
├─ seeders
└─ src
├─ app.js
├─ controllers
│ ├─ itmes.controller.js
│ └─ order_items.controller.js
├─ db
│ ├─ index.js
│ ├─ models
│ │ ├─ items.js
│ │ └─ order_items.js
│ ├─ relations
│ │ ├─ index.js
│ │ ├─ items.relation.js
│ │ └─ order_items.relation.js
│ └─ sequelize.js
├─ init.js
├─ repositories
│ ├─ items.repository.js
│ └─ order_items.repository.js
├─ routes
│ ├─ items.route.js
│ └─ order_items.route.js
└─ services
├─ items.service.js
├─ message.js
└─ order_items.repository.js
1. 발주 테이블 스키마
1-1. src/db/models/order_items.js
import { Model, DataTypes } from 'sequelize';
import sequelize from '../sequelize.js';
class Order_Items extends Model {}
Order_Items.init(
{
id: { type: DataTypes.BIGINT, autoIncrement: true, primaryKey: true },
item_id: DataTypes.BIGINT,
amount: { type: DataTypes.BIGINT, defaultValue: 0 },
state: { type: DataTypes.BIGINT, defaultValue: 0 },
createdAt: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
},
{
sequelize,
modelName: 'Order_Items',
},
);
export default Order_Items;
1-2. src/db/relations/items.relation.js
import Order_Items from '../models/order_items.js';
import Items from '../models/items.js';
export default () => {
Items.hasMany(Order_Items, {
sourceKey: 'id',
foreignKey: 'item_id',
});
};
1-3. src/db/relations/order_items.relation.js
import Order_Items from '../models/order_items.js';
import Items from '../models/items.js';
export default () => {
Order_Items.belongsTo(Items, {
targetKey: 'id',
foreignKey: 'item_id',
});
};
1-4. src/db/relation/index.js
import ItemsRelations from './items.relation.js';
import Order_ItemsRelations from './order_items.relation.js';
export default {
ItemsRelations,
Order_ItemsRelations,
};
1-5. src/db/index.js 수정
import sequelize from './sequelize.js';
import Items from './models/items.js';
import Order_Items from './models/order_items.js';
import Relations from './relations/index.js';
Object.values(Relations).forEach(relationsFunction => {
relationsFunction();
});
export { sequelize, Items, Order_Items };
1-6. src/db/models/items.js 수정
import { Model, DataTypes } from 'sequelize';
import sequelize from '../sequelize.js';
class Items extends Model {}
Items.init(
{
id: { type: DataTypes.BIGINT, autoIncrement: true, primaryKey: true },
name: DataTypes.STRING,
option_id: { type: DataTypes.BIGINT, defaultValue: 0 },
price: DataTypes.BIGINT,
type: { type: DataTypes.ENUM, values: ['COFFEE', 'JUICE', 'FOOD'] },
amount: { type: DataTypes.BIGINT, defaultValue: 0 },
createdAt: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
},
{
sequelize,
modelName: 'Items',
},
);
export default Items;
1-7. init.js :27 sequelize.sync({force : true})를 통해 스키마 만들기
2. 상품 발주 API
2-1. order_items.route.js
import { Router } from 'express';
import Order_ItemsController from '../controllers/order_items.controller.js';
const router = Router();
const order_itemsController = new Order_ItemsController();
router.post('/items/:item_id/orders', order_itemsController.makeOrder);
export default router;
2-2. app.js
import express from 'express';
import itemsRouter from './routes/items.route.js';
import order_itemsRouter from './routes/order_items.route.js';
export class ExpressApp {
app = express();
constructor() {
this.setAppSettings();
this.setAppRouter();
}
setAppSettings = () => {
this.app.use(express.json());
};
setAppRouter = () => {
this.app.use(
'/api',
[itemsRouter, order_itemsRouter],
(error, request, response, next) => {
response.status(400).json({
success: false,
error: error.message,
});
},
);
this.app.use('/ping', (req, res, next) => {
return res.status(200).json({ message: 'pong' });
});
};
}
2-3. order_items.controller.js
import Order_ItemsService from '../services/order_items.service.js';
class Order_ItemsController {
order_itemsService = new Order_ItemsService();
makeOrder = async (req, res) => {
const { item_id } = req.params;
const { amount } = req.body;
const { status, message, order } = await this.order_itemsService.makeOrder(
item_id,
amount,
);
return res.status(status).json({ message, order });
};
}
export default Order_ItemsController;
2-4. order_items.service.js
import Messages from './message.js';
import Order_ItemsRepository from '../repositories/order_items.repository.js';
const noid = new Messages('상품 id');
const noamount = new Messages('수량');
class Order_ItemsService {
order_itemRepository = new Order_ItemsRepository();
makeOrder = async (item_id, amount) => {
const messages = new Messages('상품 발주');
try {
if (!item_id) {
return noid.nosubject();
} else if (!amount) {
return noamount.nosubject();
}
const order = await this.order_itemRepository.makeOrder(item_id, amount);
if (order) {
return {
status: 200,
message: '상품 발주에 성공하였습니다.',
order,
};
} else {
return messages.status400();
}
} catch (err) {
console.log(err);
return messages.status400();
}
};
}
export default Order_ItemsService;
2-5. order_items.repository.js
import Order_Items from '../db/models/order_items.js';
class Order_ItemsRepository {
makeOrder = async (item_id, amount) => {
const order = await Order_Items.create({
item_id,
amount,
});
return order;
};
}
export default Order_ItemsRepository;
3. 발주 상태 수정 API & enum.js 추가
enum.js
const itemTypes = {
coffee: 'COFFEE',
juice: 'JUICE',
food: 'FOOD',
};
const orderItemState = {
ORDERED: 0,
PENDING: 1,
COMPLETED: 2,
CANCELED: 3,
};
export default { itemTypes, orderItemState };
items.service.js
import Messages from './message.js';
import ItemsRepository from '../repositories/items.repository.js';
import Enum from '../db/models/enum.js';
const noname = new Messages('이름');
const noprice = new Messages('가격');
class ItemsService {
itemsRepository = new ItemsRepository();
makeItem = async (name, price, type) => {
const messages = new Messages('상품 추가');
try {
if (!name.length) {
return noname.nosubject();
} else if (!price) {
return noprice.nosubject();
} else if (Enum.itemTypes[type] == undefined) {
return {
status: 400,
message: '알맞은 타입을 지정해 주세요.',
};
}
const item = await this.itemsRepository.makeItem(
name,
price,
Enum.itemTypes[type],
);
if (item.name) {
return messages.status200();
} else {
return messages.status400();
}
} catch (err) {
console.log(err);
return messages.status400();
}
};
getItemList = async category => {
try {
if (category == 'all') {
const allItemList = await this.itemsRepository.getAllItemList();
return {
status: 200,
message: '전체 상품이 조회되었습니다.',
list: allItemList,
};
} else {
const itemList = await this.itemsRepository.getItemList(category);
return {
status: 200,
message: `${category} 타입의 상품이 조회되었습니다.`,
list: itemList,
};
}
} catch (err) {
return {
status: 400,
message: '상품 조회에 실패하였습니다.',
list: null,
};
}
};
removeItem = async id => {
const messages = new Messages('상품 삭제');
try {
const checkItem = await this.itemsRepository.checkamount(id);
if (checkItem.amount > 0) {
return {
status: 200,
message: '현재 수량이 남아있습니다. 삭제하시겠습니까?',
};
} else if (checkItem.amount == 0) {
const removeItem = await this.itemsRepository.removeItem(id);
if (removeItem) {
return messages.status200();
}
}
} catch (err) {
return messages.status400();
}
};
answerRemoveItem = async (id, answer) => {
const messages = new Messages('상품 삭제');
try {
if (answer == '예') {
const removeItem = await this.itemsRepository.removeItem(id);
if (removeItem) {
return messages.status200();
}
} else {
return messages.status400();
}
} catch (err) {
return messages.status400();
}
};
editItem = async (id, name, price) => {
const messages = new Messages('상품 수정');
try {
if (!name.length) {
return noname.nosubject();
} else if (!price) {
return noprice.nosubject();
} else if (price < 0) {
return {
status: 400,
message: '알맞은 가격을 입력해주세요.',
};
}
const item = await this.itemsRepository.editItem(id, name, price);
if (item) {
return messages.status200();
} else {
return messages.status400();
}
} catch (err) {
return messages.status400();
}
};
}
export default ItemsService;
order_items.controller.js
import Order_ItemsService from '../services/order_items.service.js';
class Order_ItemsController {
order_itemsService = new Order_ItemsService();
makeOrder = async (req, res) => {
const { item_id } = req.params;
const { amount } = req.body;
const { status, message, order } = await this.order_itemsService.makeOrder(
item_id,
amount,
);
return res.status(status).json({ message, order });
};
editOrderState = async (req, res) => {
const { item_id, id } = req.params;
const { state } = req.body;
const { status, message } = await this.order_itemsService.editOrderState(
item_id,
id,
state,
);
return res.status(status).json({ message });
};
}
export default Order_ItemsController;
order_items.service.js
import Messages from './message.js';
import Order_ItemsRepository from '../repositories/order_items.repository.js';
import Enum from '../db/models/enum.js';
const noid = new Messages('상품 id');
const noamount = new Messages('수량');
const noorderid = new Messages('발주 id');
const nostate = new Messages('상품 상태');
const noenumstate = new Messages('정확한 상품 상태');
class Order_ItemsService {
order_itemRepository = new Order_ItemsRepository();
makeOrder = async (item_id, amount) => {
const messages = new Messages('상품 발주');
try {
if (!item_id) {
return noid.nosubject();
} else if (!amount) {
return noamount.nosubject();
}
const order = await this.order_itemRepository.makeOrder(item_id, amount);
if (order) {
return {
status: 200,
message: '상품 발주에 성공하였습니다.',
order,
};
} else {
return messages.status400();
}
} catch (err) {
console.log(err);
return messages.status400();
}
};
editOrderState = async (item_id, id, state) => {
const messages = new Messages('상품 발주 상태 수정');
try {
if (!item_id) {
return noid.nosubject();
} else if (!id) {
return noorderid.nosubject();
} else if (!state) {
return nostate.nosubject();
} else if (Enum.orderItemState[state] == undefined) {
return noenumstate.nosubject();
}
const prevstate = await this.order_itemRepository.stateChecker(
id,
item_id,
);
if (!prevstate.item_id) {
return messages.status400;
}
if (
(prevstate.state == 0 && state == 'PENDING') ||
(prevstate.state == 1 && state == 'CANCELED') ||
(prevstate.state == 0 && state == 'CANCELED')
) {
const nextstate = await this.order_itemRepository.editOrderState(
id,
Enum.orderItemState[state],
);
return messages.status200();
} else if (prevstate.state == 1 && state == 'COMPLETED') {
const item = await this.order_itemRepository.ItemChecker(item_id);
const updateamount = item.amount + prevstate.amount;
const pendingToCompleted =
await this.order_itemRepository.pendingToCompleted(
id,
item_id,
Enum.orderItemState[state],
updateamount,
);
if (pendingToCompleted.result == 1) {
return messages.status200();
}
} else if (
(prevstate.state == 2 && state == 'CANCELED') ||
(prevstate.state == 2 && state == 'PENDING') ||
(prevstate.state == 2 && state == 'ORDERED')
) {
const item = await this.order_itemRepository.ItemChecker(item_id);
const updateamount = item.amount - prevstate.amount;
if (updateamount < 0) {
return {
status: 400,
message: '현재 수량이 발주 수량보다 적어 발주 취소가 불가능합니다.',
};
}
const pendingToCompleted =
await this.order_itemRepository.pendingToCompleted(
id,
item_id,
Enum.orderItemState[state],
updateamount,
);
if (pendingToCompleted.result == 1) {
return messages.status200();
}
} else {
messages.status400();
}
} catch (err) {
console.log(err);
return messages.status400();
}
};
}
export default Order_ItemsService;
order_items.repository.js
import Order_Items from '../db/models/order_items.js';
import Items from '../db/models/items.js';
import { Transaction } from 'sequelize';
import sequelize from '../db/sequelize.js';
class Order_ItemsRepository {
makeOrder = async (item_id, amount) => {
const order = await Order_Items.create({
item_id,
amount,
});
return order;
};
stateChecker = async (id, item_id) => {
const prevstate = await Order_Items.findOne({ where: { item_id, id } });
return prevstate;
};
editOrderState = async (id, state) => {
const nextstate = await Order_Items.update({ state }, { where: { id } });
return nextstate;
};
ItemChecker = async item_id => {
const item = await Items.findByPk(item_id);
return item;
};
pendingToCompleted = async (id, item_id, state, updateamount) => {
const t = await sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED,
});
try {
const orderUpdate = await Order_Items.update(
{ state },
{ where: { id } },
{ transaction: t },
);
const itemUpdate = await Items.update(
{ amount: updateamount },
{ where: { id: item_id } },
{ transaction: t },
);
await t.commit();
return { result: 1, nextstate: orderUpdate };
} catch (err) {
console.log(err);
await t.rollback();
return { result: 0, nextstate: null };
}
};
}
export default Order_ItemsRepository;
order_items.route.js
import { Router } from 'express';
import Order_ItemsController from '../controllers/order_items.controller.js';
const router = Router();
const order_itemsController = new Order_ItemsController();
router.post('/items/:item_id/orders', order_itemsController.makeOrder);
router.patch(
'/items/:item_id/orders/:id',
order_itemsController.editOrderState,
);
export default router;
'내일 배움 캠프 > kiosk' 카테고리의 다른 글
kiosk 6일차 8/1 주문 관리 (2) (0) | 2023.07.28 |
---|---|
kiosk 5일차 7/31 주문 관리 (0) | 2023.07.28 |
kiosk 3일차 7/27 상품 관리 (2) (0) | 2023.07.26 |
kiosk 2일차 7/26 상품관리 (0) | 2023.07.26 |
kiosk 1일차 7/25 프로젝트 세팅 (0) | 2023.07.25 |