From 4ad72b942285ce6721a0d5703f5b8af85084e919 Mon Sep 17 00:00:00 2001 From: fengyuan_yang <1976974459@qq.com> Date: Thu, 16 Jul 2026 17:33:31 +0800 Subject: [PATCH] =?UTF-8?q?2026-07-16=20RoHS=E5=A2=9E=E5=8A=A0=E3=80=90?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=A4=B1=E6=95=88=E6=97=A5=E6=9C=9F=E3=80=91?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rohs/controller/RohsController.java | 22 +++ .../modules/rohs/entity/RohsEntity.java | 5 + .../modules/rohs/mapper/RohsMapper.java | 2 + .../modules/rohs/service/RohsService.java | 10 ++ .../rohs/service/impl/RohsServiceImpl.java | 131 ++++++++++++++++++ .../modules/rohs/task/RohsExpiryFlagTask.java | 30 ++++ src/main/resources/application-dev.yml | 1 + src/main/resources/mapper/rohs/RohsMapper.xml | 23 ++- 8 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/spring/modules/rohs/task/RohsExpiryFlagTask.java diff --git a/src/main/java/com/spring/modules/rohs/controller/RohsController.java b/src/main/java/com/spring/modules/rohs/controller/RohsController.java index da7abec5..52688469 100644 --- a/src/main/java/com/spring/modules/rohs/controller/RohsController.java +++ b/src/main/java/com/spring/modules/rohs/controller/RohsController.java @@ -154,6 +154,9 @@ public class RohsController { if (StringUtils.isBlank(rohs.getRejectFlag())) { rohs.setRejectFlag("N"); } + if (StringUtils.isBlank(rohs.getIsExpiring())) { + rohs.setIsExpiring("N"); + } sanitizeHsfStandard(rohs); rohsService.save(rohs); rohsService.saveOrUpdateMaterials(rohs); @@ -182,6 +185,9 @@ public class RohsController { if (StringUtils.isBlank(rohs.getStatus())) { rohs.setStatus(exists.getStatus()); } + if (StringUtils.isBlank(rohs.getIsExpiring())) { + rohs.setIsExpiring(exists.getIsExpiring()); + } rohs.setUpdateDate(new Date()); if (StringUtils.isBlank(rohs.getUpdateBy())) { rohs.setUpdateBy(currentUserName); @@ -271,6 +277,22 @@ public class RohsController { return R.ok(); } + /** + * 已完成且即将失效单据更新失效日期 + */ + @PostMapping("/updateExpiryInfo") + public R updateExpiryInfo(@RequestBody RohsEntity rohs) { + if (StringUtils.isBlank(rohs.getSite()) || StringUtils.isBlank(rohs.getReferenceNo())) { + return R.error("工厂(site)和序列号(referenceNo)不能为空"); + } + try { + RohsEntity latest = rohsService.updateCompletedExpiryInfo(rohs, getCurrentUserName()); + return R.ok().put("data", latest); + } catch (RuntimeException e) { + return R.error(e.getMessage()); + } + } + /** * 下达 */ diff --git a/src/main/java/com/spring/modules/rohs/entity/RohsEntity.java b/src/main/java/com/spring/modules/rohs/entity/RohsEntity.java index f674b0ab..71f20dd7 100644 --- a/src/main/java/com/spring/modules/rohs/entity/RohsEntity.java +++ b/src/main/java/com/spring/modules/rohs/entity/RohsEntity.java @@ -194,6 +194,11 @@ public class RohsEntity implements Serializable { */ private String rohsStatus; + /** + * 即将失效标识(Y/N) + */ + private String isExpiring; + /** * 报告有效期 */ diff --git a/src/main/java/com/spring/modules/rohs/mapper/RohsMapper.java b/src/main/java/com/spring/modules/rohs/mapper/RohsMapper.java index b5d500a6..58100b1e 100644 --- a/src/main/java/com/spring/modules/rohs/mapper/RohsMapper.java +++ b/src/main/java/com/spring/modules/rohs/mapper/RohsMapper.java @@ -31,4 +31,6 @@ public interface RohsMapper extends BaseMapper { IPage> queryProjectMaterialPage(IPage page, @Param("params") Map params); List queryMaterialList(@Param("site") String site, @Param("referenceNo") String referenceNo); + + int refreshAboutToExpireFlag(); } diff --git a/src/main/java/com/spring/modules/rohs/service/RohsService.java b/src/main/java/com/spring/modules/rohs/service/RohsService.java index 2dab3b30..f6598bd5 100644 --- a/src/main/java/com/spring/modules/rohs/service/RohsService.java +++ b/src/main/java/com/spring/modules/rohs/service/RohsService.java @@ -78,6 +78,16 @@ public interface RohsService extends IService { */ void removeMaterials(String site, String referenceNo); + /** + * 已完成且即将失效单据更新失效日期相关字段 + */ + RohsEntity updateCompletedExpiryInfo(RohsEntity rohs, String userName); + + /** + * 刷新即将失效标识 + */ + int refreshAboutToExpireFlag(); + /** * 流程干预提交(节点更新/流程干预) */ diff --git a/src/main/java/com/spring/modules/rohs/service/impl/RohsServiceImpl.java b/src/main/java/com/spring/modules/rohs/service/impl/RohsServiceImpl.java index 5ba4a407..e35a7f79 100644 --- a/src/main/java/com/spring/modules/rohs/service/impl/RohsServiceImpl.java +++ b/src/main/java/com/spring/modules/rohs/service/impl/RohsServiceImpl.java @@ -39,6 +39,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDate; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -488,6 +490,60 @@ public class RohsServiceImpl extends ServiceImpl impleme rohsMaterialService.remove(removeWrapper); } + @Override + @Transactional + public RohsEntity updateCompletedExpiryInfo(RohsEntity rohs, String userName) { + if (rohs == null || StringUtils.isBlank(rohs.getSite()) || StringUtils.isBlank(rohs.getReferenceNo())) { + throw new RuntimeException("工厂(site)和序列号(referenceNo)不能为空"); + } + RohsEntity exists = this.getDetail(rohs.getSite(), rohs.getReferenceNo()); + if (exists == null) { + throw new RuntimeException("未找到对应RoHs单据"); + } + if (!"已完成".equals(exists.getStatus())) { + throw new RuntimeException("仅已完成状态的单据允许更新失效日期"); + } + if (!"Y".equalsIgnoreCase(StringUtils.trimToEmpty(exists.getIsExpiring()))) { + throw new RuntimeException("当前单据非即将失效状态,不允许更新失效日期"); + } + if (rohs.getExpiredDate() == null) { + throw new RuntimeException("报告日期不能为空"); + } + Integer validUntilValue = rohs.getValidUntilValue(); + if (validUntilValue == null || validUntilValue <= 0) { + throw new RuntimeException("有效期数值必须大于0"); + } + String validUntil = StringUtils.trimToEmpty(rohs.getValidUntil()); + if (StringUtils.isBlank(validUntil)) { + throw new RuntimeException("有效期单位不能为空"); + } + + Date expiryDate = calculateExpiryDateFromRule(rohs.getExpiredDate(), validUntilValue, validUntil); + if (expiryDate == null) { + throw new RuntimeException("有效期单位不合法,无法计算失效日期"); + } + + RohsEntity updateEntity = new RohsEntity(); + updateEntity.setExpiredDate(rohs.getExpiredDate()); + updateEntity.setValidUntilValue(validUntilValue); + updateEntity.setValidUntil(validUntil); + updateEntity.setExpiryDate(expiryDate); + updateEntity.setIsExpiring(isAboutToExpire(expiryDate) ? "Y" : "N"); + updateEntity.setUpdateDate(new Date()); + updateEntity.setUpdateBy(StringUtils.isNotBlank(userName) ? userName : exists.getUpdateBy()); + + QueryWrapper updateWrapper = new QueryWrapper<>(); + updateWrapper.eq("site", rohs.getSite()).eq("reference_no", rohs.getReferenceNo()); + this.update(updateEntity, updateWrapper); + return this.getDetail(rohs.getSite(), rohs.getReferenceNo()); + } + + @Override + @Transactional + public int refreshAboutToExpireFlag() { + return this.baseMapper.refreshAboutToExpireFlag(); + } + /** * 流程干预提交(节点更新/流程干预) */ @@ -746,4 +802,79 @@ public class RohsServiceImpl extends ServiceImpl impleme rohs.setHsfStandard(""); } } + + private Date calculateExpiryDateFromRule(Date expiredDate, Integer validUntilValue, String validUntilUnit) { + if (expiredDate == null || validUntilValue == null || validUntilValue <= 0) { + return null; + } + String unitType = normalizeValidUntilUnitType(validUntilUnit); + if (StringUtils.isBlank(unitType)) { + return null; + } + LocalDate baseDate = toLocalDate(expiredDate); + if (baseDate == null) { + return null; + } + LocalDate resultDate; + switch (unitType) { + case "day": + resultDate = baseDate.plusDays(validUntilValue.longValue()); + break; + case "week": + resultDate = baseDate.plusWeeks(validUntilValue.longValue()); + break; + case "month": + resultDate = baseDate.plusMonths(validUntilValue.longValue()); + break; + case "year": + resultDate = baseDate.plusYears(validUntilValue.longValue()); + break; + default: + return null; + } + return java.sql.Date.valueOf(resultDate); + } + + private String normalizeValidUntilUnitType(String unitValue) { + String text = StringUtils.trimToEmpty(unitValue).toLowerCase(); + if (StringUtils.isBlank(text)) { + return ""; + } + if ("d".equals(text) || "day".equals(text) || "days".equals(text) + || text.contains("日") || text.contains("天")) { + return "day"; + } + if ("w".equals(text) || "week".equals(text) || "weeks".equals(text) || text.contains("周")) { + return "week"; + } + if ("m".equals(text) || "month".equals(text) || "months".equals(text) || text.contains("月")) { + return "month"; + } + if ("y".equals(text) || "year".equals(text) || "years".equals(text) || text.contains("年")) { + return "year"; + } + return ""; + } + + private boolean isAboutToExpire(Date expiryDate) { + LocalDate expiryLocalDate = toLocalDate(expiryDate); + if (expiryLocalDate == null) { + return false; + } + LocalDate limitDate = LocalDate.now().plusMonths(1); + return !expiryLocalDate.isAfter(limitDate); + } + + private LocalDate toLocalDate(Date date) { + if (date == null) { + return null; + } + // java.sql.Date may throw UnsupportedOperationException on toInstant() + if (date instanceof java.sql.Date) { + return ((java.sql.Date) date).toLocalDate(); + } + return java.time.Instant.ofEpochMilli(date.getTime()) + .atZone(ZoneId.systemDefault()) + .toLocalDate(); + } } diff --git a/src/main/java/com/spring/modules/rohs/task/RohsExpiryFlagTask.java b/src/main/java/com/spring/modules/rohs/task/RohsExpiryFlagTask.java new file mode 100644 index 00000000..8aac7d7e --- /dev/null +++ b/src/main/java/com/spring/modules/rohs/task/RohsExpiryFlagTask.java @@ -0,0 +1,30 @@ +package com.spring.modules.rohs.task; + +import com.spring.modules.rohs.service.RohsService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * 刷新RoHS即将失效标识定时任务 + */ +@Component +public class RohsExpiryFlagTask { + + private static final Logger logger = LoggerFactory.getLogger(RohsExpiryFlagTask.class); + + @Autowired + private RohsService rohsService; + + @Scheduled(cron = "${task.data.refreshRohsAboutToExpireFlag}") + public void refreshAboutToExpireFlag() { + try { + int affectedRows = rohsService.refreshAboutToExpireFlag(); + logger.info("RoHS即将失效标识刷新完成,更新记录数={}", affectedRows); + } catch (Exception e) { + logger.error("RoHS即将失效标识刷新失败,error={}", e.getMessage(), e); + } + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index bb5f0e27..d647511d 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -104,6 +104,7 @@ task: sync_part_catalog_to_plm: 0 0 0 29 2 ? # 每1分钟执行 syncInventoryPartToPlm: 0 */10 * * * ? # 每10分钟执行 refreshLdapAccountToRedis: 0 0 0 * * ? # + refreshRohsAboutToExpireFlag: 0 0 1 * * ? # 每天凌晨1点刷新RoHS即将失效标识 flag: false pool: schedulerSize: 10 diff --git a/src/main/resources/mapper/rohs/RohsMapper.xml b/src/main/resources/mapper/rohs/RohsMapper.xml index c0567d31..e97eec1f 100644 --- a/src/main/resources/mapper/rohs/RohsMapper.xml +++ b/src/main/resources/mapper/rohs/RohsMapper.xml @@ -38,6 +38,7 @@ + @@ -94,7 +95,7 @@ - a.site, a.reference_no, a.applicant, a.application_date, a.process, a.bu_no, a.pm, a.planned_mass_production_date, a.color, a.vendor_code, a.vendor_material_code, a.material_classify, a.other_material_classify, a.material_use_for, a.end_customer, a.project_id, a.is_macallan_material, a.need_create_number, a.npd_engineer, a.material_validity_time, a.material_validity_comments, a.need_deviation, a.technical_plan, a.wm_required_spec, a.is_fiber_material, a.material_thickness, a.buyer, a.expect_report_time, a.qualification_documents_needed, a.test_report_including_items, a.remark, a.status, a.sgs_report_number, a.rohs_status, a.expired_date, a.expiry_date, a.fiber_information, a.hsf_standard, a.hsf_approver, a.related_people, a.valid_until_value, a.valid_until, a.is_meet_rohs_requirement, a.is_ah_grade, a.hsf_supplier_classification, a.material_desc, a.npd_remark, a.is_same_material_diff_size, a.ifs_part_no, a.comm_group1, a.comm_group2, a.comm_group3, a.create_date, a.create_by, a.update_date, a.update_by, a.step_id, a.reject_flag, a.reject_step_id + a.site, a.reference_no, a.applicant, a.application_date, a.process, a.bu_no, a.pm, a.planned_mass_production_date, a.color, a.vendor_code, a.vendor_material_code, a.material_classify, a.other_material_classify, a.material_use_for, a.end_customer, a.project_id, a.is_macallan_material, a.need_create_number, a.npd_engineer, a.material_validity_time, a.material_validity_comments, a.need_deviation, a.technical_plan, a.wm_required_spec, a.is_fiber_material, a.material_thickness, a.buyer, a.expect_report_time, a.qualification_documents_needed, a.test_report_including_items, a.remark, a.status, a.sgs_report_number, a.rohs_status, a.is_expiring, a.expired_date, a.expiry_date, a.fiber_information, a.hsf_standard, a.hsf_approver, a.related_people, a.valid_until_value, a.valid_until, a.is_meet_rohs_requirement, a.is_ah_grade, a.hsf_supplier_classification, a.material_desc, a.npd_remark, a.is_same_material_diff_size, a.ifs_part_no, a.comm_group1, a.comm_group2, a.comm_group3, a.create_date, a.create_by, a.update_date, a.update_by, a.step_id, a.reject_flag, a.reject_step_id + + update plm_rohs + set is_expiring = + case + when status = '已完成' + and expiry_date is not null + and cast(expiry_date as date) dateadd(month, 1, cast(getdate() as date)) + then 'Y' + else 'N' + end + where isnull(is_expiring, 'N') ]]> + case + when status = '已完成' + and expiry_date is not null + and cast(expiry_date as date) dateadd(month, 1, cast(getdate() as date)) + then 'Y' + else 'N' + end + +