feat(all): 初始化版本
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
package com.lanyuanxiaoyao.leopard.server;
|
||||
|
||||
/**
|
||||
* 静态字段
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250829
|
||||
*/
|
||||
public interface Constants {
|
||||
String DATABASE_PREFIX = "leopard_";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.lanyuanxiaoyao.leopard.server;
|
||||
|
||||
import com.blinkfox.fenix.EnableFenix;
|
||||
import com.lanyuanxiaoyao.leopard.server.service.StockService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250828
|
||||
*/
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
@EnableFenix
|
||||
@EnableJpaAuditing
|
||||
public class LeopardServerApplication implements ApplicationRunner {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(LeopardServerApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
* 网络配置
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250826
|
||||
*/
|
||||
@Configuration
|
||||
public class WebConfiguration {
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowCredentials(true);
|
||||
configuration.addAllowedOriginPattern("*");
|
||||
configuration.addAllowedHeader("*");
|
||||
configuration.addAllowedMethod("*");
|
||||
configuration.setMaxAge(7200L);
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.Stock;
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.Task;
|
||||
import com.lanyuanxiaoyao.leopard.server.service.StockService;
|
||||
import com.lanyuanxiaoyao.service.template.controller.GlobalResponse;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 下拉菜单枚举类型
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250826
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("constants")
|
||||
public class CommonOptionsController {
|
||||
private static final List<String> COLORS = List.of(
|
||||
"bg-black",
|
||||
"bg-primary",
|
||||
"bg-secondary",
|
||||
"bg-success",
|
||||
"bg-info",
|
||||
"bg-warning",
|
||||
"bg-danger",
|
||||
"bg-dark",
|
||||
"bg-gray-500",
|
||||
"bg-gray-600",
|
||||
"bg-gray-700",
|
||||
"bg-gray-800",
|
||||
"bg-gray-900",
|
||||
"bg-red-500",
|
||||
"bg-red-600",
|
||||
"bg-red-700",
|
||||
"bg-red-800",
|
||||
"bg-red-900",
|
||||
"bg-yellow-500",
|
||||
"bg-yellow-600",
|
||||
"bg-yellow-700",
|
||||
"bg-yellow-800",
|
||||
"bg-yellow-900",
|
||||
"bg-green-500",
|
||||
"bg-green-600",
|
||||
"bg-green-700",
|
||||
"bg-green-800",
|
||||
"bg-green-900",
|
||||
"bg-blue-500",
|
||||
"bg-blue-600",
|
||||
"bg-blue-700",
|
||||
"bg-blue-800",
|
||||
"bg-blue-900",
|
||||
"bg-cyan-500",
|
||||
"bg-cyan-600",
|
||||
"bg-cyan-700",
|
||||
"bg-cyan-800",
|
||||
"bg-cyan-900",
|
||||
"bg-indigo-500",
|
||||
"bg-indigo-600",
|
||||
"bg-indigo-700",
|
||||
"bg-indigo-800",
|
||||
"bg-indigo-900",
|
||||
"bg-purple-500",
|
||||
"bg-purple-600",
|
||||
"bg-purple-700",
|
||||
"bg-purple-800",
|
||||
"bg-purple-900",
|
||||
"bg-pink-500",
|
||||
"bg-pink-600",
|
||||
"bg-pink-700",
|
||||
"bg-pink-800",
|
||||
"bg-pink-900"
|
||||
);
|
||||
|
||||
private final StockService stockService;
|
||||
|
||||
public CommonOptionsController(StockService stockService) {
|
||||
this.stockService = stockService;
|
||||
}
|
||||
|
||||
@GetMapping("/options/{name}")
|
||||
public GlobalResponse<List<Option>> options(@PathVariable("name") String name) {
|
||||
return switch (name) {
|
||||
case "stock_market" -> GlobalResponse.responseSuccess(
|
||||
Arrays.stream(Stock.Market.values())
|
||||
.map(market -> new Option(market.getChineseName(), market.name()))
|
||||
.toList()
|
||||
);
|
||||
case "stock_industry" -> GlobalResponse.responseSuccess(
|
||||
stockService.findDistinctIndustries()
|
||||
.stream()
|
||||
.map(industry -> new Option(industry, industry))
|
||||
.toList()
|
||||
);
|
||||
default -> GlobalResponse.responseSuccess(List.of());
|
||||
};
|
||||
}
|
||||
|
||||
@GetMapping("/mappings/{name}/{field}")
|
||||
public GlobalResponse<Map<String, String>> mappings(@PathVariable("name") String name, @PathVariable("field") String field) {
|
||||
return switch (name) {
|
||||
case "stock_market" -> GlobalResponse.responseSuccess(buildMapping(
|
||||
Arrays.stream(Stock.Market.values())
|
||||
.map(market -> new Mapping(market.name(), market.getChineseName()))
|
||||
.toList(),
|
||||
field
|
||||
));
|
||||
case "task_status" -> GlobalResponse.responseSuccess(buildMapping(
|
||||
Arrays.stream(Task.Status.values())
|
||||
.map(status -> new Mapping(status.name(), status.getChineseName()))
|
||||
.toList(),
|
||||
field
|
||||
));
|
||||
default -> GlobalResponse.responseSuccess(Map.of());
|
||||
};
|
||||
}
|
||||
|
||||
private Map<String, String> buildMapping(List<Mapping> mappings, String field) {
|
||||
var map = mappings
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Mapping::name, mapping -> {
|
||||
var color = COLORS.get(Math.abs(mapping.name.hashCode()) % COLORS.size());
|
||||
return "<span class='label %s'>%s</span>".formatted(color, mapping.label());
|
||||
}));
|
||||
map.put("*", "<span class='label bg-gray-300'>%s</span>".formatted(field));
|
||||
return map;
|
||||
}
|
||||
|
||||
public record Option(String label, Object value) {
|
||||
}
|
||||
|
||||
public record Mapping(String name, String label) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.Stock;
|
||||
import com.lanyuanxiaoyao.leopard.server.service.StockService;
|
||||
import com.lanyuanxiaoyao.service.template.controller.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport;
|
||||
import java.util.function.Function;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250829
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("stock")
|
||||
public class StockController extends SimpleControllerSupport<Stock, Void, StockController.DetailItem, StockController.DetailItem> {
|
||||
public StockController(StockService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlobalResponse<Long> save(Void unused) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Void, Stock> saveItemMapper() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private DetailItem covert(Stock stock) {
|
||||
return new DetailItem(
|
||||
stock.getId(),
|
||||
stock.getCode(),
|
||||
stock.getName(),
|
||||
stock.getFullname(),
|
||||
stock.getMarket(),
|
||||
stock.getIndustry()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Stock, DetailItem> listItemMapper() {
|
||||
return this::covert;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Stock, DetailItem> detailItemMapper() {
|
||||
return this::covert;
|
||||
}
|
||||
|
||||
public record DetailItem(
|
||||
Long id,
|
||||
String code,
|
||||
String name,
|
||||
String fullname,
|
||||
Stock.Market market,
|
||||
String industry
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.Task;
|
||||
import com.lanyuanxiaoyao.leopard.server.service.TaskService;
|
||||
import com.lanyuanxiaoyao.service.template.controller.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.function.Function;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 任务列表
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250829
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("task")
|
||||
public class TaskController extends SimpleControllerSupport<Task, Void, TaskController.ListItem, TaskController.DetailItem> {
|
||||
public TaskController(TaskService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlobalResponse<Long> save(Void unused) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Void, Task> saveItemMapper() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Task, ListItem> listItemMapper() {
|
||||
return task -> new ListItem(
|
||||
task.getId(),
|
||||
task.getName(),
|
||||
task.getDescription(),
|
||||
task.getStatus(),
|
||||
task.getLaunchedTime(),
|
||||
task.getFinishedTime()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Task, DetailItem> detailItemMapper() {
|
||||
return task -> new DetailItem(
|
||||
task.getId(),
|
||||
task.getName(),
|
||||
task.getDescription(),
|
||||
task.getStatus(),
|
||||
task.getError(),
|
||||
task.getResult(),
|
||||
task.getLaunchedTime(),
|
||||
task.getFinishedTime()
|
||||
);
|
||||
}
|
||||
|
||||
public record ListItem(
|
||||
Long id,
|
||||
String name,
|
||||
String description,
|
||||
Task.Status status,
|
||||
LocalDateTime launchedTime,
|
||||
LocalDateTime finishedTime
|
||||
) {
|
||||
}
|
||||
|
||||
public record DetailItem(
|
||||
Long id,
|
||||
String name,
|
||||
String description,
|
||||
Task.Status status,
|
||||
String error,
|
||||
String result,
|
||||
LocalDateTime launchedTime,
|
||||
LocalDateTime finishedTime
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.entity;
|
||||
|
||||
import com.lanyuanxiaoyao.leopard.server.Constants;
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.base.SimpleEnum;
|
||||
import com.lanyuanxiaoyao.service.template.entity.SimpleEntity;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.DynamicInsert;
|
||||
import org.hibernate.annotations.DynamicUpdate;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
/**
|
||||
* 股票
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250828
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@Entity
|
||||
@SoftDelete
|
||||
@DynamicUpdate
|
||||
@DynamicInsert
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Table(name = Constants.DATABASE_PREFIX + "stock")
|
||||
public class Stock extends SimpleEntity {
|
||||
@Column(unique = true, nullable = false)
|
||||
@Comment("股票代码")
|
||||
private String code;
|
||||
@Column(nullable = false)
|
||||
@Comment("股票名称")
|
||||
private String name;
|
||||
@Column(nullable = false)
|
||||
@Comment("股票全称")
|
||||
private String fullname;
|
||||
@Column(nullable = false)
|
||||
@Comment("交易市场")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Market market;
|
||||
@Column(nullable = false)
|
||||
@Comment("行业")
|
||||
private String industry;
|
||||
@Column(nullable = false)
|
||||
@Comment("上市状态")
|
||||
private boolean listed = true;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Market implements SimpleEnum {
|
||||
SSE("上交所"),
|
||||
SZSE("深交所"),
|
||||
BSE("北交所");
|
||||
|
||||
private final String chineseName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.entity;
|
||||
|
||||
import com.lanyuanxiaoyao.leopard.server.Constants;
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.base.SimpleEnum;
|
||||
import com.lanyuanxiaoyao.service.template.entity.SimpleEntity;
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.DynamicInsert;
|
||||
import org.hibernate.annotations.DynamicUpdate;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
/**
|
||||
* 任务
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250829
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@Entity
|
||||
@SoftDelete
|
||||
@DynamicUpdate
|
||||
@DynamicInsert
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Table(name = Constants.DATABASE_PREFIX + "task")
|
||||
public class Task extends SimpleEntity {
|
||||
@Column(nullable = false)
|
||||
@Comment("任务名称")
|
||||
private String name;
|
||||
@Lob
|
||||
@Comment("任务描述")
|
||||
private String description;
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
@ToString.Exclude
|
||||
@Comment("错误信息")
|
||||
private String error;
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
@ToString.Exclude
|
||||
@Comment("任务结果")
|
||||
private String result;
|
||||
@Column(nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Comment("任务状态")
|
||||
private Status status;
|
||||
@Comment("任务开始时间")
|
||||
private LocalDateTime launchedTime;
|
||||
@Comment("任务结束时间")
|
||||
private LocalDateTime finishedTime;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Status implements SimpleEnum {
|
||||
PENDING("待执行"),
|
||||
RUNNING("执行中"),
|
||||
SUCCESS("成功"),
|
||||
FAILURE("失败"),
|
||||
CANCELED("取消");
|
||||
|
||||
private final String chineseName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.entity.base;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250829
|
||||
*/
|
||||
public interface SimpleEnum {
|
||||
String getChineseName();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.repository;
|
||||
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.Stock;
|
||||
import com.lanyuanxiaoyao.service.template.repository.SimpleRepository;
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250828
|
||||
*/
|
||||
@Repository
|
||||
public interface StockRepository extends SimpleRepository<Stock> {
|
||||
@Query("select distinct stock.industry from Stock stock where stock.industry is not null")
|
||||
List<String> findDistinctIndustries();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.repository;
|
||||
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.Task;
|
||||
import com.lanyuanxiaoyao.service.template.repository.SimpleRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250829
|
||||
*/
|
||||
@Repository
|
||||
public interface TaskRepository extends SimpleRepository<Task> {
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.service;
|
||||
|
||||
import cn.hutool.core.util.EnumUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.Stock;
|
||||
import com.lanyuanxiaoyao.leopard.server.repository.StockRepository;
|
||||
import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250828
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class StockService extends SimpleServiceSupport<Stock> {
|
||||
private final StockRepository stockRepository;
|
||||
private final TuShareService tuShareService;
|
||||
|
||||
public StockService(StockRepository repository, TuShareService tuShareService) {
|
||||
super(repository);
|
||||
this.stockRepository = repository;
|
||||
this.tuShareService = tuShareService;
|
||||
}
|
||||
|
||||
public List<String> findDistinctIndustries() {
|
||||
return stockRepository.findDistinctIndustries();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
public void refresh() {
|
||||
log.info("开始刷新股票列表");
|
||||
var stocks = list();
|
||||
var stocksMap = stocks.stream().collect(Collectors.toMap(Stock::getCode, stock -> stock));
|
||||
tuShareService.stockList()
|
||||
.data()
|
||||
.items()
|
||||
.forEach(item -> {
|
||||
var code = item.get(0);
|
||||
var name = item.get(1);
|
||||
var fullname = item.get(2);
|
||||
var market = EnumUtil.fromString(Stock.Market.class, item.get(3));
|
||||
var industry = item.get(4);
|
||||
var listed = StrUtil.equals("L", item.get(5));
|
||||
if (stocksMap.containsKey(code)) {
|
||||
var stock = stocksMap.get(code);
|
||||
stock.setName(name);
|
||||
stock.setFullname(fullname);
|
||||
stock.setMarket(market);
|
||||
stock.setIndustry(industry);
|
||||
stock.setListed(listed);
|
||||
} else {
|
||||
var stock = new Stock();
|
||||
stock.setCode(code);
|
||||
stock.setName(name);
|
||||
stock.setFullname(fullname);
|
||||
stock.setMarket(market);
|
||||
stock.setIndustry(industry);
|
||||
stock.setListed(listed);
|
||||
stocks.add(stock);
|
||||
}
|
||||
});
|
||||
repository.saveAll(stocks);
|
||||
log.info("股票列表刷新完成");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.service;
|
||||
|
||||
import com.lanyuanxiaoyao.leopard.server.entity.Task;
|
||||
import com.lanyuanxiaoyao.leopard.server.repository.TaskRepository;
|
||||
import com.lanyuanxiaoyao.service.template.repository.SimpleRepository;
|
||||
import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250829
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TaskService extends SimpleServiceSupport<Task> {
|
||||
|
||||
|
||||
public TaskService(TaskRepository repository) {
|
||||
super(repository);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.service;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 对接TuShare接口
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250828
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TuShareService {
|
||||
private static final String API_URL = "https://api.tushare.pro";
|
||||
private static final String API_TOKEN = "64ebff4fa679167600b905ee45dd88e76f3963c0ff39157f3f085f0e";
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public TuShareService(Jackson2ObjectMapperBuilder builder) {
|
||||
this.mapper = builder.build();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private String buildRequest(String apiName, Map<String, Object> params, List<String> fields) {
|
||||
return mapper.writeValueAsString(Map.of(
|
||||
"api_name", apiName,
|
||||
"token", API_TOKEN,
|
||||
"params", params,
|
||||
"fields", fields
|
||||
));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public TuShareResponse stockList() {
|
||||
var response = HttpUtil.post(API_URL, buildRequest(
|
||||
"stock_basic",
|
||||
Map.of("list_status", "L"),
|
||||
List.of("ts_code", "name", "fullname", "exchange", "industry", "list_status")
|
||||
));
|
||||
var tuShareResponse = mapper.readValue(response, TuShareResponse.class);
|
||||
if (tuShareResponse.code != 0) {
|
||||
throw new RuntimeException(tuShareResponse.message);
|
||||
}
|
||||
return tuShareResponse;
|
||||
}
|
||||
|
||||
public record TuShareResponse(
|
||||
Integer code,
|
||||
@JsonProperty("msg")
|
||||
String message,
|
||||
Data data
|
||||
) {
|
||||
public record Data(
|
||||
List<String> fields,
|
||||
List<List<String>> items
|
||||
) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.lanyuanxiaoyao.leopard.server.task;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 任务
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250829
|
||||
*/
|
||||
public abstract class TaskTemplate {
|
||||
private String name;
|
||||
private String description;
|
||||
|
||||
public abstract Type getType();
|
||||
|
||||
public enum Type {
|
||||
CLASS
|
||||
}
|
||||
}
|
||||
17
leopard-server/src/main/resources/application.yml
Normal file
17
leopard-server/src/main/resources/application.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
server:
|
||||
port: 9786
|
||||
spring:
|
||||
application:
|
||||
name: leopard-server
|
||||
mvc:
|
||||
async:
|
||||
request-timeout: 3600000
|
||||
datasource:
|
||||
url: jdbc:mysql://mysql.lanyuanxiaoyao.com:43780/leopard?useSSL=false
|
||||
username: leopard
|
||||
password: '9NEzFzovnddf@PyEP?e*AYAWnCyd7UhYwQK$pJf>7?ccFiN^x4$eKEZ5~E<7<+~X'
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
jpa:
|
||||
generate-ddl: true
|
||||
fenix:
|
||||
print-banner: false
|
||||
24
leopard-server/src/main/resources/logback-spring.xml
Normal file
24
leopard-server/src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<configuration>
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
|
||||
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
|
||||
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
|
||||
<springProperty scope="context" name="LOGGING_PARENT" source="logging.parent"/>
|
||||
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
|
||||
|
||||
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%5p) %clr([${HOSTNAME}]){yellow} %clr([%t]){magenta} %clr(%logger{40}){cyan}: %m%n%wEx</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="com.zaxxer.hikari" level="ERROR"/>
|
||||
<logger name="org.hibernate.SQL" level="DEBUG"/>
|
||||
<logger name="org.hibernate.type.descriptor.jdbc.BasicBinder" level="TRACE"/>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="Console"/>
|
||||
</root>
|
||||
</configuration>
|
||||
47
leopard-server/src/test/resources/tushare.http
Normal file
47
leopard-server/src/test/resources/tushare.http
Normal file
@@ -0,0 +1,47 @@
|
||||
@api_url = http://api.tushare.pro
|
||||
@api_key = 64ebff4fa679167600b905ee45dd88e76f3963c0ff39157f3f085f0e
|
||||
|
||||
### Get Stock List
|
||||
POST {{api_url}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"api_name": "stock_basic",
|
||||
"token": "{{api_key}}",
|
||||
"params": {
|
||||
"list_status": "D,P,L"
|
||||
},
|
||||
"fields": [
|
||||
"ts_code",
|
||||
"name",
|
||||
"fullname",
|
||||
"exchange",
|
||||
"industry"
|
||||
]
|
||||
}
|
||||
|
||||
### Get income list
|
||||
POST {{api_url}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"api_name": "income",
|
||||
"token": "{{api_key}}",
|
||||
"params": {
|
||||
"ts_code": "600000.SH"
|
||||
},
|
||||
"fields": [
|
||||
"ts_code",
|
||||
"ann_date",
|
||||
"fiscal_year",
|
||||
"report_type",
|
||||
"net_profit",
|
||||
"total_revenue",
|
||||
"total_cost",
|
||||
"gross_profit",
|
||||
"operating_profit",
|
||||
"net_profit_before_tax",
|
||||
"net_profit_to_parent",
|
||||
"total_revenue_to_parent"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user