Merge branch 'main' into feature/screen-management
This commit is contained in:
@@ -73,6 +73,7 @@ import entitySearchRoutes from "./routes/entitySearchRoutes"; // 엔티티 검
|
||||
import orderRoutes from "./routes/orderRoutes"; // 수주 관리
|
||||
import screenEmbeddingRoutes from "./routes/screenEmbeddingRoutes"; // 화면 임베딩 및 데이터 전달
|
||||
import vehicleTripRoutes from "./routes/vehicleTripRoutes"; // 차량 운행 이력 관리
|
||||
import driverRoutes from "./routes/driverRoutes"; // 공차중계 운전자 관리
|
||||
import { BatchSchedulerService } from "./services/batchSchedulerService";
|
||||
// import collectionRoutes from "./routes/collectionRoutes"; // 임시 주석
|
||||
// import batchRoutes from "./routes/batchRoutes"; // 임시 주석
|
||||
@@ -238,6 +239,7 @@ app.use("/api/code-merge", codeMergeRoutes); // 코드 병합
|
||||
app.use("/api/numbering-rules", numberingRuleRoutes); // 채번 규칙 관리
|
||||
app.use("/api/entity-search", entitySearchRoutes); // 엔티티 검색
|
||||
app.use("/api/orders", orderRoutes); // 수주 관리
|
||||
app.use("/api/driver", driverRoutes); // 공차중계 운전자 관리
|
||||
app.use("/api", screenEmbeddingRoutes); // 화면 임베딩 및 데이터 전달
|
||||
app.use("/api/vehicle", vehicleTripRoutes); // 차량 운행 이력 관리
|
||||
// app.use("/api/collections", collectionRoutes); // 임시 주석
|
||||
|
||||
@@ -384,4 +384,69 @@ export class AuthController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/auth/signup
|
||||
* 공차중계 회원가입 API
|
||||
*/
|
||||
static async signup(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { userId, password, userName, phoneNumber, licenseNumber, vehicleNumber, vehicleType } = req.body;
|
||||
|
||||
logger.info(`=== 공차중계 회원가입 API 호출 ===`);
|
||||
logger.info(`userId: ${userId}, vehicleNumber: ${vehicleNumber}`);
|
||||
|
||||
// 입력값 검증
|
||||
if (!userId || !password || !userName || !phoneNumber || !licenseNumber || !vehicleNumber) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "필수 입력값이 누락되었습니다.",
|
||||
error: {
|
||||
code: "INVALID_INPUT",
|
||||
details: "아이디, 비밀번호, 이름, 연락처, 면허번호, 차량번호는 필수입니다.",
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 회원가입 처리
|
||||
const signupResult = await AuthService.signupDriver({
|
||||
userId,
|
||||
password,
|
||||
userName,
|
||||
phoneNumber,
|
||||
licenseNumber,
|
||||
vehicleNumber,
|
||||
vehicleType,
|
||||
});
|
||||
|
||||
if (signupResult.success) {
|
||||
logger.info(`공차중계 회원가입 성공: ${userId}`);
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: "회원가입이 완료되었습니다.",
|
||||
});
|
||||
} else {
|
||||
logger.warn(`공차중계 회원가입 실패: ${userId} - ${signupResult.message}`);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: signupResult.message || "회원가입에 실패했습니다.",
|
||||
error: {
|
||||
code: "SIGNUP_FAILED",
|
||||
details: signupResult.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("공차중계 회원가입 API 오류:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "회원가입 처리 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "SIGNUP_ERROR",
|
||||
details: error instanceof Error ? error.message : "알 수 없는 오류",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
458
backend-node/src/controllers/driverController.ts
Normal file
458
backend-node/src/controllers/driverController.ts
Normal file
@@ -0,0 +1,458 @@
|
||||
// 공차중계 운전자 컨트롤러
|
||||
import { Request, Response } from "express";
|
||||
import { query } from "../database/db";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
export class DriverController {
|
||||
/**
|
||||
* GET /api/driver/profile
|
||||
* 운전자 프로필 조회
|
||||
*/
|
||||
static async getProfile(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const userId = req.user?.userId;
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
message: "인증이 필요합니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 사용자 정보 조회
|
||||
const userResult = await query<any>(
|
||||
`SELECT
|
||||
user_id, user_name, cell_phone, license_number, vehicle_number, signup_type, branch_name
|
||||
FROM user_info
|
||||
WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (userResult.length === 0) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "사용자를 찾을 수 없습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const user = userResult[0];
|
||||
|
||||
// 공차중계 사용자가 아닌 경우
|
||||
if (user.signup_type !== "DRIVER") {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "공차중계 사용자만 접근할 수 있습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 차량 정보 조회
|
||||
const vehicleResult = await query<any>(
|
||||
`SELECT
|
||||
vehicle_number, vehicle_type, driver_name, driver_phone, status
|
||||
FROM vehicles
|
||||
WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
const vehicle = vehicleResult.length > 0 ? vehicleResult[0] : null;
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
userId: user.user_id,
|
||||
userName: user.user_name,
|
||||
phoneNumber: user.cell_phone,
|
||||
licenseNumber: user.license_number,
|
||||
vehicleNumber: user.vehicle_number,
|
||||
vehicleType: vehicle?.vehicle_type || null,
|
||||
vehicleStatus: vehicle?.status || null,
|
||||
branchName: user.branch_name || null,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("운전자 프로필 조회 오류:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "프로필 조회 중 오류가 발생했습니다.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/driver/profile
|
||||
* 운전자 프로필 수정 (이름, 연락처, 면허정보, 차량번호, 차종)
|
||||
*/
|
||||
static async updateProfile(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const userId = req.user?.userId;
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
message: "인증이 필요합니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { userName, phoneNumber, licenseNumber, vehicleNumber, vehicleType, branchName } = req.body;
|
||||
|
||||
// 공차중계 사용자 확인
|
||||
const userCheck = await query<any>(
|
||||
`SELECT signup_type, vehicle_number FROM user_info WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (userCheck.length === 0) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "사용자를 찾을 수 없습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (userCheck[0].signup_type !== "DRIVER") {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "공차중계 사용자만 접근할 수 있습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const oldVehicleNumber = userCheck[0].vehicle_number;
|
||||
|
||||
// 차량번호 변경 시 중복 확인
|
||||
if (vehicleNumber && vehicleNumber !== oldVehicleNumber) {
|
||||
const duplicateCheck = await query<any>(
|
||||
`SELECT vehicle_number FROM vehicles WHERE vehicle_number = $1 AND user_id != $2`,
|
||||
[vehicleNumber, userId]
|
||||
);
|
||||
|
||||
if (duplicateCheck.length > 0) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "이미 등록된 차량번호입니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// user_info 업데이트
|
||||
await query(
|
||||
`UPDATE user_info SET
|
||||
user_name = COALESCE($1, user_name),
|
||||
cell_phone = COALESCE($2, cell_phone),
|
||||
license_number = COALESCE($3, license_number),
|
||||
vehicle_number = COALESCE($4, vehicle_number),
|
||||
branch_name = COALESCE($5, branch_name)
|
||||
WHERE user_id = $6`,
|
||||
[userName || null, phoneNumber || null, licenseNumber || null, vehicleNumber || null, branchName || null, userId]
|
||||
);
|
||||
|
||||
// vehicles 테이블 업데이트
|
||||
await query(
|
||||
`UPDATE vehicles SET
|
||||
vehicle_number = COALESCE($1, vehicle_number),
|
||||
vehicle_type = COALESCE($2, vehicle_type),
|
||||
driver_name = COALESCE($3, driver_name),
|
||||
driver_phone = COALESCE($4, driver_phone),
|
||||
branch_name = COALESCE($5, branch_name),
|
||||
updated_at = NOW()
|
||||
WHERE user_id = $6`,
|
||||
[vehicleNumber || null, vehicleType || null, userName || null, phoneNumber || null, branchName || null, userId]
|
||||
);
|
||||
|
||||
logger.info(`운전자 프로필 수정 완료: ${userId}`);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "프로필이 수정되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("운전자 프로필 수정 오류:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "프로필 수정 중 오류가 발생했습니다.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/driver/status
|
||||
* 차량 상태 변경 (대기/정비만 가능)
|
||||
*/
|
||||
static async updateStatus(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const userId = req.user?.userId;
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
message: "인증이 필요합니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { status } = req.body;
|
||||
|
||||
// 허용된 상태값만 (대기: off, 정비: maintenance)
|
||||
const allowedStatuses = ["off", "maintenance"];
|
||||
if (!status || !allowedStatuses.includes(status)) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "유효하지 않은 상태값입니다. (off: 대기, maintenance: 정비)",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 공차중계 사용자 확인
|
||||
const userCheck = await query<any>(
|
||||
`SELECT signup_type FROM user_info WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (userCheck.length === 0 || userCheck[0].signup_type !== "DRIVER") {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "공차중계 사용자만 접근할 수 있습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// vehicles 테이블 상태 업데이트
|
||||
const updateResult = await query(
|
||||
`UPDATE vehicles SET status = $1, updated_at = NOW() WHERE user_id = $2`,
|
||||
[status, userId]
|
||||
);
|
||||
|
||||
logger.info(`차량 상태 변경: ${userId} -> ${status}`);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: `차량 상태가 ${status === "off" ? "대기" : "정비"}로 변경되었습니다.`,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("차량 상태 변경 오류:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "상태 변경 중 오류가 발생했습니다.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/driver/vehicle
|
||||
* 차량 삭제 (user_id = NULL 처리, 기록 보존)
|
||||
*/
|
||||
static async deleteVehicle(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const userId = req.user?.userId;
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
message: "인증이 필요합니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 공차중계 사용자 확인
|
||||
const userCheck = await query<any>(
|
||||
`SELECT signup_type, vehicle_number FROM user_info WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (userCheck.length === 0 || userCheck[0].signup_type !== "DRIVER") {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "공차중계 사용자만 접근할 수 있습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// vehicles 테이블에서 user_id를 NULL로 변경하고 status를 disabled로 (기록 보존)
|
||||
await query(
|
||||
`UPDATE vehicles SET user_id = NULL, status = 'disabled', updated_at = NOW() WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
// user_info에서 vehicle_number를 NULL로 변경
|
||||
await query(
|
||||
`UPDATE user_info SET vehicle_number = NULL WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
logger.info(`차량 삭제 완료 (기록 보존): ${userId}`);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "차량이 삭제되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("차량 삭제 오류:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "차량 삭제 중 오류가 발생했습니다.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/driver/vehicle
|
||||
* 새 차량 등록
|
||||
*/
|
||||
static async registerVehicle(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const userId = req.user?.userId;
|
||||
const companyCode = req.user?.companyCode;
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
message: "인증이 필요합니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { vehicleNumber, vehicleType, branchName } = req.body;
|
||||
|
||||
if (!vehicleNumber) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "차량번호는 필수입니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 공차중계 사용자 확인
|
||||
const userCheck = await query<any>(
|
||||
`SELECT signup_type, user_name, cell_phone, vehicle_number, company_code FROM user_info WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (userCheck.length === 0 || userCheck[0].signup_type !== "DRIVER") {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "공차중계 사용자만 접근할 수 있습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 이미 차량이 있는지 확인
|
||||
if (userCheck[0].vehicle_number) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "이미 등록된 차량이 있습니다. 먼저 기존 차량을 삭제해주세요.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 차량번호 중복 확인
|
||||
const duplicateCheck = await query<any>(
|
||||
`SELECT vehicle_number FROM vehicles WHERE vehicle_number = $1 AND user_id IS NOT NULL`,
|
||||
[vehicleNumber]
|
||||
);
|
||||
|
||||
if (duplicateCheck.length > 0) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "이미 등록된 차량번호입니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const userName = userCheck[0].user_name;
|
||||
const userPhone = userCheck[0].cell_phone;
|
||||
// 사용자의 company_code 사용 (req.user에서 가져오거나 DB에서 조회한 값 사용)
|
||||
const userCompanyCode = companyCode || userCheck[0].company_code;
|
||||
|
||||
// vehicles 테이블에 새 차량 등록 (company_code 포함, status는 'off')
|
||||
await query(
|
||||
`INSERT INTO vehicles (vehicle_number, vehicle_type, user_id, driver_name, driver_phone, branch_name, status, company_code, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, 'off', $7, NOW(), NOW())`,
|
||||
[vehicleNumber, vehicleType || null, userId, userName, userPhone, branchName || null, userCompanyCode]
|
||||
);
|
||||
|
||||
// user_info에 vehicle_number 업데이트
|
||||
await query(
|
||||
`UPDATE user_info SET vehicle_number = $1 WHERE user_id = $2`,
|
||||
[vehicleNumber, userId]
|
||||
);
|
||||
|
||||
logger.info(`새 차량 등록 완료: ${userId} -> ${vehicleNumber} (company_code: ${userCompanyCode})`);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "차량이 등록되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("차량 등록 오류:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "차량 등록 중 오류가 발생했습니다.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/driver/account
|
||||
* 회원 탈퇴 (차량 정보 포함 삭제)
|
||||
*/
|
||||
static async deleteAccount(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const userId = req.user?.userId;
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
message: "인증이 필요합니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 공차중계 사용자 확인
|
||||
const userCheck = await query<any>(
|
||||
`SELECT signup_type FROM user_info WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (userCheck.length === 0) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "사용자를 찾을 수 없습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (userCheck[0].signup_type !== "DRIVER") {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "공차중계 사용자만 탈퇴할 수 있습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// vehicles 테이블에서 삭제
|
||||
await query(`DELETE FROM vehicles WHERE user_id = $1`, [userId]);
|
||||
|
||||
// user_info 테이블에서 삭제
|
||||
await query(`DELETE FROM user_info WHERE user_id = $1`, [userId]);
|
||||
|
||||
logger.info(`회원 탈퇴 완료: ${userId}`);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "회원 탈퇴가 완료되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("회원 탈퇴 오류:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "회원 탈퇴 처리 중 오류가 발생했습니다.",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,10 @@ router.post("/logout", AuthController.logout);
|
||||
*/
|
||||
router.post("/refresh", AuthController.refreshToken);
|
||||
|
||||
/**
|
||||
* POST /api/auth/signup
|
||||
* 공차중계 회원가입 API
|
||||
*/
|
||||
router.post("/signup", AuthController.signup);
|
||||
|
||||
export default router;
|
||||
|
||||
48
backend-node/src/routes/driverRoutes.ts
Normal file
48
backend-node/src/routes/driverRoutes.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
// 공차중계 운전자 API 라우터
|
||||
import { Router } from "express";
|
||||
import { DriverController } from "../controllers/driverController";
|
||||
import { authenticateToken } from "../middleware/authMiddleware";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// 모든 라우트에 인증 필요
|
||||
router.use(authenticateToken);
|
||||
|
||||
/**
|
||||
* GET /api/driver/profile
|
||||
* 운전자 프로필 조회
|
||||
*/
|
||||
router.get("/profile", DriverController.getProfile);
|
||||
|
||||
/**
|
||||
* PUT /api/driver/profile
|
||||
* 운전자 프로필 수정 (이름, 연락처, 면허정보, 차량번호, 차종)
|
||||
*/
|
||||
router.put("/profile", DriverController.updateProfile);
|
||||
|
||||
/**
|
||||
* PUT /api/driver/status
|
||||
* 차량 상태 변경 (대기/정비만)
|
||||
*/
|
||||
router.put("/status", DriverController.updateStatus);
|
||||
|
||||
/**
|
||||
* DELETE /api/driver/vehicle
|
||||
* 차량 삭제 (기록 보존)
|
||||
*/
|
||||
router.delete("/vehicle", DriverController.deleteVehicle);
|
||||
|
||||
/**
|
||||
* POST /api/driver/vehicle
|
||||
* 새 차량 등록
|
||||
*/
|
||||
router.post("/vehicle", DriverController.registerVehicle);
|
||||
|
||||
/**
|
||||
* DELETE /api/driver/account
|
||||
* 회원 탈퇴
|
||||
*/
|
||||
router.delete("/account", DriverController.deleteAccount);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -97,6 +97,8 @@ router.post(
|
||||
const data: ExternalRestApiConnection = {
|
||||
...req.body,
|
||||
created_by: req.user?.userId || "system",
|
||||
// 로그인 사용자의 company_code 사용 (프론트에서 안 보내도 자동 설정)
|
||||
company_code: req.body.company_code || req.user?.companyCode || "*",
|
||||
};
|
||||
|
||||
const result =
|
||||
|
||||
@@ -342,4 +342,130 @@ export class AuthService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 공차중계 회원가입 처리
|
||||
* - user_info 테이블에 사용자 정보 저장
|
||||
* - vehicles 테이블에 차량 정보 저장
|
||||
*/
|
||||
static async signupDriver(data: {
|
||||
userId: string;
|
||||
password: string;
|
||||
userName: string;
|
||||
phoneNumber: string;
|
||||
licenseNumber: string;
|
||||
vehicleNumber: string;
|
||||
vehicleType?: string;
|
||||
}): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const {
|
||||
userId,
|
||||
password,
|
||||
userName,
|
||||
phoneNumber,
|
||||
licenseNumber,
|
||||
vehicleNumber,
|
||||
vehicleType,
|
||||
} = data;
|
||||
|
||||
// 1. 중복 사용자 확인
|
||||
const existingUser = await query<any>(
|
||||
`SELECT user_id FROM user_info WHERE user_id = $1`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (existingUser.length > 0) {
|
||||
return {
|
||||
success: false,
|
||||
message: "이미 존재하는 아이디입니다.",
|
||||
};
|
||||
}
|
||||
|
||||
// 2. 중복 차량번호 확인
|
||||
const existingVehicle = await query<any>(
|
||||
`SELECT vehicle_number FROM vehicles WHERE vehicle_number = $1`,
|
||||
[vehicleNumber]
|
||||
);
|
||||
|
||||
if (existingVehicle.length > 0) {
|
||||
return {
|
||||
success: false,
|
||||
message: "이미 등록된 차량번호입니다.",
|
||||
};
|
||||
}
|
||||
|
||||
// 3. 비밀번호 암호화 (MD5 - 기존 시스템 호환)
|
||||
const crypto = require("crypto");
|
||||
const hashedPassword = crypto
|
||||
.createHash("md5")
|
||||
.update(password)
|
||||
.digest("hex");
|
||||
|
||||
// 4. 사용자 정보 저장 (user_info)
|
||||
await query(
|
||||
`INSERT INTO user_info (
|
||||
user_id,
|
||||
user_password,
|
||||
user_name,
|
||||
cell_phone,
|
||||
license_number,
|
||||
vehicle_number,
|
||||
company_code,
|
||||
user_type,
|
||||
signup_type,
|
||||
status,
|
||||
regdate
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW())`,
|
||||
[
|
||||
userId,
|
||||
hashedPassword,
|
||||
userName,
|
||||
phoneNumber,
|
||||
licenseNumber,
|
||||
vehicleNumber,
|
||||
"COMPANY_13", // 기본 회사 코드
|
||||
null, // user_type: null
|
||||
"DRIVER", // signup_type: 공차중계 회원가입 사용자
|
||||
"active", // status: active
|
||||
]
|
||||
);
|
||||
|
||||
// 5. 차량 정보 저장 (vehicles)
|
||||
await query(
|
||||
`INSERT INTO vehicles (
|
||||
vehicle_number,
|
||||
vehicle_type,
|
||||
driver_name,
|
||||
driver_phone,
|
||||
status,
|
||||
company_code,
|
||||
user_id,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())`,
|
||||
[
|
||||
vehicleNumber,
|
||||
vehicleType || null,
|
||||
userName,
|
||||
phoneNumber,
|
||||
"off", // 초기 상태: off (대기)
|
||||
"COMPANY_13", // 기본 회사 코드
|
||||
userId, // 사용자 ID 연결
|
||||
]
|
||||
);
|
||||
|
||||
logger.info(`공차중계 회원가입 성공: ${userId}, 차량번호: ${vehicleNumber}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "회원가입이 완료되었습니다.",
|
||||
};
|
||||
} catch (error: any) {
|
||||
logger.error("공차중계 회원가입 오류:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: error.message || "회원가입 중 오류가 발생했습니다.",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,39 +28,39 @@ export class ExternalDbConnectionService {
|
||||
|
||||
// 회사별 필터링 (최고 관리자가 아닌 경우 필수)
|
||||
if (userCompanyCode && userCompanyCode !== "*") {
|
||||
whereConditions.push(`company_code = $${paramIndex++}`);
|
||||
whereConditions.push(`e.company_code = $${paramIndex++}`);
|
||||
params.push(userCompanyCode);
|
||||
logger.info(`회사별 외부 DB 연결 필터링: ${userCompanyCode}`);
|
||||
} else if (userCompanyCode === "*") {
|
||||
logger.info(`최고 관리자: 모든 외부 DB 연결 조회`);
|
||||
// 필터가 있으면 적용
|
||||
if (filter.company_code) {
|
||||
whereConditions.push(`company_code = $${paramIndex++}`);
|
||||
whereConditions.push(`e.company_code = $${paramIndex++}`);
|
||||
params.push(filter.company_code);
|
||||
}
|
||||
} else {
|
||||
// userCompanyCode가 없는 경우 (하위 호환성)
|
||||
if (filter.company_code) {
|
||||
whereConditions.push(`company_code = $${paramIndex++}`);
|
||||
whereConditions.push(`e.company_code = $${paramIndex++}`);
|
||||
params.push(filter.company_code);
|
||||
}
|
||||
}
|
||||
|
||||
// 필터 조건 적용
|
||||
if (filter.db_type) {
|
||||
whereConditions.push(`db_type = $${paramIndex++}`);
|
||||
whereConditions.push(`e.db_type = $${paramIndex++}`);
|
||||
params.push(filter.db_type);
|
||||
}
|
||||
|
||||
if (filter.is_active) {
|
||||
whereConditions.push(`is_active = $${paramIndex++}`);
|
||||
whereConditions.push(`e.is_active = $${paramIndex++}`);
|
||||
params.push(filter.is_active);
|
||||
}
|
||||
|
||||
// 검색 조건 적용 (연결명 또는 설명에서 검색)
|
||||
if (filter.search && filter.search.trim()) {
|
||||
whereConditions.push(
|
||||
`(connection_name ILIKE $${paramIndex} OR description ILIKE $${paramIndex})`
|
||||
`(e.connection_name ILIKE $${paramIndex} OR e.description ILIKE $${paramIndex})`
|
||||
);
|
||||
params.push(`%${filter.search.trim()}%`);
|
||||
paramIndex++;
|
||||
@@ -72,9 +72,12 @@ export class ExternalDbConnectionService {
|
||||
: "";
|
||||
|
||||
const connections = await query<any>(
|
||||
`SELECT * FROM external_db_connections
|
||||
`SELECT e.*,
|
||||
COALESCE(c.company_name, CASE WHEN e.company_code = '*' THEN '전체' ELSE e.company_code END) AS company_name
|
||||
FROM external_db_connections e
|
||||
LEFT JOIN company_mng c ON e.company_code = c.company_code
|
||||
${whereClause}
|
||||
ORDER BY is_active DESC, connection_name ASC`,
|
||||
ORDER BY e.is_active DESC, e.connection_name ASC`,
|
||||
params
|
||||
);
|
||||
|
||||
|
||||
@@ -31,15 +31,17 @@ export class ExternalRestApiConnectionService {
|
||||
try {
|
||||
let query = `
|
||||
SELECT
|
||||
id, connection_name, description, base_url, endpoint_path, default_headers,
|
||||
default_method,
|
||||
e.id, e.connection_name, e.description, e.base_url, e.endpoint_path, e.default_headers,
|
||||
e.default_method,
|
||||
-- DB 스키마의 컬럼명은 default_request_body 기준이고
|
||||
-- 코드에서는 default_body 필드로 사용하기 위해 alias 처리
|
||||
default_request_body AS default_body,
|
||||
auth_type, auth_config, timeout, retry_count, retry_delay,
|
||||
company_code, is_active, created_date, created_by,
|
||||
updated_date, updated_by, last_test_date, last_test_result, last_test_message
|
||||
FROM external_rest_api_connections
|
||||
e.default_request_body AS default_body,
|
||||
e.auth_type, e.auth_config, e.timeout, e.retry_count, e.retry_delay,
|
||||
e.company_code, e.is_active, e.created_date, e.created_by,
|
||||
e.updated_date, e.updated_by, e.last_test_date, e.last_test_result, e.last_test_message,
|
||||
COALESCE(c.company_name, CASE WHEN e.company_code = '*' THEN '전체' ELSE e.company_code END) AS company_name
|
||||
FROM external_rest_api_connections e
|
||||
LEFT JOIN company_mng c ON e.company_code = c.company_code
|
||||
WHERE 1=1
|
||||
`;
|
||||
|
||||
@@ -48,7 +50,7 @@ export class ExternalRestApiConnectionService {
|
||||
|
||||
// 회사별 필터링 (최고 관리자가 아닌 경우 필수)
|
||||
if (userCompanyCode && userCompanyCode !== "*") {
|
||||
query += ` AND company_code = $${paramIndex}`;
|
||||
query += ` AND e.company_code = $${paramIndex}`;
|
||||
params.push(userCompanyCode);
|
||||
paramIndex++;
|
||||
logger.info(`회사별 REST API 연결 필터링: ${userCompanyCode}`);
|
||||
@@ -56,14 +58,14 @@ export class ExternalRestApiConnectionService {
|
||||
logger.info(`최고 관리자: 모든 REST API 연결 조회`);
|
||||
// 필터가 있으면 적용
|
||||
if (filter.company_code) {
|
||||
query += ` AND company_code = $${paramIndex}`;
|
||||
query += ` AND e.company_code = $${paramIndex}`;
|
||||
params.push(filter.company_code);
|
||||
paramIndex++;
|
||||
}
|
||||
} else {
|
||||
// userCompanyCode가 없는 경우 (하위 호환성)
|
||||
if (filter.company_code) {
|
||||
query += ` AND company_code = $${paramIndex}`;
|
||||
query += ` AND e.company_code = $${paramIndex}`;
|
||||
params.push(filter.company_code);
|
||||
paramIndex++;
|
||||
}
|
||||
@@ -71,14 +73,14 @@ export class ExternalRestApiConnectionService {
|
||||
|
||||
// 활성 상태 필터
|
||||
if (filter.is_active) {
|
||||
query += ` AND is_active = $${paramIndex}`;
|
||||
query += ` AND e.is_active = $${paramIndex}`;
|
||||
params.push(filter.is_active);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 인증 타입 필터
|
||||
if (filter.auth_type) {
|
||||
query += ` AND auth_type = $${paramIndex}`;
|
||||
query += ` AND e.auth_type = $${paramIndex}`;
|
||||
params.push(filter.auth_type);
|
||||
paramIndex++;
|
||||
}
|
||||
@@ -86,9 +88,9 @@ export class ExternalRestApiConnectionService {
|
||||
// 검색어 필터 (연결명, 설명, URL)
|
||||
if (filter.search) {
|
||||
query += ` AND (
|
||||
connection_name ILIKE $${paramIndex} OR
|
||||
description ILIKE $${paramIndex} OR
|
||||
base_url ILIKE $${paramIndex}
|
||||
e.connection_name ILIKE $${paramIndex} OR
|
||||
e.description ILIKE $${paramIndex} OR
|
||||
e.base_url ILIKE $${paramIndex}
|
||||
)`;
|
||||
params.push(`%${filter.search}%`);
|
||||
paramIndex++;
|
||||
@@ -233,6 +235,7 @@ export class ExternalRestApiConnectionService {
|
||||
// 디버깅: 저장하려는 데이터 로깅
|
||||
logger.info(`REST API 연결 생성 요청 데이터:`, {
|
||||
connection_name: data.connection_name,
|
||||
company_code: data.company_code,
|
||||
default_method: data.default_method,
|
||||
endpoint_path: data.endpoint_path,
|
||||
base_url: data.base_url,
|
||||
|
||||
Reference in New Issue
Block a user