diff --git a/src/main/java/digital/laboratory/platform/sewage/SewageJobApplication.java b/src/main/java/digital/laboratory/platform/sewage/SewageJobApplication.java index 9cf4bae..1776638 100644 --- a/src/main/java/digital/laboratory/platform/sewage/SewageJobApplication.java +++ b/src/main/java/digital/laboratory/platform/sewage/SewageJobApplication.java @@ -6,15 +6,17 @@ import digital.laboratory.platform.common.swagger.annotation.EnableDLPSwagger2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.scheduling.annotation.EnableScheduling; @EnableDLPSwagger2 @EnableDLPFeignClients @EnableDiscoveryClient @EnableDLPResourceServer -@SpringBootApplication(scanBasePackages="digital.laboratory.platform") +@SpringBootApplication(scanBasePackages = "digital.laboratory.platform") +@EnableScheduling public class SewageJobApplication { - public static void main(String[] args) { - SpringApplication.run(SewageJobApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(SewageJobApplication.class, args); + } } diff --git a/src/main/java/digital/laboratory/platform/sewage/controller/SewageJobController.java b/src/main/java/digital/laboratory/platform/sewage/controller/SewageJobController.java index 4c7a940..c30f2f2 100644 --- a/src/main/java/digital/laboratory/platform/sewage/controller/SewageJobController.java +++ b/src/main/java/digital/laboratory/platform/sewage/controller/SewageJobController.java @@ -299,7 +299,7 @@ public class SewageJobController { sewageJob.setLauncheOrgId(dlpUser.getOrgId()); sewageJob.setStatus(0); // 初始的状态是刚创建 long count = sewageJobService.count(Wrappers.lambdaQuery().eq(SewageJob::getJobYear, sewageJob.getJobYear()).eq(SewageJob::getJobSeason, sewageJob.getJobSeason())); - if (count>0){ + if (count > 0) { return R.failed(sewageJob, "当前月份任务已创建,创建失败!"); } if (sewageJobService.save(sewageJob)) { @@ -610,7 +610,7 @@ public class SewageJobController { } @PostMapping("/lead/into/{year}/{month}") - @ApiOperation(value = "导入污水任务Excel",notes = "参数:sewageJobIdentificationMaterialList:传入的Excel行的对象数组,year:拼接在路径上的任务年份,month:拼接在对象上的任务月份") + @ApiOperation(value = "导入污水任务Excel", notes = "参数:sewageJobIdentificationMaterialList:传入的Excel行的对象数组,year:拼接在路径上的任务年份,month:拼接在对象上的任务月份") public R LeadIntoJob(@RequestBody List sewageJobIdentificationMaterialList, @PathVariable(value = "year") int year, @PathVariable(value = "month") int month, HttpServletRequest theHttpServletRequest) { Principal principal = theHttpServletRequest.getUserPrincipal(); DLPUser dlpUser = (DLPUser) ((OAuth2Authentication) principal).getUserAuthentication().getPrincipal(); @@ -666,4 +666,22 @@ public class SewageJobController { } return userOrg; } + + @GetMapping("/getPage") + @ApiOperation(value = "贵阳市局污水任务新版接口:获取污水任务列表", notes = "获取污水任务列表") + public R getPage(Page page) { + return R.ok(sewageJobService.getPage(page)); + } + + @PostMapping("/createJob") + @ApiOperation(value = "贵阳市局污水任务新版接口:根据年份和季度创建污水任务", notes = "根据年份和季度创建污水任务") + public R createJob(Integer year, Integer season) { + return R.ok(sewageJobService.createJob(year, season), "操作成功!"); + } + + @PostMapping("/removeJob") + @ApiOperation(value = "贵阳市局污水任务新版接口:删除污水任务", notes = "删除污水任务") + public R removeJob(String jobId) { + return sewageJobService.removeJob(jobId) ? R.ok("操作成功") : R.failed("操作失败"); + } } diff --git a/src/main/java/digital/laboratory/platform/sewage/controller/SewageJobIdentificationMaterialController.java b/src/main/java/digital/laboratory/platform/sewage/controller/SewageJobIdentificationMaterialController.java index 4c9d967..48f3167 100644 --- a/src/main/java/digital/laboratory/platform/sewage/controller/SewageJobIdentificationMaterialController.java +++ b/src/main/java/digital/laboratory/platform/sewage/controller/SewageJobIdentificationMaterialController.java @@ -14,6 +14,7 @@ import digital.laboratory.platform.common.core.util.R; import digital.laboratory.platform.common.core.util.TestUtils; import digital.laboratory.platform.common.log.annotation.SysLog; import digital.laboratory.platform.common.mybatis.security.service.DLPUser; +import digital.laboratory.platform.common.security.util.SecurityUtils; import digital.laboratory.platform.sewage.entity.SewageJobIdentificationMaterial; import digital.laboratory.platform.sewage.entity.SewageTreatmentPlant; import digital.laboratory.platform.sewage.entity.UpdateInfo; @@ -30,6 +31,7 @@ import digital.laboratory.platform.sys.entity.entrustment.SampleBox; import digital.laboratory.platform.sewage.vo.SewageJobIdentificationMaterialVO; import digital.laboratory.platform.sewage.vo.SewageJobItemVO; import digital.laboratory.platform.sys.feign.*; +import feign.Param; import io.seata.spring.annotation.GlobalTransactional; import org.apache.commons.lang.StringUtils; import org.springframework.beans.BeanUtils; @@ -42,7 +44,9 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.validation.constraints.NotBlank; import java.security.Principal; +import java.security.Security; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -57,7 +61,7 @@ import java.util.List; @RestController @RequestMapping("/sewage_job_identification_material") @Api(value = "sewage_job_identification_material", tags = "113-污水任务的检材信息管理") -public class SewageJobIdentificationMaterialController { +public class SewageJobIdentificationMaterialController { @Resource private SewageJobIdentificationMaterialService sewageJobIdentificationMaterialService; @@ -67,20 +71,20 @@ public class SewageJobIdentificationMaterialController { @Resource private SewageJobService sewageJobService; - + @Resource private SewageTreatmentPlantService sewageTreatmentPlantService; - - + + @Resource private RemoteDeliverService remoteDeliverService; - + @Resource private RemoteUserService remoteUserService; @Resource private RemoteOrgService remoteOrgService; - + @Resource private SewageJobIdentificationMaterialMapper sewageJobIdentificationMaterialMapper; @@ -1776,7 +1780,7 @@ public class SewageJobIdentificationMaterialController { .or() .like("job_name", keyword)) - .in(statuses != null && statuses.length != 0,"status", statuses) + .in(statuses != null && statuses.length != 0, "status", statuses) .in("job_item_id", itemId) .orderByAsc("im_no", "job_name")); return R.ok(sewageJobIdentificationMaterialVOS); @@ -1810,4 +1814,60 @@ public class SewageJobIdentificationMaterialController { } return userOrg; } + + @PostMapping("/createMaterial") + @ApiOperation(value = "贵阳市局污水任务新版接口:批量创建污水检材", notes = "批量创建污水检材") + public R createMaterial(@RequestBody List materials) { + DLPUser user = SecurityUtils.getUser(); + sewageJobIdentificationMaterialService.createMaterial(materials, user); + return R.ok("创建成功!"); + } + + + @PutMapping("/batchCommit") + @ApiOperation(value = "贵阳市局污水任务新版接口:批量提交污水检材", notes = "批量提交污水检材") + public R batchCommitMaterial(@RequestBody SewageJobIdentificationMaterialDTO materialDTO) { + List materialList = sewageJobIdentificationMaterialService.batchCommitMaterial(materialDTO); + return R.ok(materialList, "提交成功!"); + + } + + @PutMapping("/batchSignFor") + @ApiOperation(value = "贵阳市局污水任务新版接口:批量签收污水检材", notes = "批量签收污水检材") + public R batchSignForMaterial(@RequestBody SewageJobIdentificationMaterialDTO materialDTO) { + List materialList = sewageJobIdentificationMaterialService.batchSignForMaterial(materialDTO); + return R.ok(materialList, "签收成功!"); + } + + @GetMapping("/getList") + @ApiOperation(value = "贵阳市局污水任务新版接口:获取污水任务清单的检材列表", notes = "获取污水任务清单的检材列表") + public R getList(@NotBlank(message = "任务ID不能为空!") String jobId, Integer status) { + DLPUser user = SecurityUtils.getUser(); + return R.ok(sewageJobIdentificationMaterialService.getList(jobId, status, user), "查询成功!"); + } + + @GetMapping("/getPage") + @ApiOperation(value = "贵阳市局污水任务新版接口:获取污水任务清单的检材列表", notes = "获取污水任务清单的检材列表") + public R getPage(Page page, @NotBlank(message = "任务ID不能为空!") String jobId, Integer status) { + return R.ok(sewageJobIdentificationMaterialService.getPage(page, jobId, status), "查询成功!"); + } + + + @PutMapping("/editById") + @ApiOperation(value = "贵阳市局污水任务新版接口:编辑污水任务清单的检材信息", notes = "编辑污水任务清单的检材信息") + public R editById(@RequestBody SewageJobIdentificationMaterial material) { + SewageJobIdentificationMaterial material1 = sewageJobIdentificationMaterialService.editById(material); + return material != null ? R.ok(material1, "修改成功!") : R.failed("修改失败!"); + } + + + @DeleteMapping("remove") + @ApiOperation(value = "贵阳市局污水任务新版接口:根据ID删除污水检材", notes = "根据ID删除污水检材") + public R removeById(String id) { + SewageJobIdentificationMaterial material = sewageJobIdentificationMaterialService.getById(id); + if (material.getStatus() != 0) { + throw new RuntimeException("该任务已提交,不能删除"); + } + return sewageJobIdentificationMaterialService.removeMaterial(id) ? R.ok("删除成功!") : R.failed("删除失败!"); + } } diff --git a/src/main/java/digital/laboratory/platform/sewage/service/SewageJobIdentificationMaterialService.java b/src/main/java/digital/laboratory/platform/sewage/service/SewageJobIdentificationMaterialService.java index 0ee2c0b..f299333 100644 --- a/src/main/java/digital/laboratory/platform/sewage/service/SewageJobIdentificationMaterialService.java +++ b/src/main/java/digital/laboratory/platform/sewage/service/SewageJobIdentificationMaterialService.java @@ -2,7 +2,9 @@ package digital.laboratory.platform.sewage.service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; +import digital.laboratory.platform.common.mybatis.security.service.DLPUser; import digital.laboratory.platform.sewage.dto.SewageJobIdentificationMaterialDTO; import digital.laboratory.platform.sewage.entity.SewageJobIdentificationMaterial; import digital.laboratory.platform.sewage.vo.SewageJobIdentificationMaterialVO; @@ -49,4 +51,18 @@ public interface SewageJobIdentificationMaterialService extends IService materialStatus); SewageJobIdentificationMaterial getByImNo(String imNo); + + List createMaterial(List materials, DLPUser user); + + List batchCommitMaterial(SewageJobIdentificationMaterialDTO materialDTO/*,int userLv,DLPUser dlpUser,SysOrg userOrg*/); + + List batchSignForMaterial(SewageJobIdentificationMaterialDTO materialDTO); + + List getList(String jobId, Integer status,DLPUser user); + + Page getPage(Page page, String jobId, Integer status); + + SewageJobIdentificationMaterial editById(SewageJobIdentificationMaterial material); + + boolean removeMaterial(String id); } diff --git a/src/main/java/digital/laboratory/platform/sewage/service/SewageJobService.java b/src/main/java/digital/laboratory/platform/sewage/service/SewageJobService.java index b4ccc3f..d7b008e 100644 --- a/src/main/java/digital/laboratory/platform/sewage/service/SewageJobService.java +++ b/src/main/java/digital/laboratory/platform/sewage/service/SewageJobService.java @@ -98,4 +98,10 @@ public interface SewageJobService extends IService { List createSample(SewageJobIdentificationMaterial sewageJobIdentificationMaterial, String dlpUserId); List createSample(List sewageJobIdentificationMaterialList, String dlpUserId); + + Page getPage(Page page); + + SewageJob createJob(Integer year, Integer season); + + boolean removeJob(String jobId); } diff --git a/src/main/java/digital/laboratory/platform/sewage/service/impl/SewageJobIdentificationMaterialServiceImpl.java b/src/main/java/digital/laboratory/platform/sewage/service/impl/SewageJobIdentificationMaterialServiceImpl.java index da144fb..1721ec3 100644 --- a/src/main/java/digital/laboratory/platform/sewage/service/impl/SewageJobIdentificationMaterialServiceImpl.java +++ b/src/main/java/digital/laboratory/platform/sewage/service/impl/SewageJobIdentificationMaterialServiceImpl.java @@ -3,7 +3,9 @@ package digital.laboratory.platform.sewage.service.impl; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import digital.laboratory.platform.common.core.constant.CommonConstants; import digital.laboratory.platform.common.feign.RemoteTemplate2htmlService; @@ -25,6 +27,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -38,6 +41,7 @@ import java.util.Map; */ @RequiredArgsConstructor @Service +@SuppressWarnings("all") public class SewageJobIdentificationMaterialServiceImpl extends ServiceImpl implements SewageJobIdentificationMaterialService { @Resource private SewageJobService sewageJobService; @@ -45,7 +49,7 @@ public class SewageJobIdentificationMaterialServiceImpl extends ServiceImpl createMaterial(List materials, DLPUser user) { + + String jobId = materials.get(0).getJobId(); +// int year = Year.now().getValue(); +// int quarter = (LocalDate.now().getMonthValue() - 1) / 3 + 1; +// SewageJob job = sewageJobService.lambdaQuery() +// .eq(SewageJob::getJobYear, year) +// .eq(SewageJob::getJobSeason, quarter) +// .eq(SewageJob::getStatus, 1) +// .one(); +// if (job == null) { +// throw new RuntimeException("没有找到对应的任务"); +// } + SewageJob job = sewageJobService.getById(jobId); + for (SewageJobIdentificationMaterial material : materials) { + material.setId(IdWorker.get32UUID().toUpperCase()); + material.setJobId(job.getId()); + material.setDeliverOrgId(user.getOrgName()); + material.setStatus(0); + } + this.saveBatch(materials); + return materials; + } + + @Override + public List batchCommitMaterial(SewageJobIdentificationMaterialDTO materialDTO/*,int userLv,DLPUser dlpUser,SysOrg userOrg*/) { + + List ids = materialDTO.getIds(); + + List materialList = this.list(Wrappers + .query() + .in("id", ids)); + + materialList.stream().forEach(item -> { + if (item.getStatus() != 1) { + throw new RuntimeException("请勾选未提交的数据进行提交操作!"); + } + item.setStatus(1); + }); + return this.updateBatchById(materialList) ? materialList : null; + } + + @Override + public List batchSignForMaterial(SewageJobIdentificationMaterialDTO materialDTO) { + + List ids = materialDTO.getIds(); + + List materialList = this.list(Wrappers + .query() + .in("id", ids)); + + materialList.stream().forEach(item -> { + if (item.getStatus() == 1) { + item.setStatus(2); + item.setAcceptTime(LocalDateTime.now()); + } + }); + return this.updateBatchById(materialList) ? materialList : null; + } + + @Override + public List getList(String jobId, Integer status, DLPUser user) { + return this.list(Wrappers + .query() + .eq("job_id", jobId) + .eq(status != null, "status", status) + .eq("deliver_org_id", user.getOrgName()) + .orderByDesc("create_time")); + } + + @Override + public Page getPage(Page page, String jobId, Integer status) { + return this.page(page, Wrappers + .query() + .eq("job_id", jobId) + .eq(status != null, "status", status) + .orderByDesc("create_time")); + } + + @Override + public SewageJobIdentificationMaterial editById(SewageJobIdentificationMaterial material) { + return this.updateById(material) ? material : null; + } + + @Override + public boolean removeMaterial(String id) { + SewageJobIdentificationMaterial material = this.getById(id); + if (material.getStatus() != 0) { + throw new RuntimeException("该任务已提交,不能删除"); + } + return this.removeById(id); + } } diff --git a/src/main/java/digital/laboratory/platform/sewage/service/impl/SewageJobServiceImpl.java b/src/main/java/digital/laboratory/platform/sewage/service/impl/SewageJobServiceImpl.java index bd3ce0e..36caa55 100644 --- a/src/main/java/digital/laboratory/platform/sewage/service/impl/SewageJobServiceImpl.java +++ b/src/main/java/digital/laboratory/platform/sewage/service/impl/SewageJobServiceImpl.java @@ -42,6 +42,7 @@ import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.math.BigDecimal; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.TemporalAdjusters; import java.util.ArrayList; @@ -72,6 +73,7 @@ public class SewageJobServiceImpl extends ServiceImpl sampleList=new ArrayList(); + List sampleList = new ArrayList(); List candidateDrugs = JSON.parseArray(AppStartupRunner.getCfg(CommonConstants.DLP_CODE_ENTRUSTMENT_SEWAGE_JOB_IDENTIFICATION_MATERIAL_CANDIDATE_DRUGS), DrugLite.class); List additionalProperties = JSON.parseArray(AppStartupRunner.getCfg(CommonConstants.DLP_CODE_ENTRUSTMENT_SEWAGE_JOB_IDENTIFICATION_MATERIAL_ADDITIONAL_PROPERTIES), IMAdditionalProperty.class); BigDecimal quantity = new BigDecimal(Double.parseDouble(AppStartupRunner.getCfg(CommonConstants.DLP_CODE_ENTRUSTMENT_SEWAGE_JOB_IDENTIFICATION_MATERIAL_QUANTITY))); @@ -937,7 +941,7 @@ public class SewageJobServiceImpl extends ServiceImpl sampleList=new ArrayList(); + List sampleList = new ArrayList(); List candidateDrugs = JSON.parseArray(AppStartupRunner.getCfg(CommonConstants.DLP_CODE_ENTRUSTMENT_SEWAGE_JOB_IDENTIFICATION_MATERIAL_CANDIDATE_DRUGS), DrugLite.class); List additionalProperties = JSON.parseArray(AppStartupRunner.getCfg(CommonConstants.DLP_CODE_ENTRUSTMENT_SEWAGE_JOB_IDENTIFICATION_MATERIAL_ADDITIONAL_PROPERTIES), IMAdditionalProperty.class); @@ -993,7 +996,7 @@ public class SewageJobServiceImpl extends ServiceImpl getPage(Page page) { + Page paged = this.page(page, Wrappers + .lambdaQuery() + .orderByDesc(SewageJob::getJobYear) + .orderByAsc(SewageJob::getJobSeason)); + List list = paged.getRecords(); + list.forEach(job -> { + job.setComments( + sewageJobIdentificationMaterialService.lambdaQuery() + .eq(SewageJobIdentificationMaterial::getJobId, job.getId()) + .eq(SewageJobIdentificationMaterial::getStatus, 1) + .count().toString()); + }); + return paged; + } + + @Override + public SewageJob createJob(Integer year, Integer season) { + boolean exists = this.lambdaQuery() + .eq(SewageJob::getJobYear, year) + .eq(SewageJob::getJobSeason, season) + .exists(); + if (exists) { + throw new RuntimeException("任务已存在"); + } + SewageJob job = new SewageJob(); + int month = (season - 1) * 3 + 1; // 季度对应的第一个月 (1,4,7,10) + LocalDate startDate = LocalDate.of(year, month, 1); + // 计算本季度的结束日期 + LocalDate endDate = startDate.plusMonths(3).minusDays(1); + + job.setJobYear(year); + job.setJobSeason(season); + job.setName(year + "年第" + season + "季度污水监测任务"); + job.setJobNo(this.getNewJobNo(year, season)); + job.setStatus(1); + job.setDescription("贵阳市" + year + "年第" + season + "季度污水监测任务"); + job.setLauncheOrgId("0"); + job.setStartDate(startDate.atStartOfDay()); + job.setExpirationDate(endDate.atStartOfDay()); + + boolean saved = this.save(job); + + if (saved) { + System.out.println(">>> 创建成功:" + "year" + "年第" + season + "季度"); + return job; + } else { + System.out.println(">>> 创建失败:" + "year" + "年第" + season + "季度"); + return null; + } + } + + @Override + public boolean removeJob(String jobId) { + boolean exists = sewageJobIdentificationMaterialService.lambdaQuery() + .eq(SewageJobIdentificationMaterial::getJobId, jobId) + .exists(); + if (exists) { + System.out.println(">>> 删除失败:" + "已有检材存在"); + return false; + } else { + return this.removeById(jobId); + } + } } diff --git a/src/main/java/digital/laboratory/platform/sewage/task/JobTask.java b/src/main/java/digital/laboratory/platform/sewage/task/JobTask.java new file mode 100644 index 0000000..8aa15a3 --- /dev/null +++ b/src/main/java/digital/laboratory/platform/sewage/task/JobTask.java @@ -0,0 +1,74 @@ +package digital.laboratory.platform.sewage.task; + +import digital.laboratory.platform.sewage.entity.SewageJob; +import digital.laboratory.platform.sewage.service.SewageJobService; +import lombok.extern.java.Log; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.time.LocalDate; +import java.time.Year; + +@Log +@Component +public class JobTask { + + @Resource + private SewageJobService sewageJobService; + + /** + * 每天凌晨 0 点执行:判断当天所属季度任务是否存在,不存在则创建 + */ + @Scheduled(cron = "0 0 0 * * ?") + public void runAtMidnight() { + log.info(">>> 每天凌晨定时任务开始执行"); + + int year = Year.now().getValue(); + int quarter = getCurrentQuarter(); + // 计算本季度的开始日期 + int month = (quarter - 1) * 3 + 1; // 季度对应的第一个月 (1,4,7,10) + LocalDate startDate = LocalDate.of(year, month, 1); + // 计算本季度的结束日期 + LocalDate endDate = startDate.plusMonths(3).minusDays(1); + + boolean exists = sewageJobService.lambdaQuery() + .eq(SewageJob::getJobYear, year) + .eq(SewageJob::getJobSeason, quarter) + .exists(); + + if (exists) { + log.info(">>> 季度任务已存在:" + "year" + "年第" + quarter + "季度"); + } else { + log.info(">>> 未找到季度任务,开始创建:" + "year" + "年第" + quarter + "季度"); + + SewageJob job = new SewageJob(); + job.setJobYear(year); + job.setJobSeason(quarter); + job.setName(year + "年第" + quarter + "季度污水监测任务"); + job.setJobNo(sewageJobService.getNewJobNo(year, quarter)); + job.setStatus(1); + job.setDescription("贵阳市" + year + "年第" + quarter + "季度污水监测任务"); + job.setLauncheOrgId("0"); + job.setStartDate(startDate.atStartOfDay()); + job.setExpirationDate(endDate.atStartOfDay()); + + boolean saved = sewageJobService.save(job); + if (saved) { + log.info(">>> 创建成功:" + "year" + "年第" + quarter + "季度"); + } else { + log.info(">>> 创建失败:" + "year" + "年第" + quarter + "季度"); + } + } + + log.info(">>> 每天凌晨定时任务执行完毕"); + } + + /** + * 根据当前月份获取季度(1~4) + */ + private int getCurrentQuarter() { + int month = LocalDate.now().getMonthValue(); + return (month - 1) / 3 + 1; + } +}