From fb4184584d3ea38e427b43c225678e9a619ca548 Mon Sep 17 00:00:00 2001 From: hy001 Date: Thu, 30 Apr 2026 20:07:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=AC=A1=E6=9C=88=E6=94=BF=E7=AD=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/team/TeamPolicyStatusEnum.java | 32 ++ .../model/cmd/team/TeamPolicyManagerCmd.java | 40 ++- .../agency/agency/TeamPolicyManagerDTO.java | 38 ++- .../team/TeamPolicyManagerRestController.java | 8 +- .../clienobject/team/TeamPolicyManagerCO.java | 38 ++- .../scheduler/TeamPolicyActivationTask.java | 94 ++++++ .../team/TeamPolicyManagerCO.java | 38 ++- .../entity/team/team/TeamPolicyManager.java | 38 ++- .../team/team/TeamPolicyManagerService.java | 37 ++- .../impl/TeamPolicyManagerServiceImpl.java | 278 ++++++++++++++---- .../TeamPolicyManagerClientServiceImpl.java | 47 +-- 11 files changed, 549 insertions(+), 139 deletions(-) create mode 100644 rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/enums/team/TeamPolicyStatusEnum.java create mode 100644 rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/TeamPolicyActivationTask.java diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/enums/team/TeamPolicyStatusEnum.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/enums/team/TeamPolicyStatusEnum.java new file mode 100644 index 00000000..3fa1daf6 --- /dev/null +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/enums/team/TeamPolicyStatusEnum.java @@ -0,0 +1,32 @@ +package com.red.circle.other.inner.enums.team; + +/** + * 团队政策状态. + */ +public enum TeamPolicyStatusEnum { + + /** + * 草稿. + */ + DRAFT, + + /** + * 待生效. + */ + SCHEDULED, + + /** + * 当前生效. + */ + ACTIVE, + + /** + * 历史政策. + */ + HISTORY, + + /** + * 已取消. + */ + CANCELLED +} diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/team/TeamPolicyManagerCmd.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/team/TeamPolicyManagerCmd.java index 7648ee61..530c9ed2 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/team/TeamPolicyManagerCmd.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/team/TeamPolicyManagerCmd.java @@ -41,16 +41,36 @@ public class TeamPolicyManagerCmd extends CommonCommand { @NotBlank(message = "region {not.null}") private String region; - /** - * true已发布队外政策. - */ - private Boolean release; - - /** - * 标题. - */ - @NotBlank(message = "title {not.null}") - private String title; + /** + * true已发布队外政策. + */ + private Boolean release; + + /** + * 政策状态 DRAFT/SCHEDULED/ACTIVE/HISTORY/CANCELLED. + */ + private String policyStatus; + + /** + * 生效月份 yyyyMM. + */ + private Integer effectiveMonth; + + /** + * 生效时间. + */ + private Timestamp effectiveStartTime; + + /** + * 实际激活时间. + */ + private Timestamp activatedTime; + + /** + * 标题. + */ + @NotBlank(message = "title {not.null}") + private String title; /** * 政策. diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/agency/agency/TeamPolicyManagerDTO.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/agency/agency/TeamPolicyManagerDTO.java index 60f20ffe..cd6bd681 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/agency/agency/TeamPolicyManagerDTO.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/agency/agency/TeamPolicyManagerDTO.java @@ -47,15 +47,35 @@ public class TeamPolicyManagerDTO implements Serializable { */ private Boolean historyRelease; - /** - * true已发布队外政策. - */ - private Boolean release; - - /** - * 标题. - */ - private String title; + /** + * true已发布队外政策. + */ + private Boolean release; + + /** + * 政策状态 DRAFT/SCHEDULED/ACTIVE/HISTORY/CANCELLED. + */ + private String policyStatus; + + /** + * 生效月份 yyyyMM. + */ + private Integer effectiveMonth; + + /** + * 生效时间. + */ + private Timestamp effectiveStartTime; + + /** + * 实际激活时间. + */ + private Timestamp activatedTime; + + /** + * 标题. + */ + private String title; /** * 政策. diff --git a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/team/TeamPolicyManagerRestController.java b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/team/TeamPolicyManagerRestController.java index fb4378af..ad2cde58 100644 --- a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/team/TeamPolicyManagerRestController.java +++ b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/team/TeamPolicyManagerRestController.java @@ -3,8 +3,9 @@ package com.red.circle.console.adapter.app.team; import com.red.circle.console.app.dto.clienobject.team.TeamPolicyManagerCO; import com.red.circle.console.app.service.app.team.TeamPolicyManagerBackService; import com.red.circle.framework.web.controller.BaseController; -import com.red.circle.other.inner.endpoint.team.target.TeamBillCycleClient; -import com.red.circle.other.inner.model.cmd.team.TeamBillCycleBackQryCmd; +import com.red.circle.other.inner.endpoint.team.target.TeamBillCycleClient; +import com.red.circle.other.inner.enums.team.TeamPolicyStatusEnum; +import com.red.circle.other.inner.model.cmd.team.TeamBillCycleBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamPolicyManagerBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamPolicyManagerCmd; import com.red.circle.tool.core.date.TimestampUtils; @@ -60,7 +61,8 @@ public class TeamPolicyManagerRestController extends BaseController { teamPolicyManagerBackService.add(param); // 发布完政策如果有团队是其他政策需要替换最新的政策,可能出现的情况:钻石->美元;美元->钻石;美元->美元;钻石->钻石 - if (Objects.equals(param.getRelease(), Boolean.TRUE)) { + if (Objects.equals(param.getRelease(), Boolean.TRUE) + && !Objects.equals(param.getPolicyStatus(), TeamPolicyStatusEnum.SCHEDULED.name())) { // 发布后主动重算该系统、该区域下团队的未出账账单。 teamBillCycleClient.refreshUnpaidBills(new TeamBillCycleBackQryCmd() .setSysOrigin(param.getSysOrigin()) diff --git a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/team/TeamPolicyManagerCO.java b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/team/TeamPolicyManagerCO.java index 2633251b..ff115623 100644 --- a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/team/TeamPolicyManagerCO.java +++ b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/team/TeamPolicyManagerCO.java @@ -48,15 +48,35 @@ public class TeamPolicyManagerCO implements Serializable { */ private Boolean historyRelease; - /** - * true已发布队外政策. - */ - private Boolean release; - - /** - * 标题. - */ - private String title; + /** + * true已发布队外政策. + */ + private Boolean release; + + /** + * 政策状态 DRAFT/SCHEDULED/ACTIVE/HISTORY/CANCELLED. + */ + private String policyStatus; + + /** + * 生效月份 yyyyMM. + */ + private Integer effectiveMonth; + + /** + * 生效时间. + */ + private Timestamp effectiveStartTime; + + /** + * 实际激活时间. + */ + private Timestamp activatedTime; + + /** + * 标题. + */ + private String title; /** * 政策. diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/TeamPolicyActivationTask.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/TeamPolicyActivationTask.java new file mode 100644 index 00000000..d8bc97af --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/TeamPolicyActivationTask.java @@ -0,0 +1,94 @@ +package com.red.circle.other.app.scheduler; + +import com.google.common.collect.Lists; +import com.red.circle.component.redis.annotation.TaskCacheLock; +import com.red.circle.mq.rocket.business.producer.TeamSalaryMqMessage; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService; +import com.red.circle.other.inner.model.dto.agency.agency.TeamStatus; +import com.red.circle.tool.core.collection.CollectionUtils; +import com.red.circle.tool.core.date.TimestampUtils; +import com.red.circle.tool.core.text.StringUtils; +import java.sql.Timestamp; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * 团队政策待生效切换任务. + */ +@Slf4j +@Component +@AllArgsConstructor +public class TeamPolicyActivationTask { + + private final TeamPolicyManagerService teamPolicyManagerService; + private final TeamProfileService teamProfileService; + private final TeamSalaryMqMessage teamSalaryMqMessage; + + /** + * 每5分钟补偿激活已到生效时间的政策,避免服务短暂不可用导致漏切换. + */ + @Scheduled(cron = "15 0/5 * * * ?", zone = "Asia/Riyadh") + @TaskCacheLock(key = "TEAM_POLICY_ACTIVATION_TASK", expireSecond = 300) + public void activateScheduledPolicies() { + long startTime = System.currentTimeMillis(); + Timestamp now = TimestampUtils.now(); + List policies = teamPolicyManagerService.listDueScheduledPolicies(now); + if (CollectionUtils.isEmpty(policies)) { + return; + } + + Set refreshedRegions = new HashSet<>(); + int activatedCount = 0; + for (TeamPolicyManager policy : policies) { + TeamPolicyManager activatedPolicy = teamPolicyManagerService.activateScheduledPolicy(policy.getId(), now); + if (Objects.isNull(activatedPolicy)) { + continue; + } + activatedCount++; + String refreshKey = buildRefreshKey(activatedPolicy); + if (refreshedRegions.add(refreshKey)) { + refreshUnpaidBills(activatedPolicy); + } + } + + log.info("exec team_policy_activation_task activated={}, cost={}", + activatedCount, System.currentTimeMillis() - startTime); + } + + private void refreshUnpaidBills(TeamPolicyManager policy) { + if (Objects.isNull(policy) + || StringUtils.isBlank(policy.getSysOrigin()) + || StringUtils.isBlank(policy.getRegion())) { + return; + } + + List teamProfiles = Optional.ofNullable(teamProfileService.listRegionAll(policy.getRegion())) + .orElse(Lists.newArrayList()) + .stream() + .filter(teamProfile -> Objects.equals(TeamStatus.AVAILABLE, teamProfile.getStatus())) + .filter(teamProfile -> Objects.equals(policy.getSysOrigin(), teamProfile.getSysOrigin())) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(teamProfiles)) { + return; + } + + Lists.partition(teamProfiles, 200).forEach(teamList -> teamSalaryMqMessage.sendSalaryCount( + teamList.stream().map(TeamProfile::getId).collect(Collectors.toSet()))); + } + + private String buildRefreshKey(TeamPolicyManager policy) { + return policy.getSysOrigin() + ":" + policy.getRegion(); + } +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/team/TeamPolicyManagerCO.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/team/TeamPolicyManagerCO.java index 17c6bd84..2817525f 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/team/TeamPolicyManagerCO.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/team/TeamPolicyManagerCO.java @@ -46,15 +46,35 @@ public class TeamPolicyManagerCO extends ClientObject { */ private Boolean historyRelease; - /** - * true已发布队外政策. - */ - private Boolean release; - - /** - * 标题. - */ - private String title; + /** + * true已发布队外政策. + */ + private Boolean release; + + /** + * 政策状态 DRAFT/SCHEDULED/ACTIVE/HISTORY/CANCELLED. + */ + private String policyStatus; + + /** + * 生效月份 yyyyMM. + */ + private Integer effectiveMonth; + + /** + * 生效时间. + */ + private java.sql.Timestamp effectiveStartTime; + + /** + * 实际激活时间. + */ + private java.sql.Timestamp activatedTime; + + /** + * 标题. + */ + private String title; /** * 政策. diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/team/team/TeamPolicyManager.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/team/team/TeamPolicyManager.java index a196ceab..68f34ac5 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/team/team/TeamPolicyManager.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/team/team/TeamPolicyManager.java @@ -52,15 +52,35 @@ public class TeamPolicyManager implements Serializable { */ private Boolean historyRelease; - /** - * true已发布队外政策. - */ - private Boolean release; - - /** - * 标题. - */ - private String title; + /** + * true已发布队外政策. + */ + private Boolean release; + + /** + * 政策状态 DRAFT/SCHEDULED/ACTIVE/HISTORY/CANCELLED. + */ + private String policyStatus; + + /** + * 生效月份 yyyyMM. + */ + private Integer effectiveMonth; + + /** + * 生效时间. + */ + private Timestamp effectiveStartTime; + + /** + * 实际激活时间. + */ + private Timestamp activatedTime; + + /** + * 标题. + */ + private String title; /** * 政策. diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/TeamPolicyManagerService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/TeamPolicyManagerService.java index 6a5ca68c..38073f1e 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/TeamPolicyManagerService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/TeamPolicyManagerService.java @@ -1,9 +1,10 @@ package com.red.circle.other.infra.database.mongo.service.team.team; -import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager; -import java.util.List; -import java.util.Map; -import java.util.Set; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager; +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * 团队策略. @@ -74,15 +75,25 @@ public interface TeamPolicyManagerService { */ TeamPolicyManager getById(Long id); - /** - * 添加策略. - */ - void add(TeamPolicyManager policyManager); - - /** - * 删除策略. - * - * @param id id. + /** + * 添加策略. + */ + void add(TeamPolicyManager policyManager); + + /** + * 查询到期的待生效政策. + */ + List listDueScheduledPolicies(Timestamp now); + + /** + * 激活待生效政策. + */ + TeamPolicyManager activateScheduledPolicy(Long id, Timestamp now); + + /** + * 删除策略. + * + * @param id id. */ void deleteById(Long id); diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/impl/TeamPolicyManagerServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/impl/TeamPolicyManagerServiceImpl.java index 289d6b3c..4180c9f3 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/impl/TeamPolicyManagerServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/team/team/impl/TeamPolicyManagerServiceImpl.java @@ -1,23 +1,29 @@ package com.red.circle.other.infra.database.mongo.service.team.team.impl; import com.google.common.collect.Maps; +import com.mongodb.client.result.UpdateResult; import com.red.circle.other.infra.common.sys.CountryCodeAliasSupport; import com.red.circle.other.infra.database.mongo.dto.team.TeamPolicy; -import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager; -import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService; -import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum; -import com.red.circle.tool.core.collection.CollectionUtils; -import com.red.circle.tool.core.text.StringUtils; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Sort; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService; +import com.red.circle.other.inner.enums.team.TeamPolicyStatusEnum; +import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum; +import com.red.circle.tool.core.collection.CollectionUtils; +import com.red.circle.tool.core.text.StringUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.stream.Collectors; +import org.springframework.data.mongodb.core.query.Update; +import org.springframework.stereotype.Service; + +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; /** * 团队策略. @@ -28,6 +34,10 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService { + private static final ZoneId RIYADH_ZONE = ZoneId.of("Asia/Riyadh"); + private static final DateTimeFormatter EFFECTIVE_MONTH_FORMATTER = + DateTimeFormatter.ofPattern("yyyyMM"); + private final MongoTemplate mongoTemplate; private final CountryCodeAliasSupport countryCodeAliasSupport; @@ -67,24 +77,24 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService { } @Override - public List listReleaseTeamPolicy(String sysOrigin, String region) { - return mongoTemplate - .find(Query.query(Criteria.where("region").is(region) - .and("release").is(Boolean.TRUE) - .and("sysOrigin").is(sysOrigin)) - .with(Sort.by(Sort.Order.desc("createTime"))), - TeamPolicyManager.class); - } + public List listReleaseTeamPolicy(String sysOrigin, String region) { + return mongoTemplate + .find(Query.query(Criteria.where("region").is(region) + .and("release").is(Boolean.TRUE) + .and("sysOrigin").is(sysOrigin)) + .with(activePolicySort()), + TeamPolicyManager.class); + } @Override - public TeamPolicyManager getReleaseByRegionAndType(String sysOrigin, String region) { - return mongoTemplate.findOne(Query.query( - Criteria.where("region").is(region) - .and("release").is(Boolean.TRUE) - .and("sysOrigin").is(sysOrigin) - ), TeamPolicyManager.class); - } + public TeamPolicyManager getReleaseByRegionAndType(String sysOrigin, String region) { + return mongoTemplate.findOne(Query.query( + Criteria.where("region").is(region) + .and("release").is(Boolean.TRUE) + .and("sysOrigin").is(sysOrigin) + ).with(activePolicySort()), TeamPolicyManager.class); + } @Override public TeamPolicyManager getReleaseByRegionAndType(String sysOrigin, String region, String policyType, String countryCode) { @@ -102,8 +112,8 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService { criteria.and("countryCode").in(countryCodeAliasSupport.getMatchCodes(countryCode)); } - return mongoTemplate.findOne(Query.query(criteria), TeamPolicyManager.class); - } + return mongoTemplate.findOne(Query.query(criteria).with(activePolicySort()), TeamPolicyManager.class); + } @Override public Map mapRegionRelease(Set regions) { @@ -112,16 +122,17 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService { return Maps.newHashMap(); } - List teamPolicyManagers = mongoTemplate.find( - Query.query(Criteria.where("region").in(regions).and("release").is(Boolean.TRUE)), - TeamPolicyManager.class); + List teamPolicyManagers = mongoTemplate.find( + Query.query(Criteria.where("region").in(regions).and("release").is(Boolean.TRUE)) + .with(activePolicySort()), + TeamPolicyManager.class); if (CollectionUtils.isEmpty(teamPolicyManagers)) { return Maps.newHashMap(); } - return teamPolicyManagers.stream() - .collect(Collectors.toMap(TeamPolicyManager::getRegion, v -> v)); + return teamPolicyManagers.stream() + .collect(Collectors.toMap(TeamPolicyManager::getRegion, v -> v, (existing, replacement) -> existing)); } @Override @@ -132,10 +143,11 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService { } // 查询所有 region 的已发布政策(所有 countryCode) - List policies = mongoTemplate.find( - Query.query(Criteria.where("region").in(regions) - .and("release").is(Boolean.TRUE)), - TeamPolicyManager.class); + List policies = mongoTemplate.find( + Query.query(Criteria.where("region").in(regions) + .and("release").is(Boolean.TRUE)) + .with(activePolicySort()), + TeamPolicyManager.class); if (CollectionUtils.isEmpty(policies)) { return Maps.newHashMap(); @@ -165,27 +177,173 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService { return mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)), TeamPolicyManager.class); } - @Override - public void add(TeamPolicyManager policyManager) { - policyManager.setHistoryRelease(Boolean.FALSE); - - if (Boolean.TRUE.equals(policyManager.getRelease()) && StringUtils - .isNotBlank(policyManager.getRegion()) - && StringUtils.isNotBlank(policyManager.getSysOrigin())) { - - policyManager.setHistoryRelease(Boolean.TRUE); - } - - policyManager.setPolicy(policyManager.getPolicy().stream() - .sorted(Comparator.comparing(TeamPolicy::getLevel)) - .collect(Collectors.toList())); - mongoTemplate.save(policyManager); - } + @Override + public void add(TeamPolicyManager policyManager) { + Timestamp now = Optional.ofNullable(policyManager.getCreateTime()) + .orElseGet(() -> new Timestamp(System.currentTimeMillis())); + normalizePolicyState(policyManager, now); + + if (Boolean.TRUE.equals(policyManager.getRelease()) && StringUtils.isNotBlank(policyManager.getRegion()) + && StringUtils.isNotBlank(policyManager.getSysOrigin())) { + deactivateActivePolicies(policyManager, now); + } + + if (CollectionUtils.isNotEmpty(policyManager.getPolicy())) { + policyManager.setPolicy(policyManager.getPolicy().stream() + .sorted(Comparator.comparing(TeamPolicy::getLevel)) + .collect(Collectors.toList())); + } + mongoTemplate.save(policyManager); + } + + @Override + public List listDueScheduledPolicies(Timestamp now) { + return mongoTemplate.find(Query.query(Criteria.where("policyStatus") + .is(TeamPolicyStatusEnum.SCHEDULED.name()) + .and("release").is(Boolean.FALSE) + .and("effectiveStartTime").lte(now)) + .with(Sort.by(Sort.Order.asc("effectiveStartTime"), Sort.Order.asc("createTime"))), + TeamPolicyManager.class); + } + + @Override + public TeamPolicyManager activateScheduledPolicy(Long id, Timestamp now) { + TeamPolicyManager scheduled = mongoTemplate.findOne(Query.query(Criteria.where("id").is(id) + .and("policyStatus").is(TeamPolicyStatusEnum.SCHEDULED.name()) + .and("release").is(Boolean.FALSE) + .and("effectiveStartTime").lte(now)), + TeamPolicyManager.class); + if (Objects.isNull(scheduled)) { + return null; + } + + deactivateActivePolicies(scheduled, now); + + Update update = new Update() + .set("release", Boolean.TRUE) + .set("historyRelease", Boolean.TRUE) + .set("policyStatus", TeamPolicyStatusEnum.ACTIVE.name()) + .set("effectiveMonth", resolveEffectiveMonth(scheduled.getEffectiveStartTime())) + .set("activatedTime", now) + .set("updateTime", now); + + UpdateResult result = mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(id) + .and("policyStatus").is(TeamPolicyStatusEnum.SCHEDULED.name()) + .and("release").is(Boolean.FALSE)), + update, + TeamPolicyManager.class); + + if (result.getModifiedCount() <= 0) { + return null; + } + return getById(id); + } @Override - public void deleteById(Long id) { - - mongoTemplate.remove(Query.query(Criteria.where("id").is(id)), TeamPolicyManager.class); - } - -} + public void deleteById(Long id) { + + mongoTemplate.remove(Query.query(Criteria.where("id").is(id)), TeamPolicyManager.class); + } + + private void normalizePolicyState(TeamPolicyManager policyManager, Timestamp now) { + String policyStatus = policyManager.getPolicyStatus(); + if (StringUtils.isBlank(policyStatus)) { + policyStatus = Boolean.TRUE.equals(policyManager.getRelease()) + ? TeamPolicyStatusEnum.ACTIVE.name() + : TeamPolicyStatusEnum.DRAFT.name(); + } + + if (Objects.equals(policyStatus, TeamPolicyStatusEnum.SCHEDULED.name())) { + Timestamp effectiveStartTime = Optional.ofNullable(policyManager.getEffectiveStartTime()) + .orElseGet(this::nextMonthStartTime); + policyManager.setPolicyStatus(TeamPolicyStatusEnum.SCHEDULED.name()); + policyManager.setRelease(Boolean.FALSE); + policyManager.setHistoryRelease(Boolean.FALSE); + policyManager.setEffectiveStartTime(effectiveStartTime); + policyManager.setEffectiveMonth(resolveEffectiveMonth(effectiveStartTime)); + policyManager.setActivatedTime(null); + return; + } + + if (Boolean.TRUE.equals(policyManager.getRelease()) + || Objects.equals(policyStatus, TeamPolicyStatusEnum.ACTIVE.name())) { + policyManager.setPolicyStatus(TeamPolicyStatusEnum.ACTIVE.name()); + policyManager.setRelease(Boolean.TRUE); + policyManager.setHistoryRelease(Boolean.TRUE); + policyManager.setActivatedTime(Optional.ofNullable(policyManager.getActivatedTime()).orElse(now)); + policyManager.setEffectiveStartTime(Optional.ofNullable(policyManager.getEffectiveStartTime()).orElse(now)); + policyManager.setEffectiveMonth(Optional.ofNullable(policyManager.getEffectiveMonth()) + .orElseGet(() -> resolveEffectiveMonth(policyManager.getEffectiveStartTime()))); + return; + } + + policyManager.setPolicyStatus(policyStatus); + policyManager.setRelease(Boolean.FALSE); + policyManager.setHistoryRelease(Boolean.TRUE.equals(policyManager.getHistoryRelease())); + } + + private void deactivateActivePolicies(TeamPolicyManager policyManager, Timestamp now) { + Criteria criteria = samePolicyScopeCriteria(policyManager) + .and("release").is(Boolean.TRUE); + if (Objects.nonNull(policyManager.getId())) { + criteria.and("id").ne(policyManager.getId()); + } + + mongoTemplate.updateMulti(Query.query(criteria), new Update() + .set("release", Boolean.FALSE) + .set("historyRelease", Boolean.TRUE) + .set("policyStatus", TeamPolicyStatusEnum.HISTORY.name()) + .set("updateTime", now), + TeamPolicyManager.class); + } + + private Criteria samePolicyScopeCriteria(TeamPolicyManager policyManager) { + Criteria criteria = Criteria.where("region").is(policyManager.getRegion()) + .and("sysOrigin").is(policyManager.getSysOrigin()); + appendPolicyTypeCriteria(criteria, policyManager.getPolicyType()); + appendCountryScopeCriteria(criteria, policyManager.getCountryCode()); + return criteria; + } + + private void appendPolicyTypeCriteria(Criteria criteria, String policyType) { + if (StringUtils.isNotBlank(policyType) + && Objects.equals(policyType, TeamPolicyTypeEnum.SALARY_DIAMOND.name())) { + criteria.and("policyType").is(policyType); + return; + } + criteria.and("policyType").ne(TeamPolicyTypeEnum.SALARY_DIAMOND.name()); + } + + private void appendCountryScopeCriteria(Criteria criteria, String countryCode) { + if (StringUtils.isNotBlank(countryCode)) { + criteria.and("countryCode").in(countryCodeAliasSupport.getMatchCodes(countryCode)); + return; + } + criteria.orOperator( + Criteria.where("countryCode").exists(false), + Criteria.where("countryCode").is(null), + Criteria.where("countryCode").is("")); + } + + private Timestamp nextMonthStartTime() { + LocalDate nextMonthFirstDay = LocalDate.now(RIYADH_ZONE).plusMonths(1).withDayOfMonth(1); + return Timestamp.from(nextMonthFirstDay.atStartOfDay(RIYADH_ZONE).toInstant()); + } + + private Integer resolveEffectiveMonth(Timestamp effectiveStartTime) { + if (Objects.isNull(effectiveStartTime)) { + return null; + } + return Integer.valueOf(effectiveStartTime.toInstant() + .atZone(RIYADH_ZONE) + .format(EFFECTIVE_MONTH_FORMATTER)); + } + + private Sort activePolicySort() { + return Sort.by( + Sort.Order.desc("effectiveStartTime"), + Sort.Order.desc("activatedTime"), + Sort.Order.desc("createTime")); + } + +} diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/TeamPolicyManagerClientServiceImpl.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/TeamPolicyManagerClientServiceImpl.java index 6e0b3ddb..49095919 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/TeamPolicyManagerClientServiceImpl.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/TeamPolicyManagerClientServiceImpl.java @@ -1,14 +1,15 @@ package com.red.circle.other.app.inner.service.team.impl; -import com.red.circle.framework.core.asserts.ResponseAssert; -import com.red.circle.framework.core.response.CommonErrorCode; -import com.red.circle.other.infra.convertor.team.TeamInfraConvertor; -import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager; -import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService; -import com.red.circle.other.app.inner.service.team.TeamPolicyManagerClientService; -import com.red.circle.other.inner.model.cmd.team.TeamPolicyManagerCmd; -import com.red.circle.other.inner.model.dto.agency.agency.TeamPolicyManagerDTO; -import com.red.circle.tool.core.sequence.IdWorkerUtils; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.framework.core.response.CommonErrorCode; +import com.red.circle.other.infra.convertor.team.TeamInfraConvertor; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService; +import com.red.circle.other.app.inner.service.team.TeamPolicyManagerClientService; +import com.red.circle.other.inner.enums.team.TeamPolicyStatusEnum; +import com.red.circle.other.inner.model.cmd.team.TeamPolicyManagerCmd; +import com.red.circle.other.inner.model.dto.agency.agency.TeamPolicyManagerDTO; +import com.red.circle.tool.core.sequence.IdWorkerUtils; import java.util.List; import java.util.Map; import java.util.Objects; @@ -68,14 +69,26 @@ public class TeamPolicyManagerClientServiceImpl implements TeamPolicyManagerClie teamPolicyManagerService.listTeamPolicyAndTypePolicy(sysOrigin, region, policyType, countryCode)); } - @Override - public void add(TeamPolicyManagerCmd cmd) { - if (Objects.nonNull(cmd.getId())) { - teamPolicyManagerService.deleteById(cmd.getId()); - } - cmd.setId(IdWorkerUtils.getId()); - teamPolicyManagerService.add(teamAppConvertor.toTeamPolicyManager(cmd)); - } + @Override + public void add(TeamPolicyManagerCmd cmd) { + if (Objects.nonNull(cmd.getId())) { + TeamPolicyManager existingPolicy = teamPolicyManagerService.getById(cmd.getId()); + if (Objects.nonNull(existingPolicy) && shouldReplaceExistingPolicy(cmd, existingPolicy)) { + teamPolicyManagerService.deleteById(cmd.getId()); + } + } + cmd.setId(IdWorkerUtils.getId()); + teamPolicyManagerService.add(teamAppConvertor.toTeamPolicyManager(cmd)); + } + + private boolean shouldReplaceExistingPolicy(TeamPolicyManagerCmd cmd, TeamPolicyManager existingPolicy) { + if (!Objects.equals(cmd.getPolicyStatus(), TeamPolicyStatusEnum.SCHEDULED.name())) { + return true; + } + return Objects.equals(existingPolicy.getPolicyStatus(), TeamPolicyStatusEnum.SCHEDULED.name()) + && !Boolean.TRUE.equals(existingPolicy.getHistoryRelease()) + && !Boolean.TRUE.equals(existingPolicy.getRelease()); + } @Override