requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } try { $page = (int) ($this->request->getGet('page') ?? 1); $perPage = (int) ($this->request->getGet('per_page') ?? 10); if ($page < 1) $page = 1; if ($perPage < 1) $perPage = 10; $offset = ($page - 1) * $perPage; $search = trim((string) $this->request->getGet('search')); $status = trim((string) $this->request->getGet('status')); // 'Y' or 'N' 필터 (선택) $db = $this->getDB(); $builder = $db->table($this->table); // soft delete 제외 $builder->where('deleted_YN', 'N'); if ($search !== '') { $builder->like('name', $search); } if ($status === 'Y' || $status === 'N') { $builder->where('status_YN', $status); } $total = $builder->countAllResults(false); $items = $builder ->select('id, name, weight, status_YN, created_at, updated_at') ->orderBy('id', 'DESC') ->limit($perPage, $offset) ->get() ->getResult(); return $this->respondSuccess([ 'items' => $items, 'total' => $total, 'page' => $page, 'per_page' => $perPage, 'total_pages' => (int) ceil($total / $perPage), ]); } catch (\Exception $e) { log_message('error', 'FishingFieldController index error: ' . $e->getMessage()); return $this->respondError('목록 조회 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * 낚시분야 등록 * POST /api/field */ public function create() { $auth = $this->requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } try { // JSON 요청 우선, fallback 으로 form-encoded 도 허용 $payload = $this->request->getJSON(true); if (!is_array($payload) || empty($payload)) { $payload = $this->request->getPost() ?? []; } $name = trim((string) ($payload['name'] ?? '')); $weightRaw = trim((string) ($payload['weight'] ?? '')); // 분야명 검증: 1~30자, 필수 if ($name === '') { return $this->respondError('분야명을 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } if (mb_strlen($name) > 30) { return $this->respondError('분야명은 30자 이내로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } // 가중치 검증: 0.0 ~ 1.0, 소수점 1자리 if ($weightRaw === '' || !preg_match('/^(0(\.\d)?|1(\.0)?)$/', $weightRaw)) { return $this->respondError('가중치는 0.0 ~ 1.0 사이 소수점 1자리로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } // 소수점 1자리로 정규화 (예: "1" → "1.0", ".5" → "0.5") $weight = number_format((float) $weightRaw, 1, '.', ''); $db = $this->getDB(); $builder = $db->table($this->table); // 중복 검사 $exists = $builder->where('name', $name)->countAllResults(); if ($exists > 0) { return $this->respondError('이미 등록된 분야명입니다.', ResponseInterface::HTTP_CONFLICT); } $now = date('Y-m-d H:i:s'); $insertData = [ 'name' => $name, 'weight' => $weight, 'created_at' => $now, ]; if (!$db->table($this->table)->insert($insertData)) { return $this->respondError('등록에 실패했습니다.', ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } $newId = $db->insertID(); $row = $db->table($this->table)->where('id', $newId)->get()->getRow(); return $this->respondSuccess($row, '낚시분야가 등록되었습니다.', ResponseInterface::HTTP_CREATED); } catch (\Exception $e) { log_message('error', 'FishingFieldController create error: ' . $e->getMessage()); return $this->respondError('등록 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * 낚시분야 상세 조회 * GET /api/field/:id */ public function show($id = null) { $auth = $this->requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } if (empty($id)) { return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST); } try { $row = $this->getDB()->table($this->table) ->select('id, name, weight, status_YN, created_at, updated_at') ->where('id', (int) $id) ->where('deleted_YN', 'N') ->get() ->getRow(); if (!$row) { return $this->respondError('해당 낚시분야를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND); } return $this->respondSuccess($row); } catch (\Exception $e) { log_message('error', 'FishingFieldController show error: ' . $e->getMessage()); return $this->respondError('조회 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * 낚시분야 수정 * PUT /api/field/:id */ public function update($id = null) { $auth = $this->requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } if (empty($id)) { return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST); } try { $payload = $this->request->getJSON(true); if (!is_array($payload) || empty($payload)) { $payload = $this->request->getRawInput() ?? []; } $name = trim((string) ($payload['name'] ?? '')); $weightRaw = trim((string) ($payload['weight'] ?? '')); $status = trim((string) ($payload['status_YN'] ?? 'Y')); // 분야명 검증 if ($name === '') { return $this->respondError('분야명을 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } if (mb_strlen($name) > 30) { return $this->respondError('분야명은 30자 이내로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } // 가중치 검증 if ($weightRaw === '' || !preg_match('/^(0(\.\d)?|1(\.0)?)$/', $weightRaw)) { return $this->respondError('가중치는 0.0 ~ 1.0 사이 소수점 1자리로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } $weight = number_format((float) $weightRaw, 1, '.', ''); // 상태 검증 if ($status !== 'Y' && $status !== 'N') { return $this->respondError('상태값이 올바르지 않습니다.', ResponseInterface::HTTP_BAD_REQUEST); } $db = $this->getDB(); $builder = $db->table($this->table); // 대상 행 존재 확인 $exists = $builder->where('id', (int) $id)->where('deleted_YN', 'N')->countAllResults(false); if ($exists === 0) { return $this->respondError('해당 낚시분야를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND); } $builder->resetQuery(); // 중복 검사 (자기 자신 제외) $dupe = $db->table($this->table) ->where('name', $name) ->where('id !=', (int) $id) ->where('deleted_YN', 'N') ->countAllResults(); if ($dupe > 0) { return $this->respondError('이미 등록된 분야명입니다.', ResponseInterface::HTTP_CONFLICT); } $updateData = [ 'name' => $name, 'weight' => $weight, 'status_YN' => $status, 'updated_at' => date('Y-m-d H:i:s'), ]; $db->table($this->table)->where('id', (int) $id)->update($updateData); $row = $db->table($this->table)->where('id', (int) $id)->get()->getRow(); return $this->respondSuccess($row, '낚시분야가 수정되었습니다.'); } catch (\Exception $e) { log_message('error', 'FishingFieldController update error: ' . $e->getMessage()); return $this->respondError('수정 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * 낚시분야 삭제 (soft delete) * DELETE /api/field/:id */ public function delete($id = null) { $auth = $this->requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } if (empty($id)) { return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST); } try { $db = $this->getDB(); $exists = $db->table($this->table) ->where('id', (int) $id) ->where('deleted_YN', 'N') ->countAllResults(); if ($exists === 0) { return $this->respondError('해당 낚시분야를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND); } $db->table($this->table) ->where('id', (int) $id) ->update([ 'deleted_YN' => 'Y', 'updated_at' => date('Y-m-d H:i:s'), ]); return $this->respondSuccess(null, '낚시분야가 삭제되었습니다.'); } catch (\Exception $e) { log_message('error', 'FishingFieldController delete error: ' . $e->getMessage()); return $this->respondError('삭제 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } }