手动触发更新逻辑,新增 stockInfo api 接口

This commit is contained in:
2025-11-15 14:54:46 +08:00
parent 6327874166
commit 4e94e5811d
8 changed files with 178 additions and 18 deletions

View File

@@ -255,11 +255,11 @@
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
</dependency><!-- https://mvnrepository.com/artifact/com.github.yulichang/mybatis-plus-join-boot-starter -->
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId>
<version>1.4.11</version>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId>
<version>1.5.4</version>
</dependency>
</dependencies>

View File

@@ -0,0 +1,38 @@
package link.at17.mid.tushare.api.common;
import java.util.List;
import java.util.Objects;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.github.yulichang.query.MPJLambdaQueryWrapper;
import link.at17.mid.tushare.data.models.StockInfo;
import link.at17.mid.tushare.data.service.StockInfoService;
import link.at17.mid.tushare.enums.ListStatus;
import link.at17.mid.tushare.enums.StockMarket;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequestMapping("/api/v1/common/stockInfo")
@Slf4j
public class StockInfoController {
@Autowired
StockInfoService stockInfoService;
@GetMapping("list")
private List<StockInfo> list(
@Param("listStatus") ListStatus listStatus,
@Param("stockMarket") StockMarket stockMarket) {
MPJLambdaQueryWrapper<StockInfo> ew = new MPJLambdaQueryWrapper<StockInfo>();
ew.setAlias("i");
ew.eq(Objects.nonNull(listStatus), StockInfo::getListStatus, listStatus);
ew.likeLeft(Objects.nonNull(stockMarket), StockInfo::getTsCode, stockMarket);
return stockInfoService.list(ew);
}
}

View File

@@ -15,7 +15,7 @@ public class StockCodeUtil {
* @return
* @throws UnsupportedOperationException
*/
public static String toStandardCode(String code) throws UnsupportedOperationException {
public static String eastmoneyToTushare(String code) throws UnsupportedOperationException {
if (StringUtils.isEmpty(code)) {
return code;
}

View File

@@ -178,6 +178,14 @@ public class R<T> implements Serializable {
}
/**
* 执行带返回值的方法后返回,该返回值会作为 R 的 data。发生异常时抛出 RException包裹异常信息
* @param supplier 带返回值的方法如 R.judgeThrow(() -> service.getString())
* @return
* @see {@link #judgeThrow(ThrowingSupplier)}
* @see {@link #judge(ThrowingSupplier)}
* @see {@link #judge(ThrowingRunnable)}
*/
public static R<?> judge(ThrowingSupplier<?> supplier) {
try {
return R.ok(supplier.get());
@@ -186,12 +194,49 @@ public class R<T> implements Serializable {
throw RException.badRequest(e.getMessage());
}
}
/**
* 执行无返回值的方法后返回,发生异常时,抛出 RException包裹异常信息
* @param throwingRunnable void 方法如 R.judgeThrow(() -> service.someMethod())
* @return
* @throws Exception
* @see {@link #judgeThrow(ThrowingSupplier)}
* @see {@link #judge(ThrowingSupplier)}
* @see {@link #judge(ThrowingRunnable)}
*/
public static R<?> judge(ThrowingRunnable throwingRunnable) {
try {
throwingRunnable.run();
return R.ok();
}
catch (Exception e) {
throw RException.badRequest(e.getMessage());
}
}
/**
* 执行带返回值的方法后返回,该返回值会作为 R 的 data。发生异常时抛出异常而非返回一个包含异常信息的 R
* @param supplier
* @return
* @throws Exception
*/
public static R<?> judgeThrow(ThrowingSupplier<?> supplier) throws Exception {
return R.ok(supplier.get());
}
/**
* 执行无返回值的方法后返回,发生异常时,抛出异常而非返回一个包含异常信息的 R
* @param throwingRunnable void 方法如 R.judgeThrow(() -> service.someMethod())
* @return
* @throws Exception
* @see {@link #judgeThrow(ThrowingSupplier)}
* @see {@link #judge(ThrowingSupplier)}
*/
public static R<?> judgeThrow(ThrowingRunnable throwingRunnable) throws Exception {
throwingRunnable.run();
return R.ok();
}
/**
* 根据布尔值判断返回<br>
* true 返回 R.ok(T okData)<br>
@@ -214,9 +259,14 @@ public class R<T> implements Serializable {
public static R<?> judge(boolean condition, String failedMessage) {
return R.judge(condition, null, failedMessage);
}
@FunctionalInterface
public static interface ThrowingSupplier<T> {
T get() throws Exception;
}
@FunctionalInterface
public interface ThrowingRunnable {
void run() throws Exception;
}
}

View File

@@ -9,6 +9,7 @@ import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
@@ -104,8 +105,8 @@ public class UpdatePlanService extends BaseServiceImpl<UpdatePlanDao, UpdatePlan
* @throws SchedulerException
*/
public void rescheduleTask(@Validated UpdatePlan updatePlan) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(updatePlan.getId().toString(), JOB_GROUP_NAME);
TriggerKey triggerKey = TriggerKey.triggerKey(updatePlan.getId().toString(), JOB_GROUP_NAME);
JobKey jobKey = getJobKey(updatePlan);
TriggerKey triggerKey = getTriggerKey(updatePlan);
deleteTask(updatePlan.getId());
@@ -139,6 +140,33 @@ public class UpdatePlanService extends BaseServiceImpl<UpdatePlanDao, UpdatePlan
});
}
/**
* 立即触发任务
* @param updatePlan
*/
public void triggerTask(@Valid UpdatePlan updatePlan) throws SchedulerException {
JobKey jobKey = getJobKey(updatePlan);
for (JobExecutionContext context : scheduler.getCurrentlyExecutingJobs()) {
if (context.getJobDetail().getKey().equals(jobKey)) {
throw new IllegalStateException("已有同名任务在运行中");
}
}
if (scheduler.getJobDetail(jobKey) == null) {
throw new SchedulerException("指定的任务不存在");
}
scheduler.triggerJob(jobKey);
}
public void interruptTask(@Valid UpdatePlan updatePlan) throws SchedulerException {
JobKey jobKey = getJobKey(updatePlan);
for (JobExecutionContext context : scheduler.getCurrentlyExecutingJobs()) {
if (context.getJobDetail().getKey().equals(jobKey)) {
scheduler.interrupt(jobKey);
}
}
throw new SchedulerException("任务未在执行");
}
/**
* 执行方法
* @param updatePlan
@@ -158,5 +186,22 @@ public class UpdatePlanService extends BaseServiceImpl<UpdatePlanDao, UpdatePlan
public List<UpdatePlan> list() {
return list(new LambdaQueryWrapper<UpdatePlan>().orderByAsc(UpdatePlan::getId));
}
/**
* 根据 UpdatePlan 生成 JobKey
* @param updatePlan
* @return
*/
private JobKey getJobKey(UpdatePlan updatePlan) {
return JobKey.jobKey(updatePlan.getId().toString(), JOB_GROUP_NAME);
}
/**
* 根据 UpdatePlan 生成 TriggerKey
* @param updatePlan
* @return
*/
private TriggerKey getTriggerKey(UpdatePlan updatePlan) {
return TriggerKey.triggerKey(updatePlan.getId().toString(), JOB_GROUP_NAME);
}
}

View File

@@ -2,9 +2,12 @@ package link.at17.mid.tushare.task.job;
import java.lang.reflect.InvocationTargetException;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.InterruptableJob;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.UnableToInterruptJobException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
@@ -17,7 +20,8 @@ import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
@Validated
public class UpdatePlanJob implements Job {
@DisallowConcurrentExecution
public class UpdatePlanJob implements Job, InterruptableJob {
@Autowired
private UpdatePlanService updatePlanService;
@@ -58,4 +62,10 @@ public class UpdatePlanJob implements Job {
}
}
@Override
public void interrupt() throws UnableToInterruptJobException {
// TODO Auto-generated method stub
}
}

View File

@@ -8,7 +8,6 @@ import org.springframework.stereotype.Controller;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -95,11 +94,11 @@ public class UpdatePlanController extends BaseController {
return R.judge(updatePlanService.update(new UpdateWrapper<UpdatePlan>().eq(idField, id).set(dbField, value)));
}
@GetMapping("/execute")
@PostMapping("/manualTrigger")
@ResponseBody
public void execute(Integer id) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException {
public R<?> manualTrigger(Integer id) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException {
UpdatePlan updatePlan = updatePlanService.getById(id);
updatePlanService.execute(updatePlan);
return R.judge(() -> updatePlanService.triggerTask(updatePlan));
}
}

View File

@@ -97,6 +97,8 @@ td[handler] {cursor: move}
</script>
<script type="text/html" id="barDemo">
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-xs" lay-event="trigger">触发</a>
<a class="layui-btn layui-btn-xs" lay-event="interrupt">停止</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
</div>
@@ -147,7 +149,7 @@ td[handler] {cursor: move}
d.valid ? '<font color="green">有效</font>' : '<font color="red">无效</font>' +
'</span>';
}}
,{fixed: 'right', title:'操作', width: 125, minWidth: 125, toolbar: '#barDemo'}
,{fixed: 'right', title:'操作', minWidth: 125, toolbar: '#barDemo'}
]]
,done: function(){
var id = this.id;
@@ -368,13 +370,29 @@ td[handler] {cursor: move}
table.on('tool(plan-table)', function(obj){ // 双击 toolDouble
var data = obj.data;
//console.log(obj)
if(obj.event === 'del'){
if(obj.event === 'edit'){
openNewForm(obj.data.id)
}
else if(obj.event === 'del'){
layer.confirm('真的删除行么', function(index){
obj.del();
layer.close(index);
});
} else if(obj.event === 'edit'){
openNewForm(obj.data.id)
}
else if(obj.event === 'trigger'){
var load = layui.layer.load(4);
$.ajax({
method: 'POST',
url: '/admin/manage/reviews/plans/manualTrigger',
data: {id: obj.data.id},
success: res => {
layer.msg('操作成功', {time: 1000});
},
error: res => {
layer.msg(res.responseJSON.message || '操作失败', {time: 2000});
},
complete: () => {if (load) layui.layer.close(load)}
})
}
});