diff --git a/plm-api/src/controllers/app/common/product.controller.js b/plm-api/src/controllers/app/common/product.controller.js new file mode 100644 index 0000000..48683b6 --- /dev/null +++ b/plm-api/src/controllers/app/common/product.controller.js @@ -0,0 +1,113 @@ +// src/controllers/admin/users/users.controller.js +const express = require("express"); +const router = express.Router(); +const productService = require("../../../services/product.service"); +const authMiddleware = require("../../../middleware/auth.middleware"); +const roleCheck = require("../../../middleware/roleCheck.middleware"); +const { body, param } = require("express-validator"); +const validate = require("../../../middleware/validator.middleware"); + +router.use(authMiddleware); +router.use(roleCheck(["super_admin", "company_admin"])); + +// 제품군 목록 조회 +router.get("/productList", async (req, res, next) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 10; + + const result = await productService.findAll(req.user, page, limit); + res.json(result); + } catch (error) { + next(error); + } +}); + +// Create product +router.post( + "/productCreate", + [ + body("product_name").notEmpty().withMessage("유효한 ID가 필요합니다"), + body("companyId").isUUID().withMessage("유효한 회사 ID가 필요합니다"), + validate, + ], + async (req, res, next) => { + try { + const product = await productService.createProduct(req.body, req.user); + res.status(201).json(product); + } catch (error) { + next(error); + } + } +); + +// Update productUp +router.put( + "/productUpdate/:id", + [ + param("id").isUUID().withMessage("유효한 ID가 필요합니다"), + body("product_code").optional().notEmpty().withMessage("제품 코드가 필요합니다."), + body("product_name").optional().notEmpty().withMessage("제품 이름이 필요합니다."), + body("companyId").optional().isUUID().withMessage("유효한 회사 ID가 필요합니다"), + validate, + ], + async (req, res, next) => { + try { + const product = await productService.findById(req.params.id); + + if (!product) { + return res.status(404).json({ message: "고객사를 찾을 수 없습니다" }); + } + + // company_admin은 자신의 회사 고객사만 수정 가능 + if ( + req.user.role === "company_admin" && + product.companyId !== req.user.companyId + ) { + return res.status(403).json({ + message: "다른 회사의 제품군을 수정할 수 없습니다", + }); + } + + const updatedProduct = await productService.updateProduct(req.params.id, req.body, req.user); + res.json(updatedProduct); + } catch (error) { + next(error); + } + } +); + +// Delete productUp +router.delete( + "/productDelete/:id", + [ + param("id").isUUID().withMessage("유효한 ID가 필요합니다"), + validate, + ], + async (req, res, next) => { + try { + const product = await productService.findById(req.params.id); + + if (!product) { + return res.status(404).json({ message: "제품군을 찾을 수 없습니다" }); + } + + // company_admin은 자신의 회사 고객사만 삭제 가능 + if ( + req.user.role === "company_admin" && + product.companyId !== req.user.companyId + ) { + return res.status(403).json({ + message: "다른 회사의 제품군을 삭제할 수 없습니다", + }); + } + + await productService.deleteProduct(req.params.id, req.user); + res.status(204).end(); + } catch (error) { + next(error); + } + } +); + +module.exports = router; diff --git a/plm-api/src/routes/app.js b/plm-api/src/routes/app.js index 87c2517..8ec4c3c 100644 --- a/plm-api/src/routes/app.js +++ b/plm-api/src/routes/app.js @@ -19,6 +19,7 @@ const deviceController = require("../controllers/app/device/device.controller"); const commonController = require("../controllers/app/common/common.controller"); const oemMngController = require("../controllers/app/common/oemmng.controller"); const productgroupController = require("../controllers/app/common/productgroup.controller"); +const productController = require("../controllers/app/common/product.controller"); router.use("/health", healthController); router.use("/auth", authController); @@ -37,5 +38,6 @@ router.use("/devices", deviceController); router.use("/common", commonController); router.use("/oemmng", oemMngController); router.use("/productgroup", productgroupController); +router.use("/product", productController); module.exports = router; diff --git a/plm-api/src/services/product.service.js b/plm-api/src/services/product.service.js new file mode 100644 index 0000000..f4f8cb4 --- /dev/null +++ b/plm-api/src/services/product.service.js @@ -0,0 +1,163 @@ +const { + Product, + Company, + Role, +} = require("../models"); +//const { Op } = require("sequelize"); +const alertService = require("./alert.service"); + +class productService { + async findAll(currentUser, page = 1, limit = 10) { + try { + // Initialize the where clause + let where = {}; + + // company_admin은 자신의 회사 유저만 조회 가능 + if (currentUser.role === "company_admin") { + where.companyId = currentUser.companyId; + } + // isActive 필터 추가 + //where.isActive = { [Op.eq]: true }; + + const offset = (page - 1) * limit; + + // 전체 개수 조회 + const count = await Product.count({ where }); + + const products = await Product.findAll({ + where, + include: [ + { + model: Company, + attributes: ["id", "name"], + }, + ], + order: [["createdAt", "DESC"]], + offset, + limit, + distinct: true, + }); + + // 인덱스 추가 + const productsWithIndex = products.map((product, index) => { + const productJson = product.toJSON(); + productJson.index = offset + index + 1; + return productJson; + }); + + return { + total: count, + totalPages: Math.ceil(count / limit), + currentPage: page, + products: productsWithIndex, + }; + } catch (error) { + console.error("Error in findAll:", error); + throw error; + } + } + + async findById(id) { + return await Product.findByPk(id, { + include: [ + { + model: Company, + attributes: ["id", "name"], + }, + ], + }); + } + + async createProduct(productData, currentUser) { + const { roleId, ...productFields } = productData; + + // 등록자 정보 추가 + productFields.writer = currentUser.name; + productFields.regdate = new Date(); + + const product = await Product.create(productFields); + + if (roleId) { + const role = await Role.findOne({ + where: { + id: roleId, + companyId: product.companyId, + }, + }); + + if (!role) { + throw new Error("Role not found or does not belong to the company"); + } + + await product.addRole(role); + } + + await alertService.createAlert({ + type: "info", + message: `제품군 ${product.name}이(가) ${currentUser.name}에 의해 생성되었습니다.`, + companyId: product.companyId, + }); + + return product; + } + + async updateProduct(id, updateData, currentUser) { + const { roleId, ...productFields } = updateData; + + const product = await Product.findByPk(id); + if (!product) throw new Error("Product not found"); + + // company_admin은 특정 필드를 수정할 수 없음 (예: role을 super_admin으로 변경 불가) + if (currentUser.role === "company_admin") { + if (updateData.role && updateData.role === "super_admin") { + throw new Error("super_admin 역할을 부여할 수 없습니다"); + } + updateData.companyId = currentUser.companyId; + } + + const updatedProduct = await product.update(productFields); + + if (roleId) { + const role = await Role.findOne({ + where: { + id: roleId, + companyId: product.companyId, + }, + }); + + if (!role) { + throw new Error("Role not found or does not belong to the company"); + } + + await product.addRole(role); + } + + await alertService.createAlert({ + type: "info", + message: `제품군 ${product.name}이(가) ${currentUser.name}에 의해 수정되었습니다.`, + companyId: product.companyId, + }); + + return updatedProduct; + } + + async deleteProduct(id, currentUser) { + const product = await Product.findByPk(id); + if (!product) throw new Error("Product not found"); + + const productName = product.product_name; + const companyId = product.companyId; + + await product.destroy(); + + await alertService.createAlert({ + type: "info", + message: `제품군 ${productName}이(가) ${currentUser.name}에 의해 삭제되었습니다.`, + companyId: companyId, + }); + + return true; + } +} + +module.exports = new productService(); \ No newline at end of file