using Microsoft.AspNetCore.Http; using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Application.Features.Config.Update; public sealed class Request { public int ID { get; init; } public BasicConfigDto Basic { get; init; } = new(); public ImagesConfigDto Images { get; init; } = new(); public MetaConfigDto Meta { get; init; } = new(); public CompanyConfigDto Company { get; init; } = new(); public AccountConfigDto Account { get; init; } = new(); public EmailTemplateConfigDto EmailTemplate { get; init; } = new(); public ExternalApiConfigDto External { get; init; } = new(); public TossConfigDto Toss { get; init; } = new(); public AttendanceConfigDto? Attendance { get; init; } public SignupRewardConfigDto? SignupReward { get; init; } public RewardConfigDto? Reward { get; init; } public PaperConfigDto? Paper { get; init; } public DataCollectionConfigDto? DataCollection { get; init; } // 기본 설정 public sealed class BasicConfigDto { [MaxLength(100)] [DisplayName("사이트 이름")] public string? SiteName { get; init; } [MaxLength(100)] [DataType(DataType.Url)] [DisplayName("사이트 주소")] public string? SiteURL { get; init; } [DisplayName("최고 관리자 ID")] public string? RootID { get; init; } [MaxLength(100)] [DataType(DataType.EmailAddress)] [DisplayName("발송자 이메일")] public string? FromEmail { get; init; } [MaxLength(30)] [DisplayName("발송자 이름")] public string? FromName { get; init; } [MaxLength(200)] [DisplayName("SMTP Server")] public string? SmtpServer { get; init; } [Range(1, 65535)] [DisplayName("SMTP Port")] public int? SmtpPort { get; set; } [DisplayName("SMTP Enable SSL")] public bool SmtpEnableSSL { get; init; } = false; [MaxLength(100)] [DisplayName("SMTP Username")] public string? SmtpUsername { get; init; } [MaxLength(200)] [DataType(DataType.Password)] [DisplayName("SMTP Password")] public string? SmtpPassword { get; init; } [MaxLength(200)] [DataType(DataType.MultilineText)] [DisplayName("관리자단 접근 가능 IP")] public string? AdminWhiteIPList { get; init; } [MaxLength(200)] [DataType(DataType.MultilineText)] [DisplayName("사용자단 접근 가능 IP")] public string? FrontWhiteIPList { get; init; } [MaxLength(200)] [DisplayName("차단 시 안내문 제목")] public string? BlockAlertTitle { get; init; } [MaxLength(5000)] [DataType(DataType.MultilineText)] [DisplayName("차단 시 안내문 내용")] public string? BlockAlertContent { get; init; } [DisplayName("점검 여부")] public bool IsMaintenance { get; init; } = false; [MaxLength(5000)] [DataType(DataType.MultilineText)] [DisplayName("점검 내용")] public string? MaintenanceContent { get; init; } [Range(0, 100)] [DisplayName("쪽지 일일 발송 제한")] public int NoteDailySendLimit { get; init; } = 3; } // 이미지 경로 및 업로드 입력 public sealed class ImagesConfigDto { // ====== DB에 저장/표기할 경로(문자열) ====== [MaxLength(255)] [DisplayName("Favicon")] public string? FaviconPath { get; init; } [MaxLength(255)] [DisplayName("Logo-square")] public string? LogoSquarePath { get; init; } [MaxLength(255)] [DisplayName("Logo-horizontal")] public string? LogoHorizontalPath { get; init; } [MaxLength(255)] [DisplayName("og-default")] public string? OgDefaultPath { get; init; } [MaxLength(255)] [DisplayName("Twitter-image")] public string? TwitterImagePath { get; init; } [MaxLength(255)] [DisplayName("Apple-touch-icon")] public string? AppleTouchIconPath { get; init; } [MaxLength(255)] [DisplayName("App-icon-192")] public string? AppIcon192Path { get; init; } [MaxLength(255)] [DisplayName("App-icon-512")] public string? AppIcon512Path { get; init; } // ====== 업로드 입력(새 업로드용) ====== [DataType(DataType.Upload)] [DisplayName("Favicon 업로드")] public IFormFile? FaviconFile { get; init; } [DataType(DataType.Upload)] [DisplayName("Logo-square 업로드")] public IFormFile? LogoSquareFile { get; init; } [DataType(DataType.Upload)] [DisplayName("Logo-horizontal 업로드")] public IFormFile? LogoHorizontalFile { get; init; } [DataType(DataType.Upload)] [DisplayName("og-default 업로드")] public IFormFile? OgDefaultFile { get; init; } [DataType(DataType.Upload)] [DisplayName("Twitter-image 업로드")] public IFormFile? TwitterImageFile { get; init; } [DataType(DataType.Upload)] [DisplayName("Apple-touch-icon 업로드")] public IFormFile? AppleTouchIconFile { get; init; } [DataType(DataType.Upload)] [DisplayName("App-icon-192 업로드")] public IFormFile? AppIcon192File { get; init; } [DataType(DataType.Upload)] [DisplayName("App-icon-512 업로드")] public IFormFile? AppIcon512File { get; init; } } // 메타 태그 public sealed class MetaConfigDto { [MaxLength(255)] [DisplayName("Meta Keywords")] public string? Keywords { get; init; } [MaxLength(255)] [DisplayName("Meta Description")] public string? Description { get; init; } [MaxLength(255)] [DisplayName("Meta Author")] public string? Author { get; init; } [MaxLength(255)] [DisplayName("Meta Viewport")] public string? Viewport { get; init; } [MaxLength(255)] [DisplayName("Meta ApplicationName")] public string? ApplicationName { get; init; } [MaxLength(255)] [DisplayName("Meta Generator")] public string? Generator { get; init; } [MaxLength(255)] [DisplayName("Meta Robots")] public string? Robots { get; init; } [MaxLength(3000)] [DataType(DataType.MultilineText)] [DisplayName("Meta Adds")] public string? Adds { get; init; } } // 회사 정보 public sealed class CompanyConfigDto { [MaxLength(70)] [DisplayName("상호 명")] public string? Name { get; init; } [MaxLength(100)] [DisplayName("사업자 등록 번호")] public string? RegNo { get; init; } [MaxLength(255)] [DisplayName("사업장 소재지")] public string? Address { get; init; } [MaxLength(8)] [DataType(DataType.PostalCode)] [DisplayName("우편번호")] public string? ZipCode { get; init; } [MaxLength(50)] [DisplayName("대표자 명")] public string? Owner { get; init; } [MaxLength(20)] [DisplayName("대표 전화번호")] public string? Tel { get; init; } [MaxLength(20)] [DisplayName("FAX")] public string? Fax { get; init; } [MaxLength(20)] [DisplayName("통신판매업 신고번호")] public string? RetailSaleNo { get; init; } [MaxLength(20)] [DisplayName("부가통신 사업자번호")] public string? AddedSaleNo { get; init; } [MaxLength(100)] [DisplayName("호스팅 서비스")] public string? Hosting { get; init; } [MaxLength(70)] [DisplayName("정보관리책임자")] public string? AdminName { get; init; } [MaxLength(100)] [DataType(DataType.EmailAddress)] [DisplayName("정보관리책임자 이메일")] public string? AdminEmail { get; init; } [MaxLength(200)] [DataType(DataType.Url)] [DisplayName("사이트 주소")] public string? SiteUrl { get; init; } [MaxLength(10)] [DisplayName("입금계좌 - 은행")] public string? BankCode { get; init; } [MaxLength(70)] [DisplayName("입금계좌 - 예금주")] public string? BankOwner { get; init; } [MaxLength(100)] [DisplayName("입금계좌 - 계좌번호")] public string? BankNumber { get; init; } } // 회원 설정 public sealed class AccountConfigDto { [DisplayName("회원가입 시 - 가입 차단")] public bool IsRegisterBlock { get; init; } = false; [DisplayName("회원가입 시 - 이메일 인증 여부")] public bool IsRegisterEmailAuth { get; init; } = false; [Range(0, 100)] [DisplayName("회원가입 시 - 비밀번호 최소 길이")] public ushort? PasswordMinLength { get; init; } [Range(0, 100)] [DisplayName("회원가입 시 - 비밀번호 대문자 최소 길이")] public ushort? PasswordUppercaseLength { get; init; } [Range(0, 100)] [DisplayName("회원가입 시 - 비밀번호 숫자 최소 길이")] public ushort? PasswordNumbersLength { get; init; } [Range(0, 100)] [DisplayName("회원가입 시 - 비밀번호 특수문자 최소 길이")] public ushort? PasswordSpecialcharsLength { get; init; } [MaxLength(4000)] [DisplayName("회원가입 시 - 금지 이메일")] public string? DeniedEmailList { get; init; } [MaxLength(4000)] [DisplayName("회원가입 시 - 금지 별명")] public string? DeniedNameList { get; init; } [Range(0, 365)] [DisplayName("회원가입 시 - 이메일 갱신 주기")] public ushort? ChangeEmailDay { get; init; } [Range(0, 365)] [DisplayName("회원가입 시 - 별명 갱신 주기")] public ushort? ChangeNameDay { get; init; } [Range(0, 365)] [DisplayName("회원가입 시 - 한마디 갱신 주기")] public ushort? ChangeSummaryDay { get; init; } [Range(0, 365)] [DisplayName("회원가입 시 - 자기소개 갱신 주기")] public ushort? ChangeIntroDay { get; init; } [Range(0, 365)] [DisplayName("회원가입 시 - 비밀번호 갱신 주기")] public ushort? ChangePasswordDay { get; init; } [DisplayName("로그인 시 - 이메일 인증 필요")] public bool IsLoginEmailVerifiedOnly { get; init; } = false; [Range(0, 100)] [DisplayName("로그인 시 - 로그인 시도(회)")] public ushort? MaxLoginTryCount { get; init; } [Range(0, 86400)] [DisplayName("로그인 시 - 로그인 제한(초)")] public ushort? MaxLoginTryLimitSecond { get; init; } } // 알림 발송 양식 public sealed class EmailTemplateConfigDto { [MaxLength(4000)] [DisplayName("회원가입 시 - 제목")] public string? RegisterEmailFormTitle { get; init; } [MaxLength(4000)] [DisplayName("회원가입 시 - 내용")] public string? RegisterEmailFormContent { get; init; } [MaxLength(4000)] [DisplayName("회원가입 완료 - 제목")] public string? RegistrationEmailFormTitle { get; init; } [MaxLength(4000)] [DisplayName("회원가입 완료 - 내용")] public string? RegistrationEmailFormContent { get; init; } [MaxLength(4000)] [DisplayName("비밀번호 재설정 - 제목")] public string? ResetPasswordEmailFormTitle { get; init; } [MaxLength(4000)] [DisplayName("비밀번호 재설정 - 내용")] public string? ResetPasswordEmailFormContent { get; init; } [MaxLength(4000)] [DisplayName("비밀번호 변경 완료 - 제목")] public string? ChangedPasswordEmailFormTitle { get; init; } [MaxLength(4000)] [DisplayName("비밀번호 변경 완료 - 내용")] public string? ChangedPasswordEmailFormContent { get; init; } [MaxLength(4000)] [DisplayName("회원탈퇴 시 - 제목")] public string? WithdrawEmailFormTitle { get; init; } [MaxLength(4000)] [DisplayName("회원탈퇴 시 - 내용")] public string? WithdrawEmailFormContent { get; init; } [MaxLength(4000)] [DisplayName("회원탈퇴 인증 - 제목")] public string? WithdrawVerifyEmailFormTitle { get; init; } [MaxLength(4000)] [DisplayName("회원탈퇴 인증 - 내용")] public string? WithdrawVerifyEmailFormContent { get; init; } [MaxLength(4000)] [DisplayName("이메일 변경 시 - 제목")] public string? EmailVerifyFormTitle { get; init; } [MaxLength(4000)] [DisplayName("이메일 변경 시 - 내용")] public string? EmailVerifyFormContent { get; init; } [MaxLength(4000)] [DisplayName("이메일 변경 완료 - 제목")] public string? ChangedEmailFormTitle { get; init; } [MaxLength(4000)] [DisplayName("이메일 변경 완료 - 내용")] public string? ChangedEmailFormContent { get; init; } } // API 정보 public sealed class ExternalApiConfigDto { [MaxLength(500)] [DisplayName("YouTube - API Name")] public string? YouTubeApiKeyEnc { get; init; } [MaxLength(500)] [DisplayName("YouTube - API Key")] public string? YouTubeApiName { get; init; } [MaxLength(500)] [DisplayName("Google - Client ID")] public string? GoogleClientId { get; init; } [MaxLength(500)] [DisplayName("Google - Client Secret")] public string? GoogleClientSecretEnc { get; init; } [MaxLength(500)] [DisplayName("Google - App ID")] public string? GoogleAppId { get; init; } // 소셜 로그인 (OAuth) — 비밀 키는 빈 값 제출 시 기존 유지(입력 시 암호화 저장), ID/JS 키는 직접 저장 [MaxLength(500)] [DisplayName("네이버 - Client ID")] public string? NaverClientId { get; init; } [MaxLength(500)] [DisplayName("네이버 - Client Secret")] public string? NaverClientSecret { get; init; } [MaxLength(500)] [DisplayName("카카오 - REST API 키")] public string? KakaoRestApiKey { get; init; } [MaxLength(500)] [DisplayName("카카오 - JavaScript 키")] public string? KakaoJavascriptKey { get; init; } [MaxLength(500)] [DisplayName("카카오 - Admin 키")] public string? KakaoAdminKey { get; init; } [MaxLength(500)] [DisplayName("네이버검색 - Client ID")] public string? NaverSearchClientId { get; init; } [MaxLength(500)] [DisplayName("네이버검색 - Client Secret")] public string? NaverSearchClientSecret { get; init; } } // 데이터 수집 API 키 + 수집기 활성화. 키는 빈 값 제출 시 기존 유지(입력 시 암호화 저장), 플래그는 수집기 on/off. public sealed class DataCollectionConfigDto { [MaxLength(1000)] [DisplayName("금융위 data.go.kr ServiceKey")] public string? DataGoKrServiceKey { get; init; } [MaxLength(1000)] [DisplayName("KRX AUTH_KEY")] public string? KrxApiKey { get; init; } [MaxLength(1000)] [DisplayName("OpenDART crtfc_key")] public string? OpenDartApiKey { get; init; } [MaxLength(1000)] [DisplayName("수출입은행 환율(AP01) authkey")] public string? KoreaEximExchangeKey { get; init; } [MaxLength(1000)] [DisplayName("수출입은행 대출금리(AP02) authkey")] public string? KoreaEximLoanRateKey { get; init; } [MaxLength(1000)] [DisplayName("수출입은행 국제금리(AP03) authkey")] public string? KoreaEximIntlRateKey { get; init; } [MaxLength(1000)] [DisplayName("SEIBro key")] public string? SeibroApiKey { get; init; } [MaxLength(1000)] [DisplayName("KOSIS apiKey")] public string? KosisApiKey { get; init; } // 수집기 활성화 플래그 public bool StockDataMasterSync { get; init; } public bool StockDataDailyPriceSync { get; init; } public bool KrxIndexSync { get; init; } public bool KrxBondIndexSync { get; init; } public bool KrxStockSync { get; init; } public bool KrxEtpSync { get; init; } public bool KrxWarrantSync { get; init; } public bool KrxBondSync { get; init; } public bool KrxDerivativeSync { get; init; } public bool KrxCommoditySync { get; init; } public bool KrxEsgSync { get; init; } public bool OpenDartDisclosureSync { get; init; } public bool KoreaEximMacroSync { get; init; } public bool KosisSync { get; init; } public bool WorldIndexEnabled { get; init; } public bool MarketQuoteEnabled { get; init; } public bool SeibroIssuerSync { get; init; } public bool SeibroDividendSync { get; init; } public bool SeibroSupplySync { get; init; } public bool SeibroCorpActionSync { get; init; } public bool SeibroBondSync { get; init; } public bool SeibroDerivSync { get; init; } public bool SeibroForeignSync { get; init; } } // 토스페이먼츠 결제 설정 (결제 설정 화면 전용) // 키 입력값은 저장 시 암호화되며, 빈 값으로 제출하면 기존 저장 키를 유지한다. public sealed class TossConfigDto { [DisplayName("토스 - 결제 환경")] public string? TossPayMode { get; init; } [MaxLength(500)] [DisplayName("토스 - Test Client Key")] public string? TestClientKey { get; init; } [MaxLength(500)] [DisplayName("토스 - Test Secret Key")] public string? TestSecretKey { get; init; } [MaxLength(500)] [DisplayName("토스 - Live Client Key")] public string? LiveClientKey { get; init; } [MaxLength(500)] [DisplayName("토스 - Live Secret Key")] public string? LiveSecretKey { get; init; } } // 출석 설정 public sealed class AttendanceConfigDto { [DisplayName("출석 기능 활성화")] public bool IsEnabled { get; init; } = false; [Range(0, 100000)] [DisplayName("기본 경험치")] public int BaseExp { get; init; } = 0; [Range(0, 100000)] [DisplayName("기본 코인")] public int BasePoint { get; init; } = 0; [DisplayName("연속 출석 가중치 사용")] public bool UseStreakBonus { get; init; } = false; [Range(0, 10000)] [DisplayName("1일당 추가 경험치")] public int StreakBonusPerDay { get; init; } = 0; [Range(0, 10000)] [DisplayName("1일당 추가 코인")] public int StreakBonusPointPerDay { get; init; } = 0; [Range(0, 365)] [DisplayName("가중치 최대 적용 일수")] public int StreakBonusMaxDays { get; init; } = 0; [DisplayName("순위 보상 사용")] public bool UseRankBonus { get; init; } = false; [MaxLength(2000)] [DisplayName("순위별 보상 설정 (JSON)")] public string? RankBonusConfig { get; init; } } // 가입 축하 보상 설정 public sealed class SignupRewardConfigDto { [DisplayName("가입 축하 보상 활성화")] public bool Enabled { get; init; } = false; [Range(0, 100000000)] [DisplayName("지급 코인")] public int CoinAmount { get; init; } = 0; [Range(0, 100000000)] [DisplayName("지급 캐시")] public int CashAmount { get; init; } = 0; [Range(0, 100000)] [DisplayName("지급 경험치")] public int ExpAmount { get; init; } = 0; } // 보상 엔진 설정 (Reward) — d3 M2. ActivityToken 대체·통합. public sealed class RewardConfigDto { [Range(0, 100000000)] [DisplayName("게시글 경험치")] public int PostExp { get; init; } = 0; [Range(0, 100000000)] [DisplayName("게시글 토큰")] public int PostPoint { get; init; } = 0; [Range(0, 100000000)] [DisplayName("댓글 경험치")] public int CommentExp { get; init; } = 0; [Range(0, 100000000)] [DisplayName("댓글 토큰")] public int CommentPoint { get; init; } = 0; [Range(0, 100000000)] [DisplayName("추천(누른 쪽) 경험치")] public int LikeGivenExp { get; init; } = 0; [Range(0, 100000000)] [DisplayName("추천(받은 쪽) 토큰")] public int LikeReceivedPoint { get; init; } = 0; [Range(0, 100000)] [DisplayName("최소 게시글 길이")] public int MinPostLength { get; init; } = 0; [Range(0, 100000)] [DisplayName("최소 댓글 길이")] public int MinCommentLength { get; init; } = 0; [Range(0, 3650)] [DisplayName("신규 회원 보상 유예(일)")] public int NewMemberHoldDays { get; init; } = 0; [Range(0, 100000000)] [DisplayName("게시글 일일 횟수 상한")] public int PostDailyCount { get; init; } = 0; [Range(0, 100000000)] [DisplayName("게시글 일일 경험치 상한")] public int PostDailyExp { get; init; } = 0; [Range(0, 100000000)] [DisplayName("게시글 일일 토큰 상한")] public int PostDailyPoint { get; init; } = 0; [Range(0, 100000000)] [DisplayName("댓글 일일 횟수 상한")] public int CommentDailyCount { get; init; } = 0; [Range(0, 100000000)] [DisplayName("댓글 일일 경험치 상한")] public int CommentDailyExp { get; init; } = 0; [Range(0, 100000000)] [DisplayName("댓글 일일 토큰 상한")] public int CommentDailyPoint { get; init; } = 0; [Range(0, 100000000)] [DisplayName("추천(누른 쪽) 일일 횟수 상한")] public int LikeGivenDailyCount { get; init; } = 0; [Range(0, 100000000)] [DisplayName("추천(누른 쪽) 일일 경험치 상한")] public int LikeGivenDailyExp { get; init; } = 0; [Range(0, 100000000)] [DisplayName("추천(받은 쪽) 일일 횟수 상한")] public int LikeReceivedDailyCount { get; init; } = 0; [Range(0, 100000000)] [DisplayName("추천(받은 쪽) 일일 토큰 상한")] public int LikeReceivedDailyPoint { get; init; } = 0; [Range(0, 100000000)] [DisplayName("채팅 일일 횟수 상한")] public int ChatDailyCount { get; init; } = 0; [Range(0, 100000000)] [DisplayName("채팅 일일 경험치 상한")] public int ChatDailyExp { get; init; } = 0; [Range(0, 100)] [DisplayName("응원 수수료율(%)")] public int CheerFeePercent { get; init; } = 20; [Range(0, 100000000)] [DisplayName("응원 최소 금액")] public int CheerMinAmount { get; init; } = 100; [Range(0, 2000000000)] [DisplayName("응원 일일 발신 총액 상한(0=무제한)")] public int CheerDailyMax { get; init; } = 0; } // 모의투자 설정 (Paper) — d4 §④ public sealed class PaperConfigDto { [DisplayName("모의투자 활성화")] public bool Enabled { get; init; } = true; [Range(0, 100000)] [DisplayName("매매 수수료(Bp)")] public int FeeRateBp { get; init; } = 15; [Range(0, 100000)] [DisplayName("거래세(Bp)")] public int TaxRateBp { get; init; } = 18; [Range(0, 1000000000)] [DisplayName("최소 입금 토큰")] public int MinDeposit { get; init; } = 10000; [Range(0, 2000000000)] [DisplayName("최대 보유 토큰(0=무제한)")] public int MaxHolding { get; init; } = 0; [Range(0, 10000)] [DisplayName("출금 수익 소각(Bp, 0=사용안함)")] public int WithdrawProfitBurnBp { get; init; } = 0; [Range(0, 10000)] [DisplayName("1주문 상한(Bp, 3000=30%)")] public int OrderMaxPctBp { get; init; } = 3000; [Range(0, 100000)] [DisplayName("리더보드 등재 최소 체결수")] public int MinFillsForRank { get; init; } = 3; } public static Request From(Get.Response src) => new() { ID = src.ID, Basic = new BasicConfigDto { SiteName = src.Basic.SiteName, SiteURL = src.Basic.SiteURL, RootID = src.Basic.RootID, FromEmail = src.Basic.FromEmail, FromName = src.Basic.FromName, SmtpServer = src.Basic.SmtpServer, SmtpPort = src.Basic.SmtpPort, SmtpEnableSSL = src.Basic.SmtpEnableSSL, SmtpUsername = src.Basic.SmtpUsername, SmtpPassword = src.Basic.SmtpPassword, AdminWhiteIPList = src.Basic.AdminWhiteIPList, FrontWhiteIPList = src.Basic.FrontWhiteIPList, BlockAlertTitle = src.Basic.BlockAlertTitle, BlockAlertContent = src.Basic.BlockAlertContent, IsMaintenance = src.Basic.IsMaintenance, MaintenanceContent = src.Basic.MaintenanceContent, NoteDailySendLimit = src.Basic.NoteDailySendLimit }, Images = new ImagesConfigDto { FaviconPath = src.Images.FaviconPath, LogoSquarePath = src.Images.LogoSquarePath, LogoHorizontalPath = src.Images.LogoHorizontalPath, OgDefaultPath = src.Images.OgDefaultPath, TwitterImagePath = src.Images.TwitterImagePath, AppleTouchIconPath = src.Images.AppleTouchIconPath, AppIcon192Path = src.Images.AppIcon192Path, AppIcon512Path = src.Images.AppIcon512Path }, Meta = new MetaConfigDto { Keywords = src.Meta.Keywords, Description = src.Meta.Description, Author = src.Meta.Author, Viewport = src.Meta.Viewport, ApplicationName = src.Meta.ApplicationName, Generator = src.Meta.Generator, Robots = src.Meta.Robots, Adds = src.Meta.Adds }, Company = new CompanyConfigDto { Name = src.Company.Name, RegNo = src.Company.RegNo, Address = src.Company.Address, ZipCode = src.Company.ZipCode, Owner = src.Company.Owner, Tel = src.Company.Tel, Fax = src.Company.Fax, RetailSaleNo = src.Company.RetailSaleNo, AddedSaleNo = src.Company.AddedSaleNo, Hosting = src.Company.Hosting, AdminName = src.Company.AdminName, AdminEmail = src.Company.AdminEmail, SiteUrl = src.Company.SiteUrl, BankCode = src.Company.BankCode, BankOwner = src.Company.BankOwner, BankNumber = src.Company.BankNumber }, Account = new AccountConfigDto { IsRegisterBlock = src.Account.IsRegisterBlock, IsRegisterEmailAuth = src.Account.IsRegisterEmailAuth, PasswordMinLength = src.Account.PasswordMinLength, PasswordUppercaseLength = src.Account.PasswordUppercaseLength, PasswordNumbersLength = src.Account.PasswordNumbersLength, PasswordSpecialcharsLength = src.Account.PasswordSpecialcharsLength, DeniedEmailList = src.Account.DeniedEmailList, DeniedNameList = src.Account.DeniedNameList, ChangeEmailDay = src.Account.ChangeEmailDay, ChangeNameDay = src.Account.ChangeNameDay, ChangeSummaryDay = src.Account.ChangeSummaryDay, ChangeIntroDay = src.Account.ChangeIntroDay, ChangePasswordDay = src.Account.ChangePasswordDay, IsLoginEmailVerifiedOnly = src.Account.IsLoginEmailVerifiedOnly, MaxLoginTryCount = src.Account.MaxLoginTryCount, MaxLoginTryLimitSecond = src.Account.MaxLoginTryLimitSecond }, EmailTemplate = new EmailTemplateConfigDto { RegisterEmailFormTitle = src.EmailTemplate.RegisterEmailFormTitle, RegisterEmailFormContent = src.EmailTemplate.RegisterEmailFormContent, RegistrationEmailFormTitle = src.EmailTemplate.RegistrationEmailFormTitle, RegistrationEmailFormContent = src.EmailTemplate.RegistrationEmailFormContent, ResetPasswordEmailFormTitle = src.EmailTemplate.ResetPasswordEmailFormTitle, ResetPasswordEmailFormContent = src.EmailTemplate.ResetPasswordEmailFormContent, ChangedPasswordEmailFormTitle = src.EmailTemplate.ChangedPasswordEmailFormTitle, ChangedPasswordEmailFormContent = src.EmailTemplate.ChangedPasswordEmailFormContent, WithdrawEmailFormTitle = src.EmailTemplate.WithdrawEmailFormTitle, WithdrawEmailFormContent = src.EmailTemplate.WithdrawEmailFormContent, WithdrawVerifyEmailFormTitle = src.EmailTemplate.WithdrawVerifyEmailFormTitle, WithdrawVerifyEmailFormContent = src.EmailTemplate.WithdrawVerifyEmailFormContent, EmailVerifyFormTitle = src.EmailTemplate.EmailVerifyFormTitle, EmailVerifyFormContent = src.EmailTemplate.EmailVerifyFormContent, ChangedEmailFormTitle = src.EmailTemplate.ChangedEmailFormTitle, ChangedEmailFormContent = src.EmailTemplate.ChangedEmailFormContent }, External = new ExternalApiConfigDto { YouTubeApiKeyEnc = src.External.YouTubeApiKeyEnc, YouTubeApiName = src.External.YouTubeApiName, GoogleClientId = src.External.GoogleClientId, GoogleClientSecretEnc = src.External.GoogleClientSecretEnc, GoogleAppId = src.External.GoogleAppId, NaverClientId = src.External.NaverClientId, KakaoJavascriptKey = src.External.KakaoJavascriptKey, NaverSearchClientId = src.External.NaverSearchClientId // 비밀 키(Secret)는 화면에 되돌리지 않음 — 빈 값 = 기존 유지 }, // 토스 키는 화면에 되돌리지 않는다 (암호문 재제출로 인한 이중 암호화 방지 — 빈 값 = 기존 유지) Toss = new TossConfigDto { TossPayMode = src.Toss.TossPayMode }, Attendance = new AttendanceConfigDto { IsEnabled = src.Attendance.IsEnabled, BaseExp = src.Attendance.BaseExp, BasePoint = src.Attendance.BasePoint, UseStreakBonus = src.Attendance.UseStreakBonus, StreakBonusPerDay = src.Attendance.StreakBonusPerDay, StreakBonusPointPerDay = src.Attendance.StreakBonusPointPerDay, StreakBonusMaxDays = src.Attendance.StreakBonusMaxDays, UseRankBonus = src.Attendance.UseRankBonus, RankBonusConfig = src.Attendance.RankBonusConfig }, SignupReward = new SignupRewardConfigDto { Enabled = src.SignupReward.Enabled, CoinAmount = src.SignupReward.CoinAmount, CashAmount = src.SignupReward.CashAmount, ExpAmount = src.SignupReward.ExpAmount }, Reward = new RewardConfigDto { PostExp = src.Reward.PostExp, PostPoint = src.Reward.PostPoint, CommentExp = src.Reward.CommentExp, CommentPoint = src.Reward.CommentPoint, LikeGivenExp = src.Reward.LikeGivenExp, LikeReceivedPoint = src.Reward.LikeReceivedPoint, MinPostLength = src.Reward.MinPostLength, MinCommentLength = src.Reward.MinCommentLength, NewMemberHoldDays = src.Reward.NewMemberHoldDays, PostDailyCount = src.Reward.PostDailyCount, PostDailyExp = src.Reward.PostDailyExp, PostDailyPoint = src.Reward.PostDailyPoint, CommentDailyCount = src.Reward.CommentDailyCount, CommentDailyExp = src.Reward.CommentDailyExp, CommentDailyPoint = src.Reward.CommentDailyPoint, LikeGivenDailyCount = src.Reward.LikeGivenDailyCount, LikeGivenDailyExp = src.Reward.LikeGivenDailyExp, LikeReceivedDailyCount = src.Reward.LikeReceivedDailyCount, LikeReceivedDailyPoint = src.Reward.LikeReceivedDailyPoint, ChatDailyCount = src.Reward.ChatDailyCount, ChatDailyExp = src.Reward.ChatDailyExp, CheerFeePercent = src.Reward.CheerFeePercent, CheerMinAmount = src.Reward.CheerMinAmount, CheerDailyMax = src.Reward.CheerDailyMax }, Paper = new PaperConfigDto { Enabled = src.Paper.Enabled, FeeRateBp = src.Paper.FeeRateBp, TaxRateBp = src.Paper.TaxRateBp, MinDeposit = src.Paper.MinDeposit, MaxHolding = src.Paper.MaxHolding, WithdrawProfitBurnBp = src.Paper.WithdrawProfitBurnBp, OrderMaxPctBp = src.Paper.OrderMaxPctBp, MinFillsForRank = src.Paper.MinFillsForRank }, // 키는 화면에 되돌리지 않음(빈 값=유지) — 플래그만 현재값 반영 DataCollection = new DataCollectionConfigDto { StockDataMasterSync = src.DataCollection.StockDataMasterSync, StockDataDailyPriceSync = src.DataCollection.StockDataDailyPriceSync, KrxIndexSync = src.DataCollection.KrxIndexSync, KrxBondIndexSync = src.DataCollection.KrxBondIndexSync, KrxStockSync = src.DataCollection.KrxStockSync, KrxEtpSync = src.DataCollection.KrxEtpSync, KrxWarrantSync = src.DataCollection.KrxWarrantSync, KrxBondSync = src.DataCollection.KrxBondSync, KrxDerivativeSync = src.DataCollection.KrxDerivativeSync, KrxCommoditySync = src.DataCollection.KrxCommoditySync, KrxEsgSync = src.DataCollection.KrxEsgSync, OpenDartDisclosureSync = src.DataCollection.OpenDartDisclosureSync, KoreaEximMacroSync = src.DataCollection.KoreaEximMacroSync, KosisSync = src.DataCollection.KosisSync, WorldIndexEnabled = src.DataCollection.WorldIndexEnabled, MarketQuoteEnabled = src.DataCollection.MarketQuoteEnabled, SeibroIssuerSync = src.DataCollection.SeibroIssuerSync, SeibroDividendSync = src.DataCollection.SeibroDividendSync, SeibroSupplySync = src.DataCollection.SeibroSupplySync, SeibroCorpActionSync = src.DataCollection.SeibroCorpActionSync, SeibroBondSync = src.DataCollection.SeibroBondSync, SeibroDerivSync = src.DataCollection.SeibroDerivSync, SeibroForeignSync = src.DataCollection.SeibroForeignSync } }; }