From 06294d636ab225ddb6356f21e7f761f32ff1f8e3 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Wed, 9 Sep 2026 17:56:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=87=AA=E5=8A=A8=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E4=BF=AE=E6=94=B9=E4=BA=A7=E5=93=81=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/ruoyi/RuoYiApplication.java | 2 + .../price/PriceStatusController.java | 39 +++++++++++++ .../main/resources/i18n/messages.properties | 5 +- .../quartz/controller/SysJobController.java | 8 +++ .../price/task/BmsStatusRefreshTask.java | 58 +++++++++++++++++++ bms-back/sql/price/product_status_alter.sql | 40 +++++++++++++ bms-front/src/api/price/status.js | 15 +++++ bms-front/src/views/monitor/job/index.vue | 9 ++- .../src/views/price/basic/product/index.vue | 17 ++++++ 9 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/PriceStatusController.java create mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/price/task/BmsStatusRefreshTask.java create mode 100644 bms-back/sql/price/product_status_alter.sql create mode 100644 bms-front/src/api/price/status.js diff --git a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java index 16a4676d..bb88734d 100644 --- a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java +++ b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java @@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; /** * 启动程序 @@ -11,6 +12,7 @@ import org.springframework.scheduling.annotation.EnableAsync; * @author ruoyi */ @EnableAsync +@EnableScheduling @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) public class RuoYiApplication { diff --git a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/PriceStatusController.java b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/PriceStatusController.java new file mode 100644 index 00000000..c3a05a8c --- /dev/null +++ b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/price/PriceStatusController.java @@ -0,0 +1,39 @@ +package com.ruoyi.web.controller.price; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.price.task.BmsStatusRefreshTask; + +/** + * 状态自动刷新 - 手动触发入口 + * + *
日常由 BmsStatusRefreshTask 每天 02:00 跑,特殊情况可手动触发。 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/price/status") +public class PriceStatusController extends BaseController +{ + @Autowired + private BmsStatusRefreshTask refreshTask; + + /** + * 立即刷新 bms_product / bms_product_country 的 status 字段 + */ + @PreAuthorize("@ss.hasPermi('price:product:edit')") + @PostMapping("/refresh") + public AjaxResult refresh() + { + int[] counts = refreshTask.refreshNow(); + String msg = MessageUtils.message("price.status.refresh.success", + String.valueOf(counts[0]), String.valueOf(counts[1])); + return success(msg); + } +} \ No newline at end of file diff --git a/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties b/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties index c4863965..3479f047 100644 --- a/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -115,4 +115,7 @@ price.quote.bizDateInvalid=业务日期格式必须为yyyy-MM-dd price.quote.fixedPackageNoDetails=套餐固定价格没有明细,使用产品默认价格 price.quote.noCustomerPackageOverride=没有客户套餐覆盖,使用产品默认运费/挂号费 price.quote.quoteIdRequired=报价ID为空 -price.quote.hitCustomerFixedPackageDetail=命中客户套餐固定价格明细 \ No newline at end of file +price.quote.hitCustomerFixedPackageDetail=命中客户套餐固定价格明细 + +# ---- 状态自动刷新 ---- +price.status.refresh.success=状态刷新完成:bms_product 更新 {0} 行,bms_product_country 更新 {1} 行 \ No newline at end of file diff --git a/bms-back/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java b/bms-back/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java index abe6b2de..b9f7c20c 100644 --- a/bms-back/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java +++ b/bms-back/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java @@ -154,7 +154,15 @@ public class SysJobController extends BaseController @PutMapping("/changeStatus") public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException { + if (job == null || job.getJobId() == null) + { + return error("任务ID不能为空,请刷新列表后重试"); + } SysJob newJob = jobService.selectJobById(job.getJobId()); + if (newJob == null) + { + return error("任务不存在或已被删除,请刷新列表后重试"); + } newJob.setStatus(job.getStatus()); return toAjax(jobService.changeStatus(newJob)); } diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/task/BmsStatusRefreshTask.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/task/BmsStatusRefreshTask.java new file mode 100644 index 00000000..cebd0e94 --- /dev/null +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/task/BmsStatusRefreshTask.java @@ -0,0 +1,58 @@ +package com.ruoyi.price.task; + +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; +import com.ruoyi.price.mapper.BmsProductCountryMapper; +import com.ruoyi.price.mapper.BmsProductMapper; + +/** + * bms_product / bms_product_country 状态自动刷新 + * + *
每天 02:00 根据 effective_date / expire_date 与当天日期,回填 status: + *
对应字典 bms_product_effect_status 的 value=ACTIVE/PENDING/EXPIRED。 + * 实际存储使用 0/1/2 与列 comment 保持一致;前端 dict-tag 需做 value 映射或调整 dict_value。 + * + *
手动触发:调用 refreshNow()(运维/测试用,详见 StatusRefreshController)。
+ *
+ * @author ruoyi
+ */
+@Component
+public class BmsStatusRefreshTask
+{
+ private static final Logger log = LoggerFactory.getLogger(BmsStatusRefreshTask.class);
+
+ @Autowired
+ private BmsProductMapper productMapper;
+
+ @Autowired
+ private BmsProductCountryMapper productCountryMapper;
+
+ /** 每天 02:00 跑(cron: sec min hour DOM month DOW) */
+ @Scheduled(cron = "0 0 2 * * ?")
+ public void scheduled()
+ {
+ refreshNow();
+ }
+
+ /**
+ * 立即刷新两表状态。返回 [product 影响行数, country 影响行数]。
+ * 异常时打印 ERROR 并原样抛出,便于调用方(手动触发接口)回滚事务。
+ */
+ public int[] refreshNow()
+ {
+ int productCount = productMapper.refreshStatusByDates();
+ int countryCount = productCountryMapper.refreshStatusByDates();
+ log.info("BmsStatusRefreshTask done: bms_product={}, bms_product_country={}",
+ productCount, countryCount);
+ return new int[] { productCount, countryCount };
+ }
+}
\ No newline at end of file
diff --git a/bms-back/sql/price/product_status_alter.sql b/bms-back/sql/price/product_status_alter.sql
new file mode 100644
index 00000000..bee4e381
--- /dev/null
+++ b/bms-back/sql/price/product_status_alter.sql
@@ -0,0 +1,40 @@
+-- ============================================================
+-- bms_product / bms_product_country 自动状态回填
+-- ============================================================
+-- 背景:
+-- bms_product.status char(1) DEFAULT '0' COMMENT '0待生效 1已生效 2已失效'
+-- bms_product_country.status char(1) DEFAULT '0' COMMENT '0待生效 1已生效 2已失效'
+-- 字典 bms_product_effect_status value=ACTIVE/PENDING/EXPIRED
+-- 自动刷新:
+-- BmsStatusRefreshTask 每天 02:00 调用两表 Mapper.refreshStatusByDates()
+-- 即本文件末尾的回填 SQL(每日定时跑同样效果)
+-- 现状验证:
+-- bms_product.status 当前值仅 '0' / '1'('2' = EXPIRED 尚未触发,因今天 2026-09-09
+-- 还没到任一 expire_date);bms_product_country.status 同上
+-- ============================================================
+
+SET NAMES utf8mb4;
+
+-- ---------- 一次性回填(幂等:重跑结果一致) ----------
+-- bms_product:
+-- expire_date 非空且 <= 今天 → '2' 已失效
+-- effective_date 为空 / 未来 → '0' 待生效
+-- 其余(已生效且仍在有效期) → '1' 已生效
+UPDATE bms_product p
+SET p.status = (
+ CASE
+ WHEN p.expire_date IS NOT NULL AND p.expire_date <= CURDATE() THEN '2'
+ WHEN p.effective_date IS NULL OR p.effective_date > CURDATE() THEN '0'
+ ELSE '1'
+ END
+);
+
+-- bms_product_country:同上规则(多了 detail 级过期判断)
+UPDATE bms_product_country c
+SET c.status = (
+ CASE
+ WHEN c.expire_date IS NOT NULL AND c.expire_date <= CURDATE() THEN '2'
+ WHEN c.effective_date IS NULL OR c.effective_date > CURDATE() THEN '0'
+ ELSE '1'
+ END
+);
\ No newline at end of file
diff --git a/bms-front/src/api/price/status.js b/bms-front/src/api/price/status.js
new file mode 100644
index 00000000..a83a3423
--- /dev/null
+++ b/bms-front/src/api/price/status.js
@@ -0,0 +1,15 @@
+import request from '@/utils/request'
+
+/**
+ * 立即刷新 bms_product / bms_product_country 的 status 字段
+ *
+ * 日常由后端定时任务 BmsStatusRefreshTask 每天 02:00 自动跑,
+ * 此接口用于运维/测试手动触发。返回 success msg 形如:
+ * 「状态刷新完成:bms_product 更新 X 行,bms_product_country 更新 Y 行」
+ */
+export function refreshStatus() {
+ return request({
+ url: '/price/status/refresh',
+ method: 'post'
+ })
+}
\ No newline at end of file
diff --git a/bms-front/src/views/monitor/job/index.vue b/bms-front/src/views/monitor/job/index.vue
index 229236bb..0a05d309 100644
--- a/bms-front/src/views/monitor/job/index.vue
+++ b/bms-front/src/views/monitor/job/index.vue
@@ -63,6 +63,7 @@