feat: React 프론트엔드 기능 대폭 확장

- 월별근무표: 휴일/근무일 관리, 자동 초기화
- 메일양식: 템플릿 CRUD, To/CC/BCC 설정
- 그룹정보: 부서 관리, 비트 연산 기반 권한 설정
- 업무일지: 수정 성공 메시지 제거, 오늘 근무시간 필터링 수정
- 웹소켓 메시지 type 충돌 버그 수정

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
backuppc
2025-11-27 17:25:31 +09:00
parent b57af6dad7
commit c9b5d756e1
65 changed files with 14028 additions and 467 deletions

View File

@@ -229,5 +229,198 @@ namespace Project.Web
}
#endregion
#region Items API
/// <summary>
/// 품목 카테고리 목록 조회
/// </summary>
public string Items_GetCategories()
{
try
{
var sql = "SELECT DISTINCT cate FROM Items WITH (NOLOCK) WHERE gcode = @gcode AND ISNULL(cate,'') <> '' ORDER BY cate";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
var da = new SqlDataAdapter(cmd);
var dt = new DataTable();
da.Fill(dt);
da.Dispose();
cmd.Dispose();
cn.Dispose();
var result = new System.Collections.Generic.List<string>();
foreach (DataRow dr in dt.Rows)
{
result.Add(dr["cate"]?.ToString() ?? "");
}
return JsonConvert.SerializeObject(new { Success = true, Data = result });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "카테고리 조회 실패: " + ex.Message });
}
}
/// <summary>
/// 품목 목록 조회
/// </summary>
public string Items_GetList(string category, string searchKey)
{
try
{
var cateSearch = string.IsNullOrEmpty(category) || category == "all" ? "%" : category;
var skey = string.IsNullOrEmpty(searchKey) || searchKey == "%" ? "%" : $"%{searchKey}%";
var sql = @"SELECT idx, sid, cate, name, model, scale, unit, price, supply, manu, storage, disable, memo
FROM Items WITH (NOLOCK)
WHERE gcode = @gcode
AND ISNULL(cate,'') LIKE @cate
AND (ISNULL(sid,'') LIKE @search OR ISNULL(name,'') LIKE @search OR ISNULL(model,'') LIKE @search)
ORDER BY sid, name";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@cate", cateSearch);
cmd.Parameters.AddWithValue("@search", skey);
var da = new SqlDataAdapter(cmd);
var dt = new DataTable();
da.Fill(dt);
da.Dispose();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "품목 조회 실패: " + ex.Message });
}
}
/// <summary>
/// 품목 저장
/// </summary>
public string Items_Save(int idx, string sid, string cate, string name, string model,
string scale, string unit, decimal price, string supply, string manu, string storage, bool disable, string memo)
{
try
{
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var sql = string.Empty;
var cmd = new SqlCommand();
cmd.Connection = cn;
// 신규 추가 시 SID 중복 체크
if (idx == 0 && !string.IsNullOrEmpty(sid))
{
var checkSql = "SELECT COUNT(*) FROM Items WITH (NOLOCK) WHERE gcode = @gcode AND sid = @sid";
var checkCmd = new SqlCommand(checkSql, cn);
checkCmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
checkCmd.Parameters.AddWithValue("@sid", sid);
cn.Open();
var count = (int)checkCmd.ExecuteScalar();
cn.Close();
checkCmd.Dispose();
if (count > 0)
{
cn.Dispose();
return JsonConvert.SerializeObject(new { Success = false, Message = $"이미 존재하는 SID입니다: {sid}" });
}
}
if (idx > 0)
{
sql = @"UPDATE Items SET
sid = @sid, cate = @cate, name = @name, model = @model,
scale = @scale, unit = @unit, price = @price, supply = @supply,
manu = @manu, storage = @storage, disable = @disable, memo = @memo,
wuid = @wuid, wdate = GETDATE()
WHERE idx = @idx AND gcode = @gcode";
}
else
{
sql = @"INSERT INTO Items (gcode, sid, cate, name, model, scale, unit, price, supply, manu, storage, disable, memo, wuid, wdate)
VALUES (@gcode, @sid, @cate, @name, @model, @scale, @unit, @price, @supply, @manu, @storage, @disable, @memo, @wuid, GETDATE())";
}
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@sid", sid ?? "");
cmd.Parameters.AddWithValue("@cate", cate ?? "");
cmd.Parameters.AddWithValue("@name", name ?? "");
cmd.Parameters.AddWithValue("@model", model ?? "");
cmd.Parameters.AddWithValue("@scale", scale ?? "");
cmd.Parameters.AddWithValue("@unit", unit ?? "");
cmd.Parameters.AddWithValue("@price", price);
cmd.Parameters.AddWithValue("@supply", supply ?? "");
cmd.Parameters.AddWithValue("@manu", manu ?? "");
cmd.Parameters.AddWithValue("@storage", storage ?? "");
cmd.Parameters.AddWithValue("@disable", disable);
cmd.Parameters.AddWithValue("@memo", memo ?? "");
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
if (idx > 0)
{
cmd.Parameters.AddWithValue("@idx", idx);
}
cn.Open();
var result = cmd.ExecuteNonQuery();
cn.Close();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "저장되었습니다." : "저장에 실패했습니다." });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "저장 실패: " + ex.Message });
}
}
/// <summary>
/// 품목 삭제
/// </summary>
public string Items_Delete(int idx)
{
try
{
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var sql = "DELETE FROM Items WHERE idx = @idx AND gcode = @gcode";
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@idx", idx);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cn.Open();
var result = cmd.ExecuteNonQuery();
cn.Close();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "삭제되었습니다." : "삭제에 실패했습니다." });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "삭제 실패: " + ex.Message });
}
}
#endregion
}
}

View File

@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using Newtonsoft.Json;
using FCOMMON;
namespace Project.Web
{
public partial class MachineBridge
{
#region Holiday API ()
/// <summary>
/// 월별근무표 목록 조회
/// </summary>
public string Holiday_GetList(string month)
{
try
{
var monthPattern = month + "%";
var sql = @"SELECT idx, pdate, free, memo, wuid, wdate
FROM HolidayList WITH (nolock)
WHERE pdate LIKE @month
ORDER BY pdate";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@month", monthPattern);
using (var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
return JsonConvert.SerializeObject(new { Success = true, Data = dt });
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 월별근무표 저장 (없으면 생성)
/// </summary>
public string Holiday_Save(string month, string holidaysJson)
{
try
{
var holidays = JsonConvert.DeserializeObject<List<HolidayItem>>(holidaysJson);
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
{
cn.Open();
using (var tran = cn.BeginTransaction())
{
try
{
foreach (var item in holidays)
{
var sql = @"
IF EXISTS (SELECT 1 FROM HolidayList WHERE pdate = @pdate)
UPDATE HolidayList SET free = @free, memo = @memo, wuid = @wuid, wdate = GETDATE() WHERE pdate = @pdate
ELSE
INSERT INTO HolidayList (pdate, free, memo, wuid, wdate) VALUES (@pdate, @free, @memo, @wuid, GETDATE())";
using (var cmd = new SqlCommand(sql, cn, tran))
{
cmd.Parameters.AddWithValue("@pdate", item.pdate);
cmd.Parameters.AddWithValue("@free", item.free);
cmd.Parameters.AddWithValue("@memo", item.memo ?? "");
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
cmd.ExecuteNonQuery();
}
}
tran.Commit();
return JsonConvert.SerializeObject(new { Success = true, Message = "저장되었습니다." });
}
catch
{
tran.Rollback();
throw;
}
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 월별근무표 초기 데이터 생성 (해당 월에 데이터가 없을 때)
/// </summary>
public string Holiday_Initialize(string month)
{
try
{
// 해당 월 데이터 존재 여부 확인
var monthPattern = month + "%";
var checkSql = "SELECT COUNT(*) FROM HolidayList WITH (nolock) WHERE pdate LIKE @month";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
{
cn.Open();
using (var checkCmd = new SqlCommand(checkSql, cn))
{
checkCmd.Parameters.AddWithValue("@month", monthPattern);
var count = (int)checkCmd.ExecuteScalar();
if (count > 0)
{
return JsonConvert.SerializeObject(new { Success = true, Message = "이미 데이터가 존재합니다.", Created = false });
}
}
// 해당 월의 모든 날짜 생성
var startDate = DateTime.Parse(month + "-01");
var endDate = startDate.AddMonths(1).AddDays(-1);
using (var tran = cn.BeginTransaction())
{
try
{
for (var date = startDate; date <= endDate; date = date.AddDays(1))
{
var isFree = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
var memo = date.DayOfWeek == DayOfWeek.Saturday ? "토요일" :
date.DayOfWeek == DayOfWeek.Sunday ? "일요일" : "";
var sql = @"INSERT INTO HolidayList (pdate, free, memo, wuid, wdate)
VALUES (@pdate, @free, @memo, @wuid, GETDATE())";
using (var cmd = new SqlCommand(sql, cn, tran))
{
cmd.Parameters.AddWithValue("@pdate", date.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("@free", isFree);
cmd.Parameters.AddWithValue("@memo", memo);
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
cmd.ExecuteNonQuery();
}
}
tran.Commit();
return JsonConvert.SerializeObject(new { Success = true, Message = "초기 데이터가 생성되었습니다.", Created = true });
}
catch
{
tran.Rollback();
throw;
}
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
#endregion
}
public class HolidayItem
{
public int idx { get; set; }
public string pdate { get; set; }
public bool free { get; set; }
public string memo { get; set; }
}
}

View File

@@ -12,62 +12,47 @@ namespace Project.Web
#region Jobreport API
/// <summary>
/// 업무일지 목록 조회
/// 업무일지 목록 조회 (vJobReportForUser 뷰 사용)
/// </summary>
public string Jobreport_GetList(string sd, string ed, string uid, string cate, string doit)
public string Jobreport_GetList(string sd, string ed, string uid, string cate, string searchKey)
{
try
{
var sql = @"SELECT j.idx, j.jdate, j.uid, j.cate, j.title, j.doit, j.remark, j.jfrom, j.jto,
u.name as userName, j.wdate
FROM EETGW_Jobreport j WITH (nolock)
LEFT JOIN Users u ON j.uid = u.id
WHERE j.gcode = @gcode";
var sql = @"SELECT idx, pidx, pdate, id, name,type, svalue, hrs,ot,requestpart,package,userprocess,status, projectName, description, ww,otpms,process
FROM vJobReportForUser WITH (nolock)
WHERE gcode = @gcode AND (pdate BETWEEN @sd AND @ed)";
var parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("@gcode", info.Login.gcode));
parameters.Add(new SqlParameter("@sd", sd));
parameters.Add(new SqlParameter("@ed", ed));
if (!string.IsNullOrEmpty(sd))
{
sql += " AND j.jdate >= @sd";
parameters.Add(new SqlParameter("@sd", sd));
}
if (!string.IsNullOrEmpty(ed))
{
sql += " AND j.jdate <= @ed";
parameters.Add(new SqlParameter("@ed", ed));
}
if (!string.IsNullOrEmpty(uid))
{
sql += " AND j.uid = @uid";
sql += " AND id = @uid";
parameters.Add(new SqlParameter("@uid", uid));
}
if (!string.IsNullOrEmpty(cate))
if (!string.IsNullOrEmpty(searchKey))
{
sql += " AND j.cate = @cate";
parameters.Add(new SqlParameter("@cate", cate));
}
if (!string.IsNullOrEmpty(doit))
{
sql += " AND j.doit = @doit";
parameters.Add(new SqlParameter("@doit", doit));
sql += " AND (requestpart LIKE @searchKey OR package LIKE @searchKey OR projectName LIKE @searchKey OR process LIKE @searchKey OR [type] LIKE @searchKey OR description LIKE @searchKey)";
parameters.Add(new SqlParameter("@searchKey", "%" + searchKey + "%"));
}
sql += " ORDER BY j.jdate DESC, j.idx DESC";
sql += " ORDER BY pdate DESC, idx DESC";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddRange(parameters.ToArray());
var da = new SqlDataAdapter(cmd);
var dt = new DataTable();
da.Fill(dt);
da.Dispose();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(new { Success = true, Data = dt }, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddRange(parameters.ToArray());
using (var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
return JsonConvert.SerializeObject(new { Success = true, Data = dt });
}
}
}
catch (Exception ex)
{
@@ -88,18 +73,17 @@ namespace Project.Web
ORDER BY u.name";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
var da = new SqlDataAdapter(cmd);
var dt = new DataTable();
da.Fill(dt);
da.Dispose();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
using (var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
return JsonConvert.SerializeObject(dt);
}
}
}
catch (Exception ex)
{
@@ -109,36 +93,40 @@ namespace Project.Web
}
/// <summary>
/// 업무일지 상세 조회
/// 업무일지 상세 조회 (vJobReportForUser 뷰 사용)
/// </summary>
public string Jobreport_GetDetail(int id)
{
try
{
var sql = @"SELECT j.*, u.name as userName
FROM EETGW_Jobreport j WITH (nolock)
LEFT JOIN Users u ON j.uid = u.id
WHERE j.idx = @idx AND j.gcode = @gcode";
var sql = @"SELECT idx, pidx, pdate, id, name, type, svalue, hrs, ot, requestpart, package,
userprocess, status, projectName, description, ww, otpms, process
FROM vJobReportForUser WITH (nolock)
WHERE idx = @idx AND gcode = @gcode";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@idx", id);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
var da = new SqlDataAdapter(cmd);
var dt = new DataTable();
da.Fill(dt);
da.Dispose();
cmd.Dispose();
cn.Dispose();
if (dt.Rows.Count > 0)
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
return JsonConvert.SerializeObject(new { Success = true, Data = dt.Rows[0] }, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
cmd.Parameters.AddWithValue("@idx", id);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
using (var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count > 0)
{
var row = dt.Rows[0];
var data = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
{
data[col.ColumnName] = row[col] == DBNull.Value ? null : row[col];
}
return JsonConvert.SerializeObject(new { Success = true, Data = data });
}
return JsonConvert.SerializeObject(new { Success = false, Message = "데이터를 찾을 수 없습니다." });
}
}
return JsonConvert.SerializeObject(new { Success = false, Message = "데이터를 찾을 수 없습니다." });
}
catch (Exception ex)
{
@@ -147,37 +135,50 @@ namespace Project.Web
}
/// <summary>
/// 업무일지 추가
/// 업무일지 추가 (JobReport 테이블)
/// </summary>
public string Jobreport_Add(string jdate, string cate, string title, string doit, string remark, string jfrom, string jto)
public string Jobreport_Add(string pdate, string projectName, string requestpart, string package,
string type, string process, string status, string description, double hrs, double ot, string jobgrp, string tag)
{
try
{
var sql = @"INSERT INTO EETGW_Jobreport (gcode, uid, jdate, cate, title, doit, remark, jfrom, jto, wuid, wdate)
VALUES (@gcode, @uid, @jdate, @cate, @title, @doit, @remark, @jfrom, @jto, @wuid, GETDATE());
// 마감 체크
var smon = pdate.Substring(0, 7);
if (DBM.GetMagamStatus(smon))
{
return JsonConvert.SerializeObject(new { Success = false, Message = $"등록일이 속한 월({smon})이 마감되었습니다." });
}
var sql = @"INSERT INTO JobReport (gcode, uid, pdate, projectName, requestpart, package,
type, process, status, description, hrs, ot, jobgrp, tag, wuid, wdate, pidx)
VALUES (@gcode, @uid, @pdate, @projectName, @requestpart, @package,
@type, @process, @status, @description, @hrs, @ot, @jobgrp, @tag, @wuid, GETDATE(), -1);
SELECT SCOPE_IDENTITY();";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@uid", info.Login.no);
cmd.Parameters.AddWithValue("@jdate", jdate ?? DateTime.Now.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("@cate", cate ?? "");
cmd.Parameters.AddWithValue("@title", title ?? "");
cmd.Parameters.AddWithValue("@doit", doit ?? "");
cmd.Parameters.AddWithValue("@remark", remark ?? "");
cmd.Parameters.AddWithValue("@jfrom", jfrom ?? "");
cmd.Parameters.AddWithValue("@jto", jto ?? "");
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@uid", info.Login.no);
cmd.Parameters.AddWithValue("@pdate", pdate);
cmd.Parameters.AddWithValue("@projectName", projectName ?? "");
cmd.Parameters.AddWithValue("@requestpart", requestpart ?? "");
cmd.Parameters.AddWithValue("@package", package ?? "");
cmd.Parameters.AddWithValue("@type", type ?? "");
cmd.Parameters.AddWithValue("@process", process ?? "");
cmd.Parameters.AddWithValue("@status", status ?? "진행 완료");
cmd.Parameters.AddWithValue("@description", description ?? "");
cmd.Parameters.AddWithValue("@hrs", hrs);
cmd.Parameters.AddWithValue("@ot", ot);
cmd.Parameters.AddWithValue("@jobgrp", jobgrp ?? "");
cmd.Parameters.AddWithValue("@tag", tag ?? "");
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
cn.Open();
var newId = Convert.ToInt32(cmd.ExecuteScalar());
cn.Close();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(new { Success = true, Message = "저장되었습니다.", Data = new { idx = newId } });
cn.Open();
var newId = Convert.ToInt32(cmd.ExecuteScalar());
return JsonConvert.SerializeObject(new { Success = true, Message = "저장되었습니다.", Data = new { idx = newId } });
}
}
catch (Exception ex)
{
@@ -186,39 +187,73 @@ namespace Project.Web
}
/// <summary>
/// 업무일지 수정
/// 업무일지 수정 (JobReport 테이블)
/// </summary>
public string Jobreport_Edit(int idx, string jdate, string cate, string title, string doit, string remark, string jfrom, string jto)
public string Jobreport_Edit(int idx, string pdate, string projectName, string requestpart, string package,
string type, string process, string status, string description, double hrs, double ot, string jobgrp, string tag)
{
try
{
var sql = @"UPDATE EETGW_Jobreport SET
jdate = @jdate, cate = @cate, title = @title, doit = @doit,
remark = @remark, jfrom = @jfrom, jto = @jto,
// 권한 체크
int curLevel = Math.Max(info.Login.level, DBM.getAuth(DBM.eAuthType.jobreport));
// 마감 체크
var smon = pdate.Substring(0, 7);
if (DBM.GetMagamStatus(smon))
{
return JsonConvert.SerializeObject(new { Success = false, Message = $"등록일이 속한 월({smon})이 마감되었습니다." });
}
// 본인 자료인지 체크 (관리자가 아닌 경우)
if (curLevel < 5)
{
var checkSql = "SELECT uid FROM JobReport WHERE idx = @idx AND gcode = @gcode";
var cs2 = Properties.Settings.Default.gwcs;
using (var cn2 = new SqlConnection(cs2))
using (var cmd2 = new SqlCommand(checkSql, cn2))
{
cmd2.Parameters.AddWithValue("@idx", idx);
cmd2.Parameters.AddWithValue("@gcode", info.Login.gcode);
cn2.Open();
var ownerUid = cmd2.ExecuteScalar()?.ToString();
if (ownerUid != info.Login.no)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "타인의 자료는 수정할 수 없습니다." });
}
}
}
var sql = @"UPDATE JobReport SET
pdate = @pdate, projectName = @projectName, requestpart = @requestpart,
package = @package, type = @type, process = @process, status = @status,
description = @description, hrs = @hrs, ot = @ot, jobgrp = @jobgrp, tag = @tag,
wuid = @wuid, wdate = GETDATE()
WHERE idx = @idx AND gcode = @gcode";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@idx", idx);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@jdate", jdate ?? DateTime.Now.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("@cate", cate ?? "");
cmd.Parameters.AddWithValue("@title", title ?? "");
cmd.Parameters.AddWithValue("@doit", doit ?? "");
cmd.Parameters.AddWithValue("@remark", remark ?? "");
cmd.Parameters.AddWithValue("@jfrom", jfrom ?? "");
cmd.Parameters.AddWithValue("@jto", jto ?? "");
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@idx", idx);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@pdate", pdate);
cmd.Parameters.AddWithValue("@projectName", projectName ?? "");
cmd.Parameters.AddWithValue("@requestpart", requestpart ?? "");
cmd.Parameters.AddWithValue("@package", package ?? "");
cmd.Parameters.AddWithValue("@type", type ?? "");
cmd.Parameters.AddWithValue("@process", process ?? "");
cmd.Parameters.AddWithValue("@status", status ?? "");
cmd.Parameters.AddWithValue("@description", description ?? "");
cmd.Parameters.AddWithValue("@hrs", hrs);
cmd.Parameters.AddWithValue("@ot", ot);
cmd.Parameters.AddWithValue("@jobgrp", jobgrp ?? "");
cmd.Parameters.AddWithValue("@tag", tag ?? "");
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
cn.Open();
var result = cmd.ExecuteNonQuery();
cn.Close();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "수정되었습니다." : "수정에 실패했습니다." });
cn.Open();
var result = cmd.ExecuteNonQuery();
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "수정되었습니다." : "수정에 실패했습니다." });
}
}
catch (Exception ex)
{
@@ -233,21 +268,128 @@ namespace Project.Web
{
try
{
var sql = "DELETE FROM EETGW_Jobreport WHERE idx = @idx AND gcode = @gcode";
// 권한 체크
int curLevel = Math.Max(info.Login.level, DBM.getAuth(DBM.eAuthType.jobreport));
// 본인 자료인지 체크 (관리자가 아닌 경우)
if (curLevel < 5)
{
var checkSql = "SELECT uid, pdate FROM JobReport WHERE idx = @idx AND gcode = @gcode";
var cs2 = Properties.Settings.Default.gwcs;
using (var cn2 = new SqlConnection(cs2))
using (var cmd2 = new SqlCommand(checkSql, cn2))
{
cmd2.Parameters.AddWithValue("@idx", idx);
cmd2.Parameters.AddWithValue("@gcode", info.Login.gcode);
cn2.Open();
using (var reader = cmd2.ExecuteReader())
{
if (reader.Read())
{
var ownerUid = reader["uid"]?.ToString();
var pdate = reader["pdate"]?.ToString();
if (ownerUid != info.Login.no)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "타인의 자료는 삭제할 수 없습니다." });
}
// 마감 체크
if (!string.IsNullOrEmpty(pdate) && pdate.Length >= 7)
{
var smon = pdate.Substring(0, 7);
if (DBM.GetMagamStatus(smon))
{
return JsonConvert.SerializeObject(new { Success = false, Message = $"등록일이 속한 월({smon})이 마감되었습니다." });
}
}
}
}
}
}
var sql = "DELETE FROM JobReport WHERE idx = @idx AND gcode = @gcode";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@idx", idx);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@idx", idx);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cn.Open();
var result = cmd.ExecuteNonQuery();
cn.Close();
cmd.Dispose();
cn.Dispose();
cn.Open();
var result = cmd.ExecuteNonQuery();
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "삭제되었습니다." : "삭제에 실패했습니다." });
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "삭제되었습니다." : "삭제에 실패했습니다." });
/// <summary>
/// 업무형태 목록 조회 (Common 테이블, grp='15')
/// 트리뷰 형태: process > jobgrp > type
/// </summary>
public string Jobreport_GetJobTypes(string process = "")
{
try
{
var sql = @"SELECT idx, code, memo as [type], svalue as jobgrp, svalue2 as process
FROM Common WITH (nolock)
WHERE gcode = @gcode AND grp = '15'";
var parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("@gcode", info.Login.gcode));
if (!string.IsNullOrEmpty(process))
{
sql += " AND svalue2 = @process";
parameters.Add(new SqlParameter("@process", process));
}
sql += " ORDER BY svalue2, svalue, memo";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddRange(parameters.ToArray());
using (var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
return JsonConvert.SerializeObject(new { Success = true, Data = dt });
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 업무일지 권한 정보 조회
/// 본인이거나 권한 레벨 5 이상이면 OT 열을 볼 수 있음
/// </summary>
public string Jobreport_GetPermission(string targetUserId)
{
try
{
int curLevel = Math.Max(info.Login.level, DBM.getAuth(DBM.eAuthType.jobreport));
bool canViewOT = string.IsNullOrEmpty(targetUserId) ||
targetUserId == info.Login.no ||
curLevel >= 5;
return JsonConvert.SerializeObject(new
{
Success = true,
CurrentUserId = info.Login.no,
Level = curLevel,
CanViewOT = canViewOT
});
}
catch (Exception ex)
{

View File

@@ -191,8 +191,19 @@ namespace Project.Web
{
mainForm.OnLoginCompleted();
}
break;
}
else if (form is Dialog.fDashboard dashForm)
{
if (dashForm.InvokeRequired)
{
dashForm.Invoke(new Action(() => dashForm.RefreshPage()));
}
else
{
dashForm.RefreshPage();
}
}
}
}
catch (Exception ex)
@@ -258,6 +269,30 @@ namespace Project.Web
}
}
/// <summary>
/// 현재 로그인 상태 확인
/// </summary>
public string CheckLoginStatus()
{
var isLoggedIn = !string.IsNullOrEmpty(info.Login.no);
var result = new
{
Success = true,
IsLoggedIn = isLoggedIn,
User = isLoggedIn ? new
{
Id = info.Login.no,
Name = info.Login.nameK,
NameE = info.Login.nameE,
Dept = info.Login.dept,
Email = info.Login.email,
Level = info.Login.level,
Gcode = info.Login.gcode
} : null
};
return JsonConvert.SerializeObject(result);
}
/// <summary>
/// 그룹 목록 조회
/// </summary>
@@ -288,6 +323,68 @@ namespace Project.Web
return JsonConvert.SerializeObject(result);
}
/// <summary>
/// 로그아웃 처리
/// </summary>
public string Logout()
{
try
{
// 로그인 정보 초기화
info.Login.no = "";
info.Login.nameK = "";
info.Login.nameE = "";
info.Login.dept = "";
info.Login.email = "";
info.Login.level = 0;
info.Login.gcode = "";
info.Login.hp = "";
info.Login.tel = "";
info.Login.title = "";
info.Login.process = "";
info.Login.permission = 0;
info.Login.gpermission = 0;
// fMain의 CloseAllForm 호출
CallMainFormCloseAllForm();
return JsonConvert.SerializeObject(new { Success = true, Message = "로그아웃 되었습니다." });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// fMain의 CloseAllForm() 호출
/// </summary>
private void CallMainFormCloseAllForm()
{
try
{
foreach (Form form in Application.OpenForms)
{
if (form is fMain mainForm)
{
if (mainForm.InvokeRequired)
{
mainForm.Invoke(new Action(() => mainForm.CloseAllFormPublic()));
}
else
{
mainForm.CloseAllFormPublic();
}
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine($"CloseAllForm 호출 오류: {ex.Message}");
}
}
#endregion
}
}

View File

@@ -0,0 +1,200 @@
using System;
using System.Data;
using System.Data.SqlClient;
using Newtonsoft.Json;
using FCOMMON;
namespace Project.Web
{
public partial class MachineBridge
{
#region MailForm API ()
/// <summary>
/// 메일양식 목록 조회
/// </summary>
public string MailForm_GetList()
{
try
{
var sql = @"SELECT idx, gcode, cate, title, tolist, bcc, cc, subject, tail, body,
selfTo, selfCC, selfBCC, wuid, wdate, exceptmail, exceptmailcc
FROM MailForm WITH (nolock)
WHERE gcode = @gcode
ORDER BY cate, title";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
using (var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
return JsonConvert.SerializeObject(new { Success = true, Data = dt });
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 메일양식 상세 조회
/// </summary>
public string MailForm_GetDetail(int idx)
{
try
{
var sql = @"SELECT idx, gcode, cate, title, tolist, bcc, cc, subject, tail, body,
selfTo, selfCC, selfBCC, wuid, wdate, exceptmail, exceptmailcc
FROM MailForm WITH (nolock)
WHERE idx = @idx";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@idx", idx);
using (var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count > 0)
{
return JsonConvert.SerializeObject(new { Success = true, Data = dt.Rows[0] });
}
return JsonConvert.SerializeObject(new { Success = false, Message = "데이터를 찾을 수 없습니다." });
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 메일양식 추가
/// </summary>
public string MailForm_Add(string cate, string title, string tolist, string bcc, string cc,
string subject, string tail, string body, bool selfTo, bool selfCC, bool selfBCC,
string exceptmail, string exceptmailcc)
{
try
{
var sql = @"INSERT INTO MailForm (gcode, cate, title, tolist, bcc, cc, subject, tail, body,
selfTo, selfCC, selfBCC, wuid, wdate, exceptmail, exceptmailcc)
VALUES (@gcode, @cate, @title, @tolist, @bcc, @cc, @subject, @tail, @body,
@selfTo, @selfCC, @selfBCC, @wuid, GETDATE(), @exceptmail, @exceptmailcc);
SELECT SCOPE_IDENTITY();";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@cate", cate ?? "");
cmd.Parameters.AddWithValue("@title", title ?? "");
cmd.Parameters.AddWithValue("@tolist", tolist ?? "");
cmd.Parameters.AddWithValue("@bcc", bcc ?? "");
cmd.Parameters.AddWithValue("@cc", cc ?? "");
cmd.Parameters.AddWithValue("@subject", subject ?? "");
cmd.Parameters.AddWithValue("@tail", tail ?? "");
cmd.Parameters.AddWithValue("@body", body ?? "");
cmd.Parameters.AddWithValue("@selfTo", selfTo);
cmd.Parameters.AddWithValue("@selfCC", selfCC);
cmd.Parameters.AddWithValue("@selfBCC", selfBCC);
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
cmd.Parameters.AddWithValue("@exceptmail", exceptmail ?? "");
cmd.Parameters.AddWithValue("@exceptmailcc", exceptmailcc ?? "");
cn.Open();
var newIdx = cmd.ExecuteScalar();
return JsonConvert.SerializeObject(new { Success = true, Message = "등록되었습니다.", idx = newIdx });
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 메일양식 수정
/// </summary>
public string MailForm_Edit(int idx, string cate, string title, string tolist, string bcc, string cc,
string subject, string tail, string body, bool selfTo, bool selfCC, bool selfBCC,
string exceptmail, string exceptmailcc)
{
try
{
var sql = @"UPDATE MailForm SET
cate = @cate, title = @title, tolist = @tolist, bcc = @bcc, cc = @cc,
subject = @subject, tail = @tail, body = @body,
selfTo = @selfTo, selfCC = @selfCC, selfBCC = @selfBCC,
wuid = @wuid, wdate = GETDATE(), exceptmail = @exceptmail, exceptmailcc = @exceptmailcc
WHERE idx = @idx";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@idx", idx);
cmd.Parameters.AddWithValue("@cate", cate ?? "");
cmd.Parameters.AddWithValue("@title", title ?? "");
cmd.Parameters.AddWithValue("@tolist", tolist ?? "");
cmd.Parameters.AddWithValue("@bcc", bcc ?? "");
cmd.Parameters.AddWithValue("@cc", cc ?? "");
cmd.Parameters.AddWithValue("@subject", subject ?? "");
cmd.Parameters.AddWithValue("@tail", tail ?? "");
cmd.Parameters.AddWithValue("@body", body ?? "");
cmd.Parameters.AddWithValue("@selfTo", selfTo);
cmd.Parameters.AddWithValue("@selfCC", selfCC);
cmd.Parameters.AddWithValue("@selfBCC", selfBCC);
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
cmd.Parameters.AddWithValue("@exceptmail", exceptmail ?? "");
cmd.Parameters.AddWithValue("@exceptmailcc", exceptmailcc ?? "");
cn.Open();
cmd.ExecuteNonQuery();
return JsonConvert.SerializeObject(new { Success = true, Message = "수정되었습니다." });
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 메일양식 삭제
/// </summary>
public string MailForm_Delete(int idx)
{
try
{
var sql = "DELETE FROM MailForm WHERE idx = @idx";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@idx", idx);
cn.Open();
cmd.ExecuteNonQuery();
return JsonConvert.SerializeObject(new { Success = true, Message = "삭제되었습니다." });
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
#endregion
}
}

View File

@@ -79,6 +79,7 @@ namespace Project.Web
}
}
/// <summary>
/// 할일 추가
/// </summary>

View File

@@ -0,0 +1,290 @@
using System;
using System.Linq;
using Newtonsoft.Json;
using FCOMMON;
namespace Project.Web
{
public partial class MachineBridge
{
#region User API
/// <summary>
/// 현재 로그인한 사용자 정보 조회
/// </summary>
public string GetCurrentUserInfo()
{
try
{
if (string.IsNullOrEmpty(info.Login.no))
{
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
}
var taUser = new dsMSSQLTableAdapters.UsersTableAdapter();
var taGUser = new dsMSSQLTableAdapters.EETGW_GroupUserTableAdapter();
var drUser = taUser.GetID(info.Login.no).FirstOrDefault();
var drGUser = taGUser.GetbyID(info.Login.gcode, info.Login.no).FirstOrDefault();
if (drUser == null)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "사용자 정보를 찾을 수 없습니다." });
}
var userInfo = new
{
Id = drUser.id,
NameK = drUser.name,
NameE = drUser.nameE,
Dept = drUser.dept,
Grade = drUser.grade,
Email = drUser.email,
Tel = drUser.tel,
Hp = drUser.hp,
DateIn = drUser.indate,
DateO = drUser.outdate,
Memo = drUser.memo,
Process = drGUser?.Process ?? "",
State = drGUser?.state ?? "",
UseJobReport = drGUser != null && !drGUser.IsuseJobReportNull() && drGUser.useJobReport,
UseUserState = drGUser != null && !drGUser.IsuseUserStateNull() && drGUser.useUserState,
ExceptHoly = drGUser != null && !drGUser.IsexceptHolyNull() && drGUser.exceptHoly,
Level = drGUser?.level ?? 0
};
return JsonConvert.SerializeObject(new { Success = true, Data = userInfo });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "사용자 정보 조회 실패: " + ex.Message });
}
}
/// <summary>
/// 사용자 정보 조회 (ID로)
/// </summary>
public string GetUserInfoById(string userId)
{
try
{
if (string.IsNullOrEmpty(userId))
{
return JsonConvert.SerializeObject(new { Success = false, Message = "사용자 ID를 입력하세요." });
}
var taUser = new dsMSSQLTableAdapters.UsersTableAdapter();
var taGUser = new dsMSSQLTableAdapters.EETGW_GroupUserTableAdapter();
var drUser = taUser.GetID(userId).FirstOrDefault();
var drGUser = taGUser.GetbyID(info.Login.gcode, userId).FirstOrDefault();
if (drUser == null)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "등록된 사용자가 없습니다." });
}
var userInfo = new
{
Id = drUser.id,
NameK = drUser.name,
NameE = drUser.nameE,
Dept = drUser.dept,
Grade = drUser.grade,
Email = drUser.email,
Tel = drUser.tel,
Hp = drUser.hp,
DateIn = drUser.indate,
DateO = drUser.outdate,
Memo = drUser.memo,
Process = drGUser?.Process ?? "",
State = drGUser?.state ?? "",
UseJobReport = drGUser != null && !drGUser.IsuseJobReportNull() && drGUser.useJobReport,
UseUserState = drGUser != null && !drGUser.IsuseUserStateNull() && drGUser.useUserState,
ExceptHoly = drGUser != null && !drGUser.IsexceptHolyNull() && drGUser.exceptHoly,
Level = drGUser?.level ?? 0
};
return JsonConvert.SerializeObject(new { Success = true, Data = userInfo });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "사용자 정보 조회 실패: " + ex.Message });
}
}
/// <summary>
/// 사용자 정보 저장
/// </summary>
public string SaveUserInfo(string jsonData)
{
try
{
var userData = JsonConvert.DeserializeObject<UserInfoData>(jsonData);
if (userData == null)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "잘못된 데이터 형식입니다." });
}
var gcode = info.Login.gcode;
var uid = userData.Id;
// 현재 사용자 권한 확인
int curLevel = Math.Max(info.Login.level, DBM.getAuth(DBM.eAuthType.account));
// 그룹 사용자 정보 처리
var taUserGrp = new dsMSSQLTableAdapters.EETGW_GroupUserTableAdapter();
var dtUserGrp = taUserGrp.GetData(gcode);
var drGuser = dtUserGrp.Where(t => t.uid == uid).FirstOrDefault();
if (drGuser != null)
{
drGuser.Process = userData.Process ?? "";
drGuser.state = userData.State ?? "";
if (curLevel > 4)
{
drGuser.useJobReport = userData.UseJobReport;
drGuser.useUserState = userData.UseUserState;
drGuser.exceptHoly = userData.ExceptHoly;
}
}
else
{
drGuser = dtUserGrp.NewEETGW_GroupUserRow();
drGuser.wuid = info.Login.no;
drGuser.wdate = DateTime.Now;
drGuser.gcode = gcode;
drGuser.level = 1;
drGuser.uid = uid;
drGuser.state = userData.State ?? "";
drGuser.Process = userData.Process ?? "";
drGuser.useJobReport = userData.UseJobReport;
drGuser.useUserState = userData.UseUserState;
drGuser.exceptHoly = userData.ExceptHoly;
dtUserGrp.AddEETGW_GroupUserRow(drGuser);
}
// 사용자 정보 처리
var tauser = new dsMSSQLTableAdapters.UsersTableAdapter();
var dtuser = tauser.GetID(uid);
var drUser = dtuser.FirstOrDefault();
if (drUser == null)
{
drUser = dtuser.NewUsersRow();
drUser.wuid = info.Login.no;
drUser.wdate = DateTime.Now;
drUser.gcode = gcode;
drUser.level = 1;
drUser.id = uid;
drUser.password = "B6589FC6AB0DC82CF12099D1C2D40AB994E8410C"; // 기본값 0
dtuser.AddUsersRow(drUser);
}
drUser.name = userData.NameK ?? "";
drUser.nameE = userData.NameE ?? "";
drUser.dept = userData.Dept ?? "";
drUser.email = userData.Email ?? "";
drUser.tel = userData.Tel ?? "";
drUser.hp = userData.Hp ?? "";
drUser.indate = userData.DateIn ?? "";
drUser.outdate = userData.DateO ?? "";
drUser.memo = userData.Memo ?? "";
drUser.processs = userData.Process ?? "";
drUser.grade = userData.Grade ?? "";
drUser.EndEdit();
var cnt1 = taUserGrp.Update(dtUserGrp);
var cnt2 = tauser.Update(dtuser);
taUserGrp.Dispose();
tauser.Dispose();
return JsonConvert.SerializeObject(new { Success = true, Message = "저장되었습니다." });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "저장 실패: " + ex.Message });
}
}
/// <summary>
/// 비밀번호 변경
/// </summary>
public string ChangePassword(string oldPassword, string newPassword)
{
try
{
if (string.IsNullOrEmpty(info.Login.no))
{
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인이 필요합니다." });
}
if (string.IsNullOrEmpty(newPassword))
{
return JsonConvert.SerializeObject(new { Success = false, Message = "새 비밀번호를 입력하세요." });
}
var uid = info.Login.no;
int curLevel = Math.Max(info.Login.level, DBM.getAuth(DBM.eAuthType.account));
var taUser = new dsMSSQLTableAdapters.UsersTableAdapter();
var dtUser = taUser.GetID(uid);
var drUser = dtUser.FirstOrDefault();
if (drUser == null)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "사용자 정보를 찾을 수 없습니다." });
}
// 관리자가 아니면 기존 암호 확인
if (curLevel < 5)
{
var encOldPass = Pub.MakePasswordEnc(oldPassword);
if (!encOldPass.Equals(drUser.password))
{
return JsonConvert.SerializeObject(new { Success = false, Message = "기존 암호가 일치하지 않습니다." });
}
}
drUser.password = Pub.MakePasswordEnc(newPassword);
drUser.EndEdit();
taUser.Update(dtUser);
taUser.Dispose();
return JsonConvert.SerializeObject(new { Success = true, Message = "비밀번호가 변경되었습니다." });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "비밀번호 변경 실패: " + ex.Message });
}
}
#endregion
}
/// <summary>
/// 사용자 정보 데이터 클래스
/// </summary>
public class UserInfoData
{
public string Id { get; set; }
public string NameK { get; set; }
public string NameE { get; set; }
public string Dept { get; set; }
public string Grade { get; set; }
public string Email { get; set; }
public string Tel { get; set; }
public string Hp { get; set; }
public string DateIn { get; set; }
public string DateO { get; set; }
public string Memo { get; set; }
public string Process { get; set; }
public string State { get; set; }
public bool UseJobReport { get; set; }
public bool UseUserState { get; set; }
public bool ExceptHoly { get; set; }
}
}

View File

@@ -0,0 +1,228 @@
using System;
using System.Data;
using System.Data.SqlClient;
using Newtonsoft.Json;
using FCOMMON;
namespace Project.Web
{
public partial class MachineBridge
{
#region UserGroup API (/)
/// <summary>
/// 그룹 목록 조회
/// </summary>
public string UserGroup_GetList()
{
try
{
var sql = @"SELECT dept, gcode, path_kj, permission, advpurchase, advkisul,
managerinfo, devinfo, usemail
FROM UserGroup WITH (nolock)
WHERE gcode = @gcode
ORDER BY dept";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
using (var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
return JsonConvert.SerializeObject(new { Success = true, Data = dt });
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 그룹 정보 추가
/// </summary>
public string UserGroup_Add(string dept, string path_kj, int permission,
bool advpurchase, bool advkisul, string managerinfo, string devinfo, bool usemail)
{
try
{
// 중복 체크
var checkSql = "SELECT COUNT(*) FROM UserGroup WHERE gcode = @gcode AND dept = @dept";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
{
cn.Open();
using (var checkCmd = new SqlCommand(checkSql, cn))
{
checkCmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
checkCmd.Parameters.AddWithValue("@dept", dept);
var count = (int)checkCmd.ExecuteScalar();
if (count > 0)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "이미 존재하는 부서명입니다." });
}
}
var sql = @"INSERT INTO UserGroup (dept, gcode, path_kj, permission, advpurchase, advkisul, managerinfo, devinfo, usemail)
VALUES (@dept, @gcode, @path_kj, @permission, @advpurchase, @advkisul, @managerinfo, @devinfo, @usemail)";
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@dept", dept ?? "");
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@path_kj", path_kj ?? "");
cmd.Parameters.AddWithValue("@permission", permission);
cmd.Parameters.AddWithValue("@advpurchase", advpurchase);
cmd.Parameters.AddWithValue("@advkisul", advkisul);
cmd.Parameters.AddWithValue("@managerinfo", managerinfo ?? "");
cmd.Parameters.AddWithValue("@devinfo", devinfo ?? "");
cmd.Parameters.AddWithValue("@usemail", usemail);
cmd.ExecuteNonQuery();
return JsonConvert.SerializeObject(new { Success = true, Message = "등록되었습니다." });
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 그룹 정보 수정
/// </summary>
public string UserGroup_Edit(string originalDept, string dept, string path_kj, int permission,
bool advpurchase, bool advkisul, string managerinfo, string devinfo, bool usemail)
{
try
{
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
{
cn.Open();
// 부서명이 변경되었을 경우 중복 체크
if (originalDept != dept)
{
var checkSql = "SELECT COUNT(*) FROM UserGroup WHERE gcode = @gcode AND dept = @dept";
using (var checkCmd = new SqlCommand(checkSql, cn))
{
checkCmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
checkCmd.Parameters.AddWithValue("@dept", dept);
var count = (int)checkCmd.ExecuteScalar();
if (count > 0)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "이미 존재하는 부서명입니다." });
}
}
}
var sql = @"UPDATE UserGroup SET
dept = @dept, path_kj = @path_kj, permission = @permission,
advpurchase = @advpurchase, advkisul = @advkisul,
managerinfo = @managerinfo, devinfo = @devinfo, usemail = @usemail
WHERE gcode = @gcode AND dept = @originalDept";
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@originalDept", originalDept);
cmd.Parameters.AddWithValue("@dept", dept ?? "");
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@path_kj", path_kj ?? "");
cmd.Parameters.AddWithValue("@permission", permission);
cmd.Parameters.AddWithValue("@advpurchase", advpurchase);
cmd.Parameters.AddWithValue("@advkisul", advkisul);
cmd.Parameters.AddWithValue("@managerinfo", managerinfo ?? "");
cmd.Parameters.AddWithValue("@devinfo", devinfo ?? "");
cmd.Parameters.AddWithValue("@usemail", usemail);
cmd.ExecuteNonQuery();
return JsonConvert.SerializeObject(new { Success = true, Message = "수정되었습니다." });
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 그룹 삭제
/// </summary>
public string UserGroup_Delete(string dept)
{
try
{
// 해당 그룹에 소속된 사용자가 있는지 확인
var checkSql = "SELECT COUNT(*) FROM GroupUser WHERE gcode = @gcode AND dept = @dept";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
{
cn.Open();
using (var checkCmd = new SqlCommand(checkSql, cn))
{
checkCmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
checkCmd.Parameters.AddWithValue("@dept", dept);
var count = (int)checkCmd.ExecuteScalar();
if (count > 0)
{
return JsonConvert.SerializeObject(new { Success = false, Message = $"해당 그룹에 {count}명의 사용자가 소속되어 있어 삭제할 수 없습니다." });
}
}
var sql = "DELETE FROM UserGroup WHERE gcode = @gcode AND dept = @dept";
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@dept", dept);
cmd.ExecuteNonQuery();
return JsonConvert.SerializeObject(new { Success = true, Message = "삭제되었습니다." });
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 권한 정보 목록 (프론트엔드용)
/// </summary>
public string UserGroup_GetPermissionInfo()
{
try
{
var permissions = new[]
{
new { index = 0, name = "menu_purchase", label = "구매신청", description = "구매신청 메뉴 표시" },
new { index = 1, name = "menu_project", label = "프로젝트", description = "프로젝트 메뉴 표시" },
new { index = 2, name = "menu_history", label = "업무일지", description = "업무일지 메뉴 표시" },
new { index = 3, name = "menu_jago", label = "품목재고", description = "품목재고 메뉴 표시" },
new { index = 4, name = "menu_equipment", label = "장비목록", description = "장비목록 메뉴 표시" },
new { index = 5, name = "menu_workday", label = "근태관리", description = "근태관리 메뉴 표시" },
new { index = 6, name = "purchase_adv", label = "(구매)상세입력", description = "구매신청 상세입력 권한" },
new { index = 7, name = "menu_docu", label = "문서", description = "문서 메뉴 표시" },
new { index = 8, name = "menu_logdata", label = "운영기록", description = "운영기록 메뉴 표시" },
new { index = 9, name = "jobreport_kisul", label = "업무일지-기술료", description = "업무일지 기술료 보기 권한" },
new { index = 10, name = "jobreport_editblock", label = "업무일지-편집제한", description = "업무일지 편집 제한" },
};
return JsonConvert.SerializeObject(new { Success = true, Data = permissions });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
#endregion
}
}

View File

@@ -0,0 +1,378 @@
using System;
using System.Data;
using System.Data.SqlClient;
using Newtonsoft.Json;
using FCOMMON;
namespace Project.Web
{
public partial class MachineBridge
{
#region UserList API
/// <summary>
/// 현재 사용자 권한 레벨 조회 (로그인 레벨 + account 권한 중 높은 값)
/// </summary>
public string UserList_GetCurrentLevel()
{
try
{
int curLevel = Math.Max(info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.account));
return JsonConvert.SerializeObject(new
{
Success = true,
Data = new
{
Level = curLevel,
CurrentUserId = info.Login.no,
CanEdit = curLevel >= 5
}
});
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "권한 조회 실패: " + ex.Message });
}
}
/// <summary>
/// 부서 목록 조회
/// </summary>
public string UserList_GetDepts()
{
try
{
var sql = "SELECT DISTINCT dept FROM UserGroup WITH (NOLOCK) WHERE gcode = @gcode AND ISNULL(dept,'') <> '' ORDER BY dept";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
var da = new SqlDataAdapter(cmd);
var dt = new DataTable();
da.Fill(dt);
da.Dispose();
cmd.Dispose();
cn.Dispose();
var result = new System.Collections.Generic.List<string>();
foreach (DataRow dr in dt.Rows)
{
result.Add(dr["dept"]?.ToString() ?? "");
}
return JsonConvert.SerializeObject(new { Success = true, Data = result });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "부서 조회 실패: " + ex.Message });
}
}
/// <summary>
/// 사용자 목록 조회
/// </summary>
public string UserList_GetList(string process)
{
try
{
if (string.IsNullOrEmpty(process) || process == "%") process = "%";
else process = "%" + process + "%";
var gcode = info.Login.gcode;
System.Diagnostics.Debug.WriteLine($"[UserList_GetList] gcode={gcode}, process={process}");
var sql = @"SELECT gcode, dept, level, name, nameE, grade, email, tel, indate, outdate, hp,
memo, processs, id, state, useJobReport, useUserState, exceptHoly
FROM vGroupUser WITH (NOLOCK)
WHERE gcode = @gcode
AND ISNULL(processs,'') LIKE @process
ORDER BY useUserState DESC, useJobReport DESC, name";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@gcode", gcode);
cmd.Parameters.AddWithValue("@process", process);
var da = new SqlDataAdapter(cmd);
var dt = new DataTable();
da.Fill(dt);
System.Diagnostics.Debug.WriteLine($"[UserList_GetList] 결과 행 수: {dt.Rows.Count}");
da.Dispose();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "사용자 목록 조회 실패: " + ex.Message });
}
}
/// <summary>
/// 사용자 상세 정보 조회
/// </summary>
public string UserList_GetUser(string userId)
{
try
{
var sql = @"SELECT
u.id,
u.name,
u.nameE,
u.grade,
u.email,
u.tel,
u.indate,
u.outdate,
u.hp,
u.processs,
u.state,
u.memo,
gu.level,
gu.useUserState,
gu.useJobReport,
gu.exceptHoly,
gu.dept,
gu.gcode
FROM EETGW_GroupUser gu WITH (NOLOCK)
INNER JOIN Users u WITH (NOLOCK) ON gu.uid = u.id
WHERE gu.gcode = @gcode AND gu.uid = @uid";
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@uid", userId);
var da = new SqlDataAdapter(cmd);
var dt = new DataTable();
da.Fill(dt);
da.Dispose();
cmd.Dispose();
cn.Dispose();
if (dt.Rows.Count > 0)
{
return JsonConvert.SerializeObject(new { Success = true, Data = dt.Rows[0] },
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
}
else
{
return JsonConvert.SerializeObject(new { Success = false, Message = "사용자를 찾을 수 없습니다." });
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "사용자 조회 실패: " + ex.Message });
}
}
/// <summary>
/// 사용자 전체 정보 저장 (Users + GroupUser)
/// </summary>
public string UserList_SaveUserFull(string jsonData)
{
try
{
var userData = JsonConvert.DeserializeObject<UserListFullData>(jsonData);
if (userData == null)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "잘못된 데이터 형식입니다." });
}
// 권한 체크
int curLevel = Math.Max(info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.account));
bool isSelf = info.Login.no == userData.id;
// 본인이 아니고 권한이 없으면 거부
if (!isSelf && curLevel < 5)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "타인의 계정은 편집할 수 없습니다." });
}
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
{
cn.Open();
// Users 테이블 업데이트
var sqlUser = @"UPDATE Users SET
name = @name,
nameE = @nameE,
grade = @grade,
email = @email,
tel = @tel,
hp = @hp,
indate = @indate,
outdate = @outdate,
memo = @memo,
processs = @processs,
state = @state
WHERE id = @id";
using (var cmdUser = new SqlCommand(sqlUser, cn))
{
cmdUser.Parameters.AddWithValue("@id", userData.id);
cmdUser.Parameters.AddWithValue("@name", userData.name ?? "");
cmdUser.Parameters.AddWithValue("@nameE", userData.nameE ?? "");
cmdUser.Parameters.AddWithValue("@grade", userData.grade ?? "");
cmdUser.Parameters.AddWithValue("@email", userData.email ?? "");
cmdUser.Parameters.AddWithValue("@tel", userData.tel ?? "");
cmdUser.Parameters.AddWithValue("@hp", userData.hp ?? "");
cmdUser.Parameters.AddWithValue("@indate", userData.indate ?? "");
cmdUser.Parameters.AddWithValue("@outdate", userData.outdate ?? "");
cmdUser.Parameters.AddWithValue("@memo", userData.memo ?? "");
cmdUser.Parameters.AddWithValue("@processs", userData.processs ?? "");
cmdUser.Parameters.AddWithValue("@state", userData.state ?? "");
cmdUser.ExecuteNonQuery();
}
// EETGW_GroupUser 테이블 업데이트 (관리자만)
if (curLevel >= 5)
{
var sqlGroup = @"UPDATE EETGW_GroupUser SET
level = @level,
useUserState = @useUserState,
useJobReport = @useJobReport,
exceptHoly = @exceptHoly
WHERE gcode = @gcode AND uid = @uid";
using (var cmdGroup = new SqlCommand(sqlGroup, cn))
{
cmdGroup.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmdGroup.Parameters.AddWithValue("@uid", userData.id);
cmdGroup.Parameters.AddWithValue("@level", userData.level);
cmdGroup.Parameters.AddWithValue("@useUserState", userData.useUserState);
cmdGroup.Parameters.AddWithValue("@useJobReport", userData.useJobReport);
cmdGroup.Parameters.AddWithValue("@exceptHoly", userData.exceptHoly);
cmdGroup.ExecuteNonQuery();
}
}
}
return JsonConvert.SerializeObject(new { Success = true, Message = "저장되었습니다." });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "저장 실패: " + ex.Message });
}
}
/// <summary>
/// 사용자 저장 (그룹 설정만)
/// </summary>
public string UserList_SaveGroupUser(string userId, string dept, int level, bool useUserState, bool useJobReport, bool exceptHoly)
{
try
{
// 권한 체크
int curLevel = Math.Max(info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.account));
if (curLevel < 5)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "사용자 관리 권한이 없습니다." });
}
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var sql = @"UPDATE EETGW_GroupUser SET
dept = @dept,
level = @level,
useUserState = @useUserState,
useJobReport = @useJobReport,
exceptHoly = @exceptHoly
WHERE gcode = @gcode AND uid = @uid";
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@uid", userId);
cmd.Parameters.AddWithValue("@dept", dept ?? "");
cmd.Parameters.AddWithValue("@level", level);
cmd.Parameters.AddWithValue("@useUserState", useUserState);
cmd.Parameters.AddWithValue("@useJobReport", useJobReport);
cmd.Parameters.AddWithValue("@exceptHoly", exceptHoly);
cn.Open();
var result = cmd.ExecuteNonQuery();
cn.Close();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "저장되었습니다." : "저장에 실패했습니다." });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "저장 실패: " + ex.Message });
}
}
/// <summary>
/// 사용자 삭제 (그룹에서 제거)
/// </summary>
public string UserList_DeleteGroupUser(string userId)
{
try
{
// 권한 체크
int curLevel = Math.Max(info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.account));
if (curLevel < 5)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "계정 관리자만 사용할 수 있습니다." });
}
var cs = Properties.Settings.Default.gwcs;
var cn = new SqlConnection(cs);
var sql = "DELETE FROM EETGW_GroupUser WHERE gcode = @gcode AND uid = @uid";
var cmd = new SqlCommand(sql, cn);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@uid", userId);
cn.Open();
var result = cmd.ExecuteNonQuery();
cn.Close();
cmd.Dispose();
cn.Dispose();
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "삭제되었습니다." : "삭제에 실패했습니다." });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = "삭제 실패: " + ex.Message });
}
}
#endregion
}
/// <summary>
/// 사용자 전체 정보 데이터 클래스
/// </summary>
public class UserListFullData
{
public string id { get; set; }
public string name { get; set; }
public string nameE { get; set; }
public string grade { get; set; }
public string email { get; set; }
public string tel { get; set; }
public string hp { get; set; }
public string indate { get; set; }
public string outdate { get; set; }
public string memo { get; set; }
public string processs { get; set; }
public string state { get; set; }
public int level { get; set; }
public bool useUserState { get; set; }
public bool useJobReport { get; set; }
public bool exceptHoly { get; set; }
}
}

View File

@@ -20,12 +20,102 @@ namespace Project.Web
public partial class MachineBridge
{
// Reference to the main form to update logic
private Dialog.fDashboardNew _host;
private Dialog.fDashboard _host;
public MachineBridge(Dialog.fDashboardNew host)
// WebSocket 서버 인스턴스
private static Project.Web.WebSocketServer _wsServer;
private static readonly object _wsLock = new object();
private const int WS_PORT = 8082;
public MachineBridge(Dialog.fDashboard host)
{
_host = host;
StartWebSocketServer();
}
#region WebSocket Server Control
/// <summary>
/// WebSocket 서버 시작
/// </summary>
private void StartWebSocketServer()
{
lock (_wsLock)
{
if (_wsServer != null)
{
Console.WriteLine("[WS] WebSocket server already running");
return;
}
try
{
string url = $"http://localhost:{WS_PORT}/";
_wsServer = new Project.Web.WebSocketServer(url, this);
_wsServer.Start();
Console.WriteLine($"[WS] WebSocket server started on port {WS_PORT}");
}
catch (Exception ex)
{
Console.WriteLine($"[WS] Failed to start WebSocket server: {ex.Message}");
}
}
}
/// <summary>
/// WebSocket 서버 중지
/// </summary>
public static void StopWebSocketServer()
{
lock (_wsLock)
{
if (_wsServer != null)
{
_wsServer.Stop();
_wsServer = null;
Console.WriteLine("[WS] WebSocket server stopped");
}
}
}
/// <summary>
/// WebSocket 서버 실행 여부 확인
/// </summary>
public static bool IsWebSocketServerRunning()
{
lock (_wsLock)
{
return _wsServer != null;
}
}
#endregion
#region App Info
/// <summary>
/// 애플리케이션 버전 정보 반환
/// </summary>
public string GetAppVersion()
{
try
{
return JsonConvert.SerializeObject(new
{
Success = true,
ProductName = Application.ProductName,
ProductVersion = Application.ProductVersion,
DisplayVersion = $"{Application.ProductName} v{Application.ProductVersion}"
});
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
#endregion
}
/// <summary>

View File

@@ -0,0 +1,705 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Project.Web
{
/// <summary>
/// GroupWare WebSocket 서버
/// npm run dev 환경에서 핫 리로드 개발을 위한 WebSocket 통신 지원
/// </summary>
public class WebSocketServer
{
private HttpListener _httpListener;
private List<WebSocket> _clients = new List<WebSocket>();
private ConcurrentDictionary<WebSocket, SemaphoreSlim> _socketLocks = new ConcurrentDictionary<WebSocket, SemaphoreSlim>();
private MachineBridge _bridge;
private bool _isRunning = false;
public WebSocketServer(string url, MachineBridge bridge)
{
_bridge = bridge;
_httpListener = new HttpListener();
_httpListener.Prefixes.Add(url);
}
public void Start()
{
if (_isRunning) return;
try
{
_httpListener.Start();
_isRunning = true;
Console.WriteLine($"[WS] GroupWare WebSocket Server Started");
Task.Run(AcceptConnections);
}
catch (Exception ex)
{
Console.WriteLine($"[WS] Start Error: {ex.Message}");
}
}
public void Stop()
{
_isRunning = false;
try
{
_httpListener.Stop();
_httpListener.Close();
}
catch { }
}
private async Task AcceptConnections()
{
while (_httpListener.IsListening && _isRunning)
{
try
{
var context = await _httpListener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
ProcessRequest(context);
}
else
{
context.Response.StatusCode = 400;
context.Response.Close();
}
}
catch (Exception ex)
{
if (_isRunning)
Console.WriteLine($"[WS] Accept Error: {ex.Message}");
}
}
}
private async void ProcessRequest(HttpListenerContext context)
{
WebSocketContext wsContext = null;
try
{
wsContext = await context.AcceptWebSocketAsync(subProtocol: null);
WebSocket socket = wsContext.WebSocket;
_socketLocks.TryAdd(socket, new SemaphoreSlim(1, 1));
lock (_clients) { _clients.Add(socket); }
Console.WriteLine("[WS] Client Connected");
await ReceiveLoop(socket);
}
catch (Exception ex)
{
Console.WriteLine($"[WS] Process Error: {ex.Message}");
}
finally
{
if (wsContext != null)
{
WebSocket socket = wsContext.WebSocket;
lock (_clients) { _clients.Remove(socket); }
if (_socketLocks.TryRemove(socket, out var semaphore))
{
semaphore.Dispose();
}
socket.Dispose();
}
}
}
private async Task ReceiveLoop(WebSocket socket)
{
var buffer = new byte[1024 * 4];
while (socket.State == WebSocketState.Open && _isRunning)
{
try
{
var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
else if (result.MessageType == WebSocketMessageType.Text)
{
string msg = Encoding.UTF8.GetString(buffer, 0, result.Count);
await HandleMessage(msg, socket);
}
}
catch
{
break;
}
}
}
private async Task HandleMessage(string msg, WebSocket socket)
{
try
{
dynamic json = JsonConvert.DeserializeObject(msg);
string type = json.type;
Console.WriteLine($"[WS] Message: {type}");
switch (type)
{
// ===== Todo API =====
case "GET_TODOS":
{
string result = _bridge.Todo_GetTodos();
var response = new { type = "TODOS_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_TODO":
{
int id = json.id;
string result = _bridge.GetTodo(id);
var response = new { type = "TODO_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "CREATE_TODO":
{
string title = json.title ?? "";
string remark = json.remark ?? "";
string expire = json.expire;
int seqno = json.seqno ?? 0;
bool flag = json.flag ?? false;
string request = json.request;
string status = json.status ?? "0";
string result = _bridge.CreateTodo(title, remark, expire, seqno, flag, request, status);
var response = new { type = "TODO_CREATED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "UPDATE_TODO":
{
int idx = json.idx;
string title = json.title ?? "";
string remark = json.remark ?? "";
string expire = json.expire;
int seqno = json.seqno ?? 0;
bool flag = json.flag ?? false;
string request = json.request;
string status = json.status ?? "0";
string result = _bridge.Todo_UpdateTodo(idx, title, remark, expire, seqno, flag, request, status);
var response = new { type = "TODO_UPDATED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "DELETE_TODO":
{
int id = json.id;
string result = _bridge.Todo_DeleteTodo(id);
var response = new { type = "TODO_DELETED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_URGENT_TODOS":
{
string result = _bridge.GetUrgentTodos();
var response = new { type = "URGENT_TODOS_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
// ===== Dashboard API =====
case "GET_PURCHASE_WAIT_COUNT":
{
string result = _bridge.GetPurchaseWaitCount();
var response = new { type = "PURCHASE_WAIT_COUNT_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_TODAY_COUNT_H":
{
string result = _bridge.TodayCountH();
var response = new { type = "TODAY_COUNT_H_DATA", count = int.Parse(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_HOLY_USER":
{
string result = _bridge.GetHolyUser();
var response = new { type = "HOLY_USER_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_HOLY_REQUEST_USER":
{
string result = _bridge.GetHolyRequestUser();
var response = new { type = "HOLY_REQUEST_USER_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_PURCHASE_NR_LIST":
{
string result = _bridge.GetPurchaseNRList();
var response = new { type = "PURCHASE_NR_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_PURCHASE_CR_LIST":
{
string result = _bridge.GetPurchaseCRList();
var response = new { type = "PURCHASE_CR_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_HOLYDAY_REQUEST_COUNT":
{
string result = _bridge.GetHolydayRequestCount();
var response = new { type = "HOLYDAY_REQUEST_COUNT_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_CURRENT_USER_COUNT":
{
string result = _bridge.GetCurrentUserCount();
var response = new { type = "CURRENT_USER_COUNT_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
// ===== Login API =====
case "CHECK_LOGIN_STATUS":
{
string result = _bridge.CheckLoginStatus();
var response = new { type = "LOGIN_STATUS_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "LOGIN":
{
string gcode = json.gcode ?? "";
string id = json.id ?? "";
string password = json.password ?? "";
bool rememberMe = json.rememberMe ?? false;
string result = _bridge.Login(gcode, id, password, rememberMe);
var response = new { type = "LOGIN_RESULT", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "LOGOUT":
{
string result = _bridge.Logout();
var response = new { type = "LOGOUT_RESULT", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_USER_GROUPS":
{
string result = _bridge.GetUserGroups();
var response = new { type = "USER_GROUPS_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_PREVIOUS_LOGIN_INFO":
{
string result = _bridge.GetPreviousLoginInfo();
var response = new { type = "PREVIOUS_LOGIN_INFO_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
// ===== User API =====
case "GET_CURRENT_USER_INFO":
{
string result = _bridge.GetCurrentUserInfo();
var response = new { type = "CURRENT_USER_INFO_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_USER_INFO_BY_ID":
{
string userId = json.userId ?? "";
string result = _bridge.GetUserInfoById(userId);
var response = new { type = "USER_INFO_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "SAVE_USER_INFO":
{
string userData = JsonConvert.SerializeObject(json.userData);
string result = _bridge.SaveUserInfo(userData);
var response = new { type = "USER_INFO_SAVED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "CHANGE_PASSWORD":
{
string oldPassword = json.oldPassword ?? "";
string newPassword = json.newPassword ?? "";
string result = _bridge.ChangePassword(oldPassword, newPassword);
var response = new { type = "PASSWORD_CHANGED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
// ===== Common Code API =====
case "COMMON_GET_GROUPS":
{
string result = _bridge.Common_GetGroups();
var response = new { type = "COMMON_GROUPS_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "COMMON_GET_LIST":
{
string grp = json.grp ?? "99";
string result = _bridge.Common_GetList(grp);
var response = new { type = "COMMON_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "COMMON_SAVE":
{
int idx = json.idx ?? 0;
string grp = json.grp ?? "";
string code = json.code ?? "";
string svalue = json.svalue ?? "";
int ivalue = json.ivalue ?? 0;
float fvalue = json.fvalue ?? 0f;
string svalue2 = json.svalue2 ?? "";
string memo = json.memo ?? "";
string result = _bridge.Common_Save(idx, grp, code, svalue, ivalue, fvalue, svalue2, memo);
var response = new { type = "COMMON_SAVED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "COMMON_DELETE":
{
int idx = json.idx ?? 0;
string result = _bridge.Common_Delete(idx);
var response = new { type = "COMMON_DELETED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
// ===== Items API =====
case "ITEMS_GET_CATEGORIES":
{
string result = _bridge.Items_GetCategories();
var response = new { type = "ITEMS_CATEGORIES_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "ITEMS_GET_LIST":
{
string category = json.category ?? "";
string searchKey = json.searchKey ?? "";
string result = _bridge.Items_GetList(category, searchKey);
var response = new { type = "ITEMS_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "ITEMS_SAVE":
{
int idx = json.idx ?? 0;
string sid = json.sid ?? "";
string cate = json.cate ?? "";
string name = json.name ?? "";
string model = json.model ?? "";
string scale = json.scale ?? "";
string unit = json.unit ?? "";
decimal price = json.price ?? 0m;
string supply = json.supply ?? "";
string manu = json.manu ?? "";
string storage = json.storage ?? "";
bool disable = json.disable ?? false;
string memo = json.memo ?? "";
string result = _bridge.Items_Save(idx, sid, cate, name, model, scale, unit, price, supply, manu, storage, disable, memo);
var response = new { type = "ITEMS_SAVED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "ITEMS_DELETE":
{
int idx = json.idx ?? 0;
string result = _bridge.Items_Delete(idx);
var response = new { type = "ITEMS_DELETED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
// ===== UserList API =====
case "USERLIST_GET_CURRENT_LEVEL":
{
string result = _bridge.UserList_GetCurrentLevel();
var response = new { type = "USERLIST_CURRENT_LEVEL_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "USERLIST_GET_DEPTS":
{
string result = _bridge.UserList_GetDepts();
var response = new { type = "USERLIST_DEPTS_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "USERLIST_GET_LIST":
{
string process = json.process ?? "%";
string result = _bridge.UserList_GetList(process);
var response = new { type = "USERLIST_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "USERLIST_GET_USER":
{
string userId = json.userId ?? "";
string result = _bridge.UserList_GetUser(userId);
var response = new { type = "USERLIST_USER_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "USERLIST_SAVE_GROUP_USER":
{
string userId = json.userId ?? "";
string dept = json.dept ?? "";
int level = json.level ?? 1;
bool useUserState = json.useUserState ?? false;
bool useJobReport = json.useJobReport ?? false;
bool exceptHoly = json.exceptHoly ?? false;
string result = _bridge.UserList_SaveGroupUser(userId, dept, level, useUserState, useJobReport, exceptHoly);
var response = new { type = "USERLIST_SAVED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "USERLIST_SAVE_USER_FULL":
{
string userData = JsonConvert.SerializeObject(json.userData);
string result = _bridge.UserList_SaveUserFull(userData);
var response = new { type = "USERLIST_USER_FULL_SAVED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "USERLIST_DELETE_GROUP_USER":
{
string userId = json.userId ?? "";
string result = _bridge.UserList_DeleteGroupUser(userId);
var response = new { type = "USERLIST_DELETED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
// ===== JobReport API (JobReport 뷰/테이블) =====
case "JOBREPORT_GET_LIST":
{
string sd = json.sd ?? "";
string ed = json.ed ?? "";
string uid = json.uid ?? "";
string cate = json.cate ?? ""; // 사용안함 (호환성)
string searchKey = json.searchKey ?? "";
Console.WriteLine($"[WS] JOBREPORT_GET_LIST: sd={sd}, ed={ed}, uid={uid}, searchKey={searchKey}");
string result = _bridge.Jobreport_GetList(sd, ed, uid, cate, searchKey);
var response = new { type = "JOBREPORT_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "JOBREPORT_GET_USERS":
{
string result = _bridge.Jobreport_GetUsers();
var response = new { type = "JOBREPORT_USERS_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "JOBREPORT_GET_DETAIL":
{
int idx = json.idx ?? 0;
string result = _bridge.Jobreport_GetDetail(idx);
var response = new { type = "JOBREPORT_DETAIL_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "JOBREPORT_ADD":
{
string pdate = json.pdate ?? "";
string projectName = json.projectName ?? "";
string requestpart = json.requestpart ?? "";
string package = json.package ?? "";
string type1 = json.type ?? "";
string process = json.process ?? "";
string status = json.status ?? "진행 완료";
string description = json.description ?? "";
double hrs = json.hrs ?? 0.0;
double ot = json.ot ?? 0.0;
string jobgrp = json.jobgrp ?? "";
string tag = json.tag ?? "";
string result = _bridge.Jobreport_Add(pdate, projectName, requestpart, package, type1, process, status, description, hrs, ot, jobgrp, tag);
var response = new { type = "JOBREPORT_ADDED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "JOBREPORT_EDIT":
{
int idx = json.idx ?? 0;
string pdate = json.pdate ?? "";
string projectName = json.projectName ?? "";
string requestpart = json.requestpart ?? "";
string package = json.package ?? "";
string type2 = json.type ?? "";
string process = json.process ?? "";
string status = json.status ?? "";
string description = json.description ?? "";
double hrs = json.hrs ?? 0.0;
double ot = json.ot ?? 0.0;
string jobgrp = json.jobgrp ?? "";
string tag = json.tag ?? "";
string result = _bridge.Jobreport_Edit(idx, pdate, projectName, requestpart, package, type2, process, status, description, hrs, ot, jobgrp, tag);
var response = new { type = "JOBREPORT_EDITED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "JOBREPORT_DELETE":
{
int idx = json.idx ?? 0;
string result = _bridge.Jobreport_Delete(idx);
var response = new { type = "JOBREPORT_DELETED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "JOBREPORT_GET_PERMISSION":
{
string targetUserId = json.targetUserId ?? "";
string result = _bridge.Jobreport_GetPermission(targetUserId);
var response = new { type = "JOBREPORT_PERMISSION", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "JOBREPORT_GET_JOBTYPES":
{
string process = json.process ?? "";
string result = _bridge.Jobreport_GetJobTypes(process);
var response = new { type = "JOBREPORT_JOBTYPES", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "GET_APP_VERSION":
{
string result = _bridge.GetAppVersion();
var response = new { type = "APP_VERSION", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
default:
Console.WriteLine($"[WS] Unknown message type: {type}");
break;
}
}
catch (Exception ex)
{
Console.WriteLine($"[WS] Handle Error: {ex.Message}");
}
}
private async Task Send(WebSocket socket, string message)
{
if (_socketLocks.TryGetValue(socket, out var semaphore))
{
await semaphore.WaitAsync();
try
{
if (socket.State == WebSocketState.Open)
{
byte[] buffer = Encoding.UTF8.GetBytes(message);
await socket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
finally
{
semaphore.Release();
}
}
}
public async void Broadcast(string message)
{
byte[] buffer = Encoding.UTF8.GetBytes(message);
WebSocket[] clientsCopy;
lock (_clients)
{
clientsCopy = _clients.ToArray();
}
foreach (var client in clientsCopy)
{
if (client.State == WebSocketState.Open && _socketLocks.TryGetValue(client, out var semaphore))
{
_ = Task.Run(async () =>
{
if (await semaphore.WaitAsync(0))
{
try
{
if (client.State == WebSocketState.Open)
{
await client.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
catch { }
finally
{
semaphore.Release();
}
}
});
}
}
}
}
}