package kr.itn.itnhub.system; import kr.itn.itnhub.mattermost.MattermostClient; import kr.itn.itnhub.org.Organization; import kr.itn.itnhub.org.OrganizationMapper; import kr.itn.itnhub.stage.StageHistoryMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; /** * 시스템관리 > 채널 초기화. 웹앱이 만든 Mattermost 채널을 보관 처리하고, 기관의 채널 상태를 * 비워 채널관리에서 처음부터 다시 만들 수 있게 되돌린다. * *

대상은 웹앱 DB가 아는 채널뿐이다. organization.channel_id_mj / channel_id_law에 * 들어 있는 ID만 건드린다. Mattermost 팀에는 엑셀 VBA(M07)로 만든 예전 채널과 공용 채널 * (Town Square, FAQ, 대쉬보드, 양식 모음, 자료실)이 함께 있는데, 이름으로 훑어서 지우면 * 그것들까지 날아간다. 그래서 이름 검색 경로는 의도적으로 만들지 않았다.

* *

사용자 계정은 건드리지 않는다. 웹앱은 만든 계정을 기록하지 않고 이메일로 조회해 * 없을 때만 만드는 구조라, 원래 있던 실제 직원 계정과 구분할 방법이 없다. 계정까지 정리하려면 * 생성 출처를 DB에 남기는 작업이 먼저다.

* *

보관은 Mattermost의 아카이브다(영구 삭제 아님). 게시글·파일은 남고 시스템 콘솔에서 * 복구할 수 있다. 권리확인/권리처리·업무메모·담당자 배정 등 DB 데이터도 그대로 둔다.

*/ @Service public class SystemResetService { private static final Logger log = LoggerFactory.getLogger(SystemResetService.class); static final String KIND_MJ = "문정원"; static final String KIND_LAW = "법률검토"; private final OrganizationMapper orgMapper; private final StageHistoryMapper stageHistoryMapper; private final MattermostClient mattermost; public SystemResetService(OrganizationMapper orgMapper, StageHistoryMapper stageHistoryMapper, MattermostClient mattermost) { this.orgMapper = orgMapper; this.stageHistoryMapper = stageHistoryMapper; this.mattermost = mattermost; } /** 초기화하면 무엇이 사라지는지 미리 보여준다. 아무것도 바꾸지 않는다. */ public List preview() { List targets = new ArrayList<>(); for (Organization org : orgMapper.findAll()) { List channels = channelsOf(org); if (channels.isEmpty()) { continue; } targets.add(new ResetTarget(org.getId(), org.getOrgNo(), org.getOrgName(), org.getStage(), channels)); } return targets; } /** * 실제 초기화. 채널 하나가 실패해도 나머지는 계속한다 - 이미 지워진 채널이나 권한 문제로 * 전체가 멈추면, 절반만 정리된 상태에서 다시 실행할 방법이 없어지기 때문이다. * *

DB 정리는 그 기관의 채널을 모두 보관한 뒤에만 한다. 한쪽만 보관된 상태에서 * ID를 지워버리면 남은 채널을 다시 찾을 방법이 사라진다.

*/ public ResetReport reset() { int archived = 0; int cleared = 0; List failures = new ArrayList<>(); for (ResetTarget target : preview()) { boolean allArchived = true; for (ResetTarget.ResetChannel channel : target.channels()) { try { mattermost.archiveChannel(channel.channelId()); archived++; } catch (RuntimeException e) { allArchived = false; failures.add("%s %s (%s): %s".formatted( target.orgNo(), target.orgName(), channel.kind(), e.getMessage())); log.warn("채널 보관 실패 org={} kind={}", target.orgNo(), channel.kind(), e); } } if (allArchived) { orgMapper.clearChannels(target.orgId()); stageHistoryMapper.deleteByOrg(target.orgId()); cleared++; } } log.info("채널 초기화 완료: 보관 {}개, 기관 {}곳 초기화, 실패 {}건", archived, cleared, failures.size()); return new ResetReport(archived, cleared, failures); } private List channelsOf(Organization org) { List channels = new ArrayList<>(2); if (hasText(org.getChannelIdMj())) { channels.add(new ResetTarget.ResetChannel(KIND_MJ, org.getChannelIdMj())); } if (hasText(org.getChannelIdLaw())) { channels.add(new ResetTarget.ResetChannel(KIND_LAW, org.getChannelIdLaw())); } return channels; } private boolean hasText(String value) { return value != null && !value.isBlank(); } }