feat:重构"食堂菜品购买统计"+"食堂菜品部门统计"菜单统计预约核销订单+线下购买订单
feat:增加机器的每日菜谱库存扣款 feat:增加订单"退回到核销前"按钮
This commit is contained in:
1 parent
8d31f20cec
commit
d1cd86dfa4
92 files changed
+9628
-454
No files matched your search
+96
@@ -0,0 +1,96 @@
|
||||
package com.bsd.pjcloud.daemon.quartz.config;
|
||||
|
||||
import com.bsd.pjcloud.daemon.quartz.job.ThirdPaymentDispatchJob;
|
||||
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPaymentTaskTypeEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.*;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码托管的支付任务触发器,不受后台 {@code sys_job} 增删、暂停或频率修改影响。
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class ThirdPaymentQuartzInitializer implements InitializingBean {
|
||||
/** 支付固定任务所属 Quartz 分组。 */
|
||||
private static final String GROUP = "THIRD_PAYMENT_SYSTEM";
|
||||
/** 每轮默认最大领取数量。 */
|
||||
private static final int DEFAULT_BATCH_SIZE = 100;
|
||||
/** Quartz调度器。 */
|
||||
private final Scheduler scheduler;
|
||||
|
||||
/**
|
||||
* 支付任务与固定触发间隔;同一 JobKey 在 Quartz 集群中同一时刻只会由一个节点执行。
|
||||
*/
|
||||
private static final List<PaymentSchedule> SCHEDULES = List.of(
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.PAY_CREATE, 5),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.PAY_QUERY, 5),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.REFUND_CREATE, 10),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.REFUND_QUERY, 10),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.FZ_CREATE, 10),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.FZ_SETTLE_CONFIRM, 10),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.FZ_ROYALTY, 10),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.FZ_ROYALTY_QUERY, 10),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.FZ_QUERY, 10),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.FZ_REFUND_QUERY, 10),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.BIZ_CALLBACK, 10),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.AGGREGATE_SESSION, 5),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.PAY_CLOSE, 300),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.AGGREGATE_CLOSE, 300),
|
||||
new PaymentSchedule(ThirdPaymentTaskTypeEnum.RECONCILE, 300));
|
||||
|
||||
/**
|
||||
* 幂等注册固定任务。多副本同时启动时,Quartz数据库唯一键裁决创建竞争。
|
||||
* @throws SchedulerException Quartz持久化失败
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws SchedulerException {
|
||||
for (PaymentSchedule schedule : SCHEDULES) {
|
||||
register(schedule);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或覆盖一个代码托管 Job,并重置其固定频率。
|
||||
* @param schedule 任务类型和秒级间隔
|
||||
* @throws SchedulerException Quartz持久化失败
|
||||
*/
|
||||
private void register(PaymentSchedule schedule) throws SchedulerException {
|
||||
String name = "third-payment-" + schedule.taskType().getCode().toLowerCase().replace('_', '-');
|
||||
JobKey jobKey = JobKey.jobKey(name, GROUP);
|
||||
TriggerKey triggerKey = TriggerKey.triggerKey(name + "-trigger", GROUP);
|
||||
JobDetail job = JobBuilder.newJob(ThirdPaymentDispatchJob.class).withIdentity(jobKey)
|
||||
.usingJobData(ThirdPaymentDispatchJob.TASK_TYPE_KEY, schedule.taskType().getCode())
|
||||
.usingJobData(ThirdPaymentDispatchJob.BATCH_SIZE_KEY, DEFAULT_BATCH_SIZE)
|
||||
.requestRecovery(true).storeDurably(true).build();
|
||||
SimpleScheduleBuilder simpleSchedule = SimpleScheduleBuilder.simpleSchedule()
|
||||
.withIntervalInSeconds(schedule.intervalSeconds()).repeatForever()
|
||||
.withMisfireHandlingInstructionNextWithRemainingCount();
|
||||
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobKey)
|
||||
.startNow().withSchedule(simpleSchedule).build();
|
||||
try {
|
||||
scheduler.addJob(job, true);
|
||||
if (scheduler.checkExists(triggerKey)) {
|
||||
scheduler.rescheduleJob(triggerKey, trigger);
|
||||
} else {
|
||||
scheduler.scheduleJob(trigger);
|
||||
}
|
||||
} catch (ObjectAlreadyExistsException exception) {
|
||||
// 多节点同时首次部署时另一个实例已完成注册,读取共享QRTZ表即可。
|
||||
log.info("支付Quartz任务已由其他集群节点初始化,jobKey:{}", jobKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 固定任务频率配置。
|
||||
* @param taskType 支付任务类型
|
||||
* @param intervalSeconds 触发间隔,单位秒
|
||||
*/
|
||||
private record PaymentSchedule(ThirdPaymentTaskTypeEnum taskType, int intervalSeconds) {
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.bsd.pjcloud.daemon.quartz.job;
|
||||
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.thirdparty.api.feign.RemoteThirdPaymentTaskService;
|
||||
import com.bsd.pjcloud.thirdparty.dto.pay.ThirdPaymentTaskDispatchDto;
|
||||
import com.bsd.pjcloud.thirdparty.vo.pay.ThirdPaymentTaskDispatchVo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 支付任务固定 Quartz Job,只同步确认 thirdparty 接受分发,不直接访问支付 SDK。
|
||||
*/
|
||||
@Slf4j
|
||||
@DisallowConcurrentExecution
|
||||
public class ThirdPaymentDispatchJob implements Job {
|
||||
/** JobDataMap中的任务类型键。 */
|
||||
public static final String TASK_TYPE_KEY = "taskType";
|
||||
/** JobDataMap中的批量大小键。 */
|
||||
public static final String BATCH_SIZE_KEY = "batchSize";
|
||||
/** thirdparty内部任务分发 Feign 客户端,由 Quartz JobFactory 注入。 */
|
||||
@Autowired
|
||||
private RemoteThirdPaymentTaskService paymentTaskService;
|
||||
|
||||
/**
|
||||
* 调用服务发现后的 thirdparty 内部接口;业务响应失败会让 Quartz 记录本次执行失败。
|
||||
* @param context Quartz触发上下文
|
||||
* @throws JobExecutionException thirdparty未接受或调用异常
|
||||
*/
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
String taskType = context.getMergedJobDataMap().getString(TASK_TYPE_KEY);
|
||||
int batchSize = context.getMergedJobDataMap().getIntValue(BATCH_SIZE_KEY);
|
||||
ThirdPaymentTaskDispatchDto dto = new ThirdPaymentTaskDispatchDto();
|
||||
dto.setTaskType(taskType);
|
||||
dto.setBatchSize(batchSize);
|
||||
dto.setTriggerSource("QUARTZ");
|
||||
dto.setTriggerTime(LocalDateTime.now());
|
||||
try {
|
||||
R<ThirdPaymentTaskDispatchVo> result = paymentTaskService.dispatch(dto);
|
||||
if (result == null || !result.isOk() || result.getData() == null) {
|
||||
throw new JobExecutionException("thirdparty未接受支付任务分发:" + taskType);
|
||||
}
|
||||
ThirdPaymentTaskDispatchVo data = result.getData();
|
||||
if (data.isBusy() || data.getBacklogCount() > 0) {
|
||||
log.warn("支付任务分发存在积压,taskType:{},claimed:{},backlog:{},busy:{}",
|
||||
taskType, data.getClaimedCount(), data.getBacklogCount(), data.isBusy());
|
||||
}
|
||||
} catch (JobExecutionException exception) {
|
||||
throw exception;
|
||||
} catch (Exception exception) {
|
||||
throw new JobExecutionException("调用thirdparty支付任务分发失败:" + taskType, exception, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,14 @@ spring:
|
||||
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore # 数据库存储
|
||||
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 数据库代理
|
||||
tablePrefix: QRTZ_ # 表前缀
|
||||
isClustered: false # 集群
|
||||
clusterCheckinInterval: 30000 # 集群检查间隔
|
||||
isClustered: true # 支付任务只由Quartz集群触发,生产实例必须共享同一套QRTZ表
|
||||
clusterCheckinInterval: 10000 # 集群心跳,节点失联后由存活实例接管
|
||||
misfireThreshold: 60000 # 允许一分钟短暂抖动,恢复后按misfire策略继续触发
|
||||
acquireTriggersWithinLock: true # 多节点批量获取Trigger时在数据库锁内完成
|
||||
useProperties: false
|
||||
threadPool:
|
||||
class: org.quartz.simpl.SimpleThreadPool # 线程池
|
||||
threadCount: 3 # 线程数量
|
||||
threadCount: 8 # 支付固定触发器与既有通用任务共享,外部支付调用不在Quartz线程执行
|
||||
threadPriority: 5 # 线程优先级
|
||||
threadsInheritContextClassLoaderOfInitializingThread: true # 是否继承类加载器
|
||||
job-store-type: jdbc # 持久化到数据库
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.bsd.pjcloud.daemon.quartz.config;
|
||||
|
||||
import com.bsd.pjcloud.daemon.quartz.job.ThirdPaymentDispatchJob;
|
||||
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPaymentTaskTypeEnum;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SimpleTrigger;
|
||||
import org.quartz.Trigger;
|
||||
import org.quartz.TriggerKey;
|
||||
import org.quartz.impl.StdSchedulerFactory;
|
||||
import org.quartz.impl.matchers.GroupMatcher;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* 支付固定 Quartz 触发器初始化测试,验证服务重复启动不会产生重复调度记录。
|
||||
*/
|
||||
class ThirdPaymentQuartzInitializerTest {
|
||||
|
||||
/**
|
||||
* 使用内存 JobStore 连续初始化两次,确认 Job、Trigger、任务参数和固定频率均保持稳定。
|
||||
*
|
||||
* @throws Exception Quartz 调度器创建、查询或关闭失败
|
||||
*/
|
||||
@Test
|
||||
void shouldRegisterManagedPaymentSchedulesIdempotently() throws Exception {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("org.quartz.scheduler.instanceName", "thirdPaymentInitializerTest");
|
||||
properties.setProperty("org.quartz.threadPool.threadCount", "1");
|
||||
properties.setProperty("org.quartz.jobStore.class", "org.quartz.simpl.RAMJobStore");
|
||||
Scheduler scheduler = new StdSchedulerFactory(properties).getScheduler();
|
||||
try {
|
||||
ThirdPaymentQuartzInitializer initializer = new ThirdPaymentQuartzInitializer(scheduler);
|
||||
initializer.afterPropertiesSet();
|
||||
initializer.afterPropertiesSet();
|
||||
|
||||
String group = "THIRD_PAYMENT_SYSTEM";
|
||||
assertThat(scheduler.getJobKeys(GroupMatcher.jobGroupEquals(group))).hasSize(15);
|
||||
assertThat(scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(group))).hasSize(15);
|
||||
|
||||
JobKey jobKey = JobKey.jobKey("third-payment-pay-create", group);
|
||||
assertThat(scheduler.getJobDetail(jobKey).getJobClass()).isEqualTo(ThirdPaymentDispatchJob.class);
|
||||
assertThat(scheduler.getJobDetail(jobKey).getJobDataMap().getString(ThirdPaymentDispatchJob.TASK_TYPE_KEY))
|
||||
.isEqualTo(ThirdPaymentTaskTypeEnum.PAY_CREATE.getCode());
|
||||
assertThat(scheduler.getJobDetail(jobKey).getJobDataMap().getInt(ThirdPaymentDispatchJob.BATCH_SIZE_KEY))
|
||||
.isEqualTo(100);
|
||||
|
||||
TriggerKey triggerKey = TriggerKey.triggerKey("third-payment-pay-create-trigger", group);
|
||||
Trigger trigger = scheduler.getTrigger(triggerKey);
|
||||
assertThat(trigger).isInstanceOf(SimpleTrigger.class);
|
||||
assertThat(((SimpleTrigger) trigger).getRepeatInterval()).isEqualTo(5_000L);
|
||||
} finally {
|
||||
scheduler.shutdown(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user