Implement Customs API backend and frontend integration

This commit is contained in:
backuppc
2025-12-02 15:35:28 +09:00
parent 2d160ceabb
commit 9b2ee87542
4 changed files with 256 additions and 34 deletions

View File

@@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using Newtonsoft.Json;
using FCOMMON;
namespace Project.Web
{
public partial class MachineBridge
{
#region Customs API
/// <summary>
/// 업체정보 목록 조회
/// </summary>
public string Customs_GetList(string searchKey = "")
{
try
{
// 로그인 체크
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
{
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
}
var connStr = Properties.Settings.Default.CS;
using (var conn = new SqlConnection(connStr))
{
conn.Open();
var cmd = new SqlCommand();
cmd.Connection = conn;
// 검색 조건이 있으면 필터링
if (string.IsNullOrEmpty(searchKey))
{
cmd.CommandText = @"
SELECT idx, grp, name, owner, ownertel, address, tel, fax, email, memo,
wuid, wdate, uptae, staff, stafftel, name2, gcode
FROM Customs WITH (nolock)
WHERE gcode = @gcode
ORDER BY name";
}
else
{
// 여러 필드에서 검색 (fCustoms.cs의 btFind_Click 로직 참고)
cmd.CommandText = @"
SELECT idx, grp, name, owner, ownertel, address, tel, fax, email, memo,
wuid, wdate, uptae, staff, stafftel, name2, gcode
FROM Customs WITH (nolock)
WHERE gcode = @gcode
AND (stafftel LIKE @search
OR grp LIKE @search
OR name2 LIKE @search
OR name LIKE @search
OR owner LIKE @search
OR address LIKE @search
OR tel LIKE @search
OR email LIKE @search
OR memo LIKE @search
OR staff LIKE @search)
ORDER BY name";
cmd.Parameters.Add("@search", SqlDbType.NVarChar).Value = "%" + searchKey.Replace("'", "''") + "%";
}
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
var list = new List<object>();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
list.Add(new
{
idx = reader["idx"],
grp = reader["grp"] != DBNull.Value ? reader["grp"] : "",
name = reader["name"] != DBNull.Value ? reader["name"] : "",
owner = reader["owner"] != DBNull.Value ? reader["owner"] : "",
ownertel = reader["ownertel"] != DBNull.Value ? reader["ownertel"] : "",
address = reader["address"] != DBNull.Value ? reader["address"] : "",
tel = reader["tel"] != DBNull.Value ? reader["tel"] : "",
fax = reader["fax"] != DBNull.Value ? reader["fax"] : "",
email = reader["email"] != DBNull.Value ? reader["email"] : "",
memo = reader["memo"] != DBNull.Value ? reader["memo"] : "",
wuid = reader["wuid"],
wdate = reader["wdate"],
uptae = reader["uptae"] != DBNull.Value ? reader["uptae"] : "",
staff = reader["staff"] != DBNull.Value ? reader["staff"] : "",
stafftel = reader["stafftel"] != DBNull.Value ? reader["stafftel"] : "",
name2 = reader["name2"] != DBNull.Value ? reader["name2"] : "",
gcode = reader["gcode"]
});
}
}
return JsonConvert.SerializeObject(new { Success = true, Message = "", Data = list });
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message, Data = (object)null });
}
}
/// <summary>
/// 업체정보 상세 조회
/// </summary>
public string Customs_GetDetail(int idx)
{
try
{
// 로그인 체크
if (string.IsNullOrEmpty(info.Login.no) || string.IsNullOrEmpty(info.Login.gcode))
{
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
}
var connStr = Properties.Settings.Default.CS;
using (var conn = new SqlConnection(connStr))
{
conn.Open();
var cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = @"
SELECT idx, grp, name, owner, ownertel, address, tel, fax, email, memo,
wuid, wdate, uptae, staff, stafftel, name2, gcode
FROM Customs WITH (nolock)
WHERE idx = @idx AND gcode = @gcode";
cmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx;
cmd.Parameters.Add("@gcode", SqlDbType.VarChar).Value = info.Login.gcode;
object result = null;
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
result = new
{
idx = reader["idx"],
grp = reader["grp"] != DBNull.Value ? reader["grp"] : "",
name = reader["name"] != DBNull.Value ? reader["name"] : "",
owner = reader["owner"] != DBNull.Value ? reader["owner"] : "",
ownertel = reader["ownertel"] != DBNull.Value ? reader["ownertel"] : "",
address = reader["address"] != DBNull.Value ? reader["address"] : "",
tel = reader["tel"] != DBNull.Value ? reader["tel"] : "",
fax = reader["fax"] != DBNull.Value ? reader["fax"] : "",
email = reader["email"] != DBNull.Value ? reader["email"] : "",
memo = reader["memo"] != DBNull.Value ? reader["memo"] : "",
wuid = reader["wuid"],
wdate = reader["wdate"],
uptae = reader["uptae"] != DBNull.Value ? reader["uptae"] : "",
staff = reader["staff"] != DBNull.Value ? reader["staff"] : "",
stafftel = reader["stafftel"] != DBNull.Value ? reader["stafftel"] : "",
name2 = reader["name2"] != DBNull.Value ? reader["name2"] : "",
gcode = reader["gcode"]
};
}
}
if (result == null)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "해당 업체 정보를 찾을 수 없습니다.", Data = (object)null });
}
return JsonConvert.SerializeObject(new { Success = true, Message = "", Data = result });
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message, Data = (object)null });
}
}
#endregion
}
}

View File

@@ -41,6 +41,7 @@ import type {
MachineBridgeInterface,
BoardItem,
MailItem,
CustomItem,
} from '@/types';
// WebView2 환경 감지
@@ -1278,6 +1279,34 @@ class CommunicationLayer {
return this.wsRequest<ApiResponse<MailItem[]>>('MAIL_GET_LIST', 'MAIL_LIST_DATA', { startDate, endDate, searchKey });
}
}
/**
* 업체정보 목록 조회
* @param searchKey 검색어
* @returns ApiResponse<CustomItem[]>
*/
public async getCustomsList(searchKey: string = ''): Promise<ApiResponse<CustomItem[]>> {
if (isWebView && machine) {
const result = await machine.Customs_GetList(searchKey);
return JSON.parse(result);
} else {
return this.wsRequest<ApiResponse<CustomItem[]>>('CUSTOMS_GET_LIST', 'CUSTOMS_LIST_DATA', { searchKey });
}
}
/**
* 업체정보 상세 조회
* @param idx 업체 인덱스
* @returns ApiResponse<CustomItem>
*/
public async getCustomsDetail(idx: number): Promise<ApiResponse<CustomItem>> {
if (isWebView && machine) {
const result = await machine.Customs_GetDetail(idx);
return JSON.parse(result);
} else {
return this.wsRequest<ApiResponse<CustomItem>>('CUSTOMS_GET_DETAIL', 'CUSTOMS_DETAIL_DATA', { idx });
}
}
}
export const comms = new CommunicationLayer();

View File

@@ -1,21 +1,7 @@
import { useState, useEffect } from 'react';
import { Building, Search, RefreshCw } from 'lucide-react';
// 임시 타입 정의 (실제 타입은 백엔드에 맞게 수정 필요)
interface CustomItem {
idx: number;
ccode: string;
cname: string;
gubun: string;
addr: string;
tel: string;
fax: string;
email: string;
ceo: string;
busino: string;
uptae: string;
jongmok: string;
}
import { comms } from '@/communication';
import type { CustomItem } from '@/types';
export function Customs() {
const [customsList, setCustomsList] = useState<CustomItem[]>([]);
@@ -29,19 +15,18 @@ export function Customs() {
const loadData = async () => {
setLoading(true);
try {
// TODO: 실제 API 호출로 변경 필요
// const response = await comms.getCustomsList(searchKey);
// if (response.Success && response.Data) {
// setCustomsList(response.Data);
// }
// 임시 데이터
console.log('업체정보 조회 (구현 필요):', { searchKey });
alert('업체정보 API가 아직 구현되지 않았습니다.');
setCustomsList([]);
const response = await comms.getCustomsList(searchKey);
if (response.Success && response.Data) {
setCustomsList(response.Data);
} else {
console.error('업체정보 조회 실패:', response.Message);
alert(response.Message || '업체정보 조회에 실패했습니다.');
setCustomsList([]);
}
} catch (error) {
console.error('업체정보 로드 오류:', error);
alert('데이터를 불러오는 중 오류가 발생했습니다.');
setCustomsList([]);
} finally {
setLoading(false);
}
@@ -63,7 +48,7 @@ export function Customs() {
value={searchKey}
onChange={(e) => setSearchKey(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
placeholder="업체명, 사업자번호, 대표자 등"
placeholder="업체명, 대표자, 전화번호, 이메일, 담당자 등"
className="flex-1 h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400"
/>
</div>
@@ -115,16 +100,21 @@ export function Customs() {
>
<div className="flex items-center justify-between gap-4">
<div className="flex-1 min-w-0">
<h4 className="text-white font-medium mb-1">{item.cname}</h4>
<div className="flex items-center gap-4 text-white/60 text-sm">
<div>: {item.ceo}</div>
<div>: {item.busino}</div>
<div>: {item.uptae}</div>
<h4 className="text-white font-medium mb-1">
{item.name}
{item.name2 && <span className="text-white/60 text-sm ml-2">({item.name2})</span>}
</h4>
<div className="flex items-center gap-4 text-white/60 text-sm flex-wrap">
{item.grp && <div>: {item.grp}</div>}
{item.owner && <div>: {item.owner}</div>}
{item.uptae && <div>: {item.uptae}</div>}
{item.address && <div>{item.address}</div>}
</div>
</div>
<div className="flex flex-col items-end gap-1 flex-shrink-0 text-white/60 text-sm">
<div>{item.tel}</div>
<div>{item.email}</div>
{item.tel && <div>{item.tel}</div>}
{item.email && <div>{item.email}</div>}
{item.staff && <div>: {item.staff}</div>}
</div>
</div>
</div>

View File

@@ -462,6 +462,10 @@ export interface MachineBridgeInterface {
// Mail API (메일 발신 내역)
Mail_GetList(startDate: string, endDate: string, searchKey: string): Promise<string>;
// Customs API (업체정보)
Customs_GetList(searchKey: string): Promise<string>;
Customs_GetDetail(idx: number): Promise<string>;
}
// 사용자 권한 정보 타입
@@ -859,3 +863,24 @@ export interface MailItem {
cate: string;
wdate: string | null;
}
// 업체정보 타입
export interface CustomItem {
idx: number;
grp: string;
name: string;
owner: string;
ownertel: string;
address: string;
tel: string;
fax: string;
email: string;
memo: string;
wuid: string;
wdate: string;
uptae: string;
staff: string;
stafftel: string;
name2: string;
gcode: string;
}