File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
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 채널을 보관 처리하고, 기관의 채널 상태를
* 비워 채널관리에서 처음부터 다시 만들 수 있게 되돌린다.
*
* <p><b>대상은 웹앱 DB가 아는 채널뿐이다.</b> organization.channel_id_mj / channel_id_law에
* 들어 있는 ID만 건드린다. Mattermost 팀에는 엑셀 VBA(M07)로 만든 예전 채널과 공용 채널
* (Town Square, FAQ, 대쉬보드, 양식 모음, 자료실)이 함께 있는데, 이름으로 훑어서 지우면
* 그것들까지 날아간다. 그래서 이름 검색 경로는 의도적으로 만들지 않았다.</p>
*
* <p><b>사용자 계정은 건드리지 않는다.</b> 웹앱은 만든 계정을 기록하지 않고 이메일로 조회해
* 없을 때만 만드는 구조라, 원래 있던 실제 직원 계정과 구분할 방법이 없다. 계정까지 정리하려면
* 생성 출처를 DB에 남기는 작업이 먼저다.</p>
*
* <p>보관은 Mattermost의 아카이브다(영구 삭제 아님). 게시글·파일은 남고 시스템 콘솔에서
* 복구할 수 있다. 권리확인/권리처리·업무메모·담당자 배정 등 DB 데이터도 그대로 둔다.</p>
*/
@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<ResetTarget> preview() {
List<ResetTarget> targets = new ArrayList<>();
for (Organization org : orgMapper.findAll()) {
List<ResetTarget.ResetChannel> channels = channelsOf(org);
if (channels.isEmpty()) {
continue;
}
targets.add(new ResetTarget(org.getId(), org.getOrgNo(), org.getOrgName(),
org.getStage(), channels));
}
return targets;
}
/**
* 실제 초기화. 채널 하나가 실패해도 나머지는 계속한다 - 이미 지워진 채널이나 권한 문제로
* 전체가 멈추면, 절반만 정리된 상태에서 다시 실행할 방법이 없어지기 때문이다.
*
* <p>DB 정리는 <b>그 기관의 채널을 모두 보관한 뒤에만</b> 한다. 한쪽만 보관된 상태에서
* ID를 지워버리면 남은 채널을 다시 찾을 방법이 사라진다.</p>
*/
public ResetReport reset() {
int archived = 0;
int cleared = 0;
List<String> 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<ResetTarget.ResetChannel> channelsOf(Organization org) {
List<ResetTarget.ResetChannel> 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();
}
}