개인정보-컴플라이언스-웹애플리케이션(8) - (피드백) 백엔드 코드

2025. 1. 26. 19:52프로젝트

728x90

이 코드는 관리자(전문가) 가 유저가 작성한 자가진단에 대해 피드백을 남겨줄수 있게 해주는 api입니다.

코드는 다음과 같습니다.

import pool from "../db/connection.js";

/**
 * 🔹 전문가가 배정된 시스템 목록 조회
 */
const getAssignedSystems = async (req, res) => {
  const { expertId } = req.query;

  console.log("✅ [getAssignedSystems] Received expertId:", expertId);

  if (!expertId) {
    return res.status(400).json({
      resultCode: "F-1",
      msg: "전문가 ID가 필요합니다.",
    });
  }

  try {
    const query = `
        SELECT s.id AS system_id, s.name AS system_name, u.institution_name, 
               ar.score, ar.grade, ar.feedback_status
        FROM assignment a
        JOIN systems s ON a.systems_id = s.id
        JOIN User u ON s.user_id = u.id
        LEFT JOIN assessment_result ar ON s.id = ar.system_id
        WHERE a.expert_id = ?;
      `;

    console.log("🟡 [getAssignedSystems] Running query:", query);

    const [results] = await pool.query(query, [expertId]);

    console.log("✅ [getAssignedSystems] Query results:", results);

    res.status(200).json({
      resultCode: "S-1",
      msg: "매칭된 시스템 조회 성공",
      data: results,
    });
  } catch (error) {
    console.error(
      "❌ [getAssignedSystems] 배정된 시스템 조회 실패:",
      error.message
    );
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  }
};

/**
 * 🔹 특정 시스템의 자가진단 결과 조회
 */
const getSystemAssessmentResult = async (req, res) => {
  const { systemId } = req.query;

  console.log("✅ [getSystemAssessmentResult] Received systemId:", systemId);

  if (!systemId) {
    return res.status(400).json({
      resultCode: "F-1",
      msg: "시스템 ID가 필요합니다.",
    });
  }

  try {
    const query = `
      SELECT ar.id AS assessment_id, ar.system_id, ar.user_id, ar.score, 
             ar.grade, ar.feedback_status, ar.completed_at, u.institution_name
      FROM assessment_result ar
      JOIN systems s ON ar.system_id = s.id
      JOIN User u ON s.user_id = u.id
      WHERE ar.system_id = ?;
    `;

    console.log("🟡 [getSystemAssessmentResult] Running query:", query);

    const [results] = await pool.query(query, [systemId]);

    console.log("✅ [getSystemAssessmentResult] Query results:", results);

    if (results.length === 0) {
      console.warn(
        "⚠️ [getSystemAssessmentResult] No results found for systemId:",
        systemId
      );
      return res.status(404).json({
        resultCode: "F-1",
        msg: "자가진단 결과를 찾을 수 없습니다.",
      });
    }

    res.status(200).json({
      resultCode: "S-1",
      msg: "자가진단 결과 조회 성공",
      data: results[0],
    });
  } catch (error) {
    console.error(
      "❌ [getSystemAssessmentResult] 자가진단 결과 조회 실패:",
      error.message
    );
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  }
};

const SystemsResult = async (req, res) => {
  const { userId } = req.query;

  if (!userId) {
    return res.status(400).json({
      resultCode: "F-1",
      msg: "기관회원 ID가 필요합니다.",
    });
  }

  try {
    const query = `
      SELECT s.id AS system_id, s.name AS system_name, 
             ar.score, ar.grade, ar.feedback_status, ar.completed_at,
             f.feedback_content, e.name AS expert_name
      FROM systems s
      LEFT JOIN assessment_result ar ON s.id = ar.system_id
      LEFT JOIN assignment a ON s.id = a.systems_id
      LEFT JOIN feedback f ON ar.id = f.assessment_result_id
      LEFT JOIN expert e ON a.expert_id = e.id
      WHERE s.user_id = ?;
    `;

    const [results] = await pool.query(query, [userId]);

    res.status(200).json({
      resultCode: "S-1",
      msg: "시스템 결과 조회 성공",
      data: results,
    });
  } catch (error) {
    console.error("기관회원 시스템 결과 조회 실패:", error.message);
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  }
};
const updateQuantitativeFeedback = async (req, res) => {
  const { systemId, feedbackResponses } = req.body;

  if (!systemId || !Array.isArray(feedbackResponses)) {
    console.error("Invalid data format:", { systemId, feedbackResponses });
    return res.status(400).json({
      resultCode: "F-1",
      msg: "잘못된 요청 형식입니다. 'systemId' 및 'feedbackResponses'가 필요합니다.",
    });
  }

  console.log("Received systemId:", systemId);
  console.log("Received feedbackResponses:", feedbackResponses);

  try {
    const query = `
      INSERT INTO quantitative (
        question_number, system_id, feedback
      )
      VALUES (?, ?, ?)
      ON DUPLICATE KEY UPDATE feedback = VALUES(feedback);
    `;

    const connection = await pool.getConnection();
    await connection.beginTransaction();

    for (const { questionNumber, feedback } of feedbackResponses) {
      await connection.query(query, [
        questionNumber,
        systemId,
        feedback || "피드백 없음", // 기본값 설정
      ]);
    }

    await connection.commit();
    connection.release();

    res.status(200).json({
      resultCode: "S-1",
      msg: "정량 피드백 업데이트 성공",
    });
  } catch (error) {
    console.error("Error updating feedback:", error.message);
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  }
};

/**
 * 🔹 정성 피드백 업데이트
 */
const updateQualitativeFeedback = async (req, res) => {
  const { systemId, feedbackResponses } = req.body;

  if (!systemId || !feedbackResponses || !Array.isArray(feedbackResponses)) {
    console.error("Invalid request data:", { systemId, feedbackResponses });
    return res.status(400).json({
      resultCode: "F-1",
      msg: "잘못된 요청 형식입니다. 'systemId' 및 'feedbackResponses'가 필요합니다.",
    });
  }

  const connection = await pool.getConnection();

  try {
    const query = `
      UPDATE qualitative
      SET feedback = ?, additional_comment = ?, response = ?
      WHERE question_number = ? AND system_id = ?
    `;

    await connection.beginTransaction();

    for (const response of feedbackResponses) {
      const {
        questionNumber,
        feedback,
        additionalComment,
        response: userResponse,
      } = response;

      if (
        typeof questionNumber !== "number" ||
        typeof feedback !== "string" ||
        typeof additionalComment !== "string" ||
        typeof userResponse !== "string"
      ) {
        console.error("Invalid feedback response:", response);
        throw new Error("피드백 데이터 형식이 잘못되었습니다.");
      }

      await connection.query(query, [
        feedback,
        additionalComment,
        userResponse,
        questionNumber,
        systemId,
      ]);
    }

    console.log("Feedbacks updated successfully for system_id:", systemId);

    // ✅ 전문가 자문 상태 업데이트
    const updateStatusQuery = `
      UPDATE assessment_result
      SET feedback_status = '전문가 자문이 반영되었습니다'
      WHERE system_id = ?
    `;

    const [updateResult] = await connection.query(updateStatusQuery, [
      systemId,
    ]);
    console.log(
      "Feedback status updated:",
      updateResult.affectedRows,
      "rows affected"
    );

    await connection.commit();
    console.log("Transaction committed successfully");

    res.status(200).json({
      resultCode: "S-1",
      msg: "정성 피드백 및 상태 업데이트 성공",
    });
  } catch (error) {
    await connection.rollback();
    console.error("정성 피드백 업데이트 실패:", error.message);
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  } finally {
    connection.release();
  }
};

const updateFeedbackStatus = async (req, res) => {
  const { systemId } = req.body;

  if (!systemId) {
    return res.status(400).json({
      resultCode: "F-1",
      msg: "시스템 ID가 필요합니다.",
    });
  }

  const query = `
    UPDATE assessment_result
    SET feedback_status = '전문가 자문이 반영되었습니다'
    WHERE system_id = ?
  `;

  try {
    const [result] = await pool.query(query, [systemId]);
    console.log(`Feedback status updated:`, result);
    if (result.affectedRows === 0) {
      return res.status(404).json({
        resultCode: "F-1",
        msg: "해당 시스템 ID에 대한 결과를 찾을 수 없습니다.",
      });
    }

    res.status(200).json({
      resultCode: "S-1",
      msg: "피드백 상태 업데이트 성공",
    });
  } catch (error) {
    console.error("Error updating feedback status:", error.message);
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  }
};

export {
  getAssignedSystems,
  getSystemAssessmentResult,
  SystemsResult,
  updateQuantitativeFeedback,
  updateQualitativeFeedback,
  updateFeedbackStatus,
};

 

1. getAssignedSystems 전문가가 배정된 시스템 목록 조회

const getAssignedSystems = async (req, res) => {
  const { expertId } = req.query;

  console.log("✅ [getAssignedSystems] Received expertId:", expertId);

  if (!expertId) {
    return res.status(400).json({
      resultCode: "F-1",
      msg: "전문가 ID가 필요합니다.",
    });
  }

  try {
    const query = `
        SELECT s.id AS system_id, s.name AS system_name, u.institution_name, 
               ar.score, ar.grade, ar.feedback_status
        FROM assignment a
        JOIN systems s ON a.systems_id = s.id
        JOIN User u ON s.user_id = u.id
        LEFT JOIN assessment_result ar ON s.id = ar.system_id
        WHERE a.expert_id = ?;
      `;

    console.log("🟡 [getAssignedSystems] Running query:", query);

    const [results] = await pool.query(query, [expertId]);

    console.log("✅ [getAssignedSystems] Query results:", results);

    res.status(200).json({
      resultCode: "S-1",
      msg: "매칭된 시스템 조회 성공",
      data: results,
    });
  } catch (error) {
    console.error(
      "❌ [getAssignedSystems] 배정된 시스템 조회 실패:",
      error.message
    );
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  }
};

 

const query = `
  SELECT s.id AS system_id, s.name AS system_name, u.institution_name, 
         ar.score, ar.grade, ar.feedback_status
  FROM assignment a
  JOIN systems s ON a.systems_id = s.id
  JOIN User u ON s.user_id = u.id
  LEFT JOIN assessment_result ar ON s.id = ar.system_id
  WHERE a.expert_id = ?;
`;

 

전문가(expertId)가 배정된 시스템(assignment 테이블)을 조회.
결과에 시스템 이름, 기관 이름, 진단 결과(점수, 등급, 피드백 상태) 포함.
LEFT JOIN: assessment_result 데이터가 없어도 기본 시스템 정보를 반환.

 

LEFT JOIN이란?
두 개의 테이블을 연결할 때, 왼쪽 테이블(기준 테이블)에 있는 데이터를 모두 보여주고, 오른쪽 테이블의 일치하는 데이터만 추가로 가져오는 방식입니다.

 

 

2. getSystemAssessmentResult 특정 시스템의 자가진단 결과 조회

const getSystemAssessmentResult = async (req, res) => {
  const { systemId } = req.query;

  console.log("✅ [getSystemAssessmentResult] Received systemId:", systemId);

  if (!systemId) {
    return res.status(400).json({
      resultCode: "F-1",
      msg: "시스템 ID가 필요합니다.",
    });
  }

  try {
    const query = `
      SELECT ar.id AS assessment_id, ar.system_id, ar.user_id, ar.score, 
             ar.grade, ar.feedback_status, ar.completed_at, u.institution_name
      FROM assessment_result ar
      JOIN systems s ON ar.system_id = s.id
      JOIN User u ON s.user_id = u.id
      WHERE ar.system_id = ?;
    `;

    console.log("🟡 [getSystemAssessmentResult] Running query:", query);

    const [results] = await pool.query(query, [systemId]);

    console.log("✅ [getSystemAssessmentResult] Query results:", results);

    if (results.length === 0) {
      console.warn(
        "⚠️ [getSystemAssessmentResult] No results found for systemId:",
        systemId
      );
      return res.status(404).json({
        resultCode: "F-1",
        msg: "자가진단 결과를 찾을 수 없습니다.",
      });
    }

    res.status(200).json({
      resultCode: "S-1",
      msg: "자가진단 결과 조회 성공",
      data: results[0],
    });
  } catch (error) {
    console.error(
      "❌ [getSystemAssessmentResult] 자가진단 결과 조회 실패:",
      error.message
    );
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  }
};

 

const query = `
  SELECT ar.id AS assessment_id, ar.system_id, ar.user_id, ar.score, 
         ar.grade, ar.feedback_status, ar.completed_at, u.institution_name
  FROM assessment_result ar
  JOIN systems s ON ar.system_id = s.id
  JOIN User u ON s.user_id = u.id
  WHERE ar.system_id = ?;
`;

 

JOIN systems s ON ar.system_id = s.id

 

systems 테이블과 조인: 자가진단 결과(assessment_result)의 system_id와 시스템 테이블의 id를 연결합니다
역할: 자가진단 결과가 어떤 시스템과 관련 있는지 가져옵니다.

ar.system_id = s.id
assessment_result.system_id와 systems.id가 같은 경우 데이터를 조인.

 

JOIN User u ON s.user_id = u.id

 

User 테이블과 조인:
systems 테이블의 user_id와 사용자 테이블의 id를 연결합니다.
역할: 시스템을 등록한 기관(사용자)의 정보를 가져옵니다.
s.user_id = u.id
systems.user_id와 User.id가 같은 경우 데이터를 조인.

 

3. updateQuantitativeFeedback, updateQualitativeFeedback

정량, 정성 피드백 업데이트

const updateQuantitativeFeedback = async (req, res) => {
  const { systemId, feedbackResponses } = req.body;

  if (!systemId || !Array.isArray(feedbackResponses)) {
    console.error("Invalid data format:", { systemId, feedbackResponses });
    return res.status(400).json({
      resultCode: "F-1",
      msg: "잘못된 요청 형식입니다. 'systemId' 및 'feedbackResponses'가 필요합니다.",
    });
  }

  console.log("Received systemId:", systemId);
  console.log("Received feedbackResponses:", feedbackResponses);

  const connection = await pool.getConnection();

  try {
    const query = `
      UPDATE quantitative
      SET feedback = ?
      WHERE question_number = ? AND system_id = ?
    `;

    await connection.beginTransaction();

    // Update quantitative feedback
    for (const { questionNumber, feedback } of feedbackResponses) {
      if (typeof questionNumber !== "number" || typeof feedback !== "string") {
        console.error("Invalid feedback response:", {
          questionNumber,
          feedback,
        });
        throw new Error("피드백 데이터 형식이 잘못되었습니다.");
      }

      await connection.query(query, [
        feedback || "피드백 없음", // 기본값 설정
        questionNumber,
        systemId,
      ]);
    }

    console.log("Feedbacks updated successfully for system_id:", systemId);

    // ✅ Update feedback status in `assessment_result`
    const updateStatusQuery = `
      UPDATE assessment_result
      SET feedback_status = '전문가 자문이 반영되었습니다'
      WHERE system_id = ?
    `;

    const [updateResult] = await connection.query(updateStatusQuery, [
      systemId,
    ]);

    console.log(
      "Feedback status updated:",
      updateResult.affectedRows,
      "rows affected"
    );

    // Commit transaction
    await connection.commit();
    console.log("Transaction committed successfully");

    res.status(200).json({
      resultCode: "S-1",
      msg: "정량 피드백 및 상태 업데이트 성공",
    });
  } catch (error) {
    await connection.rollback();
    console.error("Error updating feedback:", error.message);
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  } finally {
    connection.release();
  }
};

/**
 * 🔹 정성 피드백 업데이트
 */
const updateQualitativeFeedback = async (req, res) => {
  const { systemId, feedbackResponses } = req.body;

  if (!systemId || !feedbackResponses || !Array.isArray(feedbackResponses)) {
    console.error("Invalid request data:", { systemId, feedbackResponses });
    return res.status(400).json({
      resultCode: "F-1",
      msg: "잘못된 요청 형식입니다. 'systemId' 및 'feedbackResponses'가 필요합니다.",
    });
  }

  const connection = await pool.getConnection();

  try {
    const query = `
      UPDATE qualitative
      SET feedback = ?, additional_comment = ?, response = ?
      WHERE question_number = ? AND system_id = ?
    `;

    await connection.beginTransaction();

    for (const response of feedbackResponses) {
      const {
        questionNumber,
        feedback,
        additionalComment,
        response: userResponse,
      } = response;

      if (
        typeof questionNumber !== "number" ||
        typeof feedback !== "string" ||
        typeof additionalComment !== "string" ||
        typeof userResponse !== "string"
      ) {
        console.error("Invalid feedback response:", response);
        throw new Error("피드백 데이터 형식이 잘못되었습니다.");
      }

      await connection.query(query, [
        feedback,
        additionalComment,
        userResponse,
        questionNumber,
        systemId,
      ]);
    }

    console.log("Feedbacks updated successfully for system_id:", systemId);

    // ✅ 전문가 자문 상태 업데이트
    const updateStatusQuery = `
      UPDATE assessment_result
      SET feedback_status = '전문가 자문이 반영되었습니다'
      WHERE system_id = ?
    `;

    const [updateResult] = await connection.query(updateStatusQuery, [
      systemId,
    ]);
    console.log(
      "Feedback status updated:",
      updateResult.affectedRows,
      "rows affected"
    );

    await connection.commit();
    console.log("Transaction committed successfully");

    res.status(200).json({
      resultCode: "S-1",
      msg: "정성 피드백 및 상태 업데이트 성공",
    });
  } catch (error) {
    await connection.rollback();
    console.error("정성 피드백 업데이트 실패:", error.message);
    res.status(500).json({
      resultCode: "F-1",
      msg: "서버 오류 발생",
      error: error.message,
    });
  } finally {
    connection.release();
  }
};

 

const query = `
  UPDATE quantitative
  SET feedback = ?
  WHERE question_number = ? AND system_id = ?
`;

 

UPDATE 문: quantitative 테이블에서 question_number와 system_id를 기준으로 feedback 값을 업데이트.

const connection = await pool.getConnection();
await connection.beginTransaction();

for (const { questionNumber, feedback } of feedbackResponses) {
  if (typeof questionNumber !== "number" || typeof feedback !== "string") {
    console.error("Invalid feedback response:", {
      questionNumber,
      feedback,
    });
    throw new Error("피드백 데이터 형식이 잘못되었습니다.");
  }

  await connection.query(query, [
    feedback || "피드백 없음", // 기본값 설정
    questionNumber,
    systemId,
  ]);
}

await connection.commit();
connection.release();
const connection = await pool.getConnection();
await connection.beginTransaction();

 

connection: pool.getConnection()을 호출해 데이터베이스와의 연결을 생성.
여러 작업(쿼리)을 묶어서 처리하기 위해 연결 객체를 사용.


beginTransaction(): 트랜잭션을 시작.
트랜잭션은 데이터베이스 작업을 하나의 묶음으로 처리하는 기능입니다.
이 트랜잭션이 끝나기 전까지 데이터베이스 변경 사항은 다른 사용자에게 보이지 않습니다.
목적: 모든 작업이 성공했을 때만 데이터베이스에 변경 사항을 적용(커밋) 합니다
하나라도 실패하면 이전 변경 사항을 롤백하여 데이터 일관성을 보장합니다.

 

for (const { questionNumber, feedback } of feedbackResponses) {
  if (typeof questionNumber !== "number" || typeof feedback !== "string") {
    console.error("Invalid feedback response:", {
      questionNumber,
      feedback,
    });
    throw new Error("피드백 데이터 형식이 잘못되었습니다.");
  }

  await connection.query(query, [
    feedback || "피드백 없음", // 기본값 설정
    questionNumber,
    systemId,
  ]);
}

 

for문으로 feedbackResponses 배열을 순회 합니다.

if (typeof questionNumber !== "number" || typeof feedback !== "string") {
  console.error("Invalid feedback response:", {
    questionNumber,
    feedback,
  });
  throw new Error("피드백 데이터 형식이 잘못되었습니다.");
}

 

각 questionNumber와 feedback 값이 예상된 타입인지 확인합니다.
questionNumber: 숫자여야 합니다
feedback: 문자열이어야 합니다.
유효하지 않은 데이터가 발견되면 오류를 출력하고 예외(throw new Error)를 발생시켜 트랜잭션을 중단합니다.

 

정량과 정성 비슷한 구조로 되어있어서 이해가 될겁니다.

 

마지막으로

 

6. updateFeedbackStatus 피드백 상태 업데이트

const query = `
  UPDATE assessment_result
  SET feedback_status = '전문가 자문이 반영되었습니다'
  WHERE system_id = ?;
`;

 

특정 system_id에 대한 피드백 상태를 업데이트 합니다.