基本完成牛只数据的导入接口
This commit is contained in:
@@ -35,6 +35,7 @@ public class CattleDataController {
|
|||||||
private final com.example.cattletends.service.CattleProvinceService cattleProvinceService;
|
private final com.example.cattletends.service.CattleProvinceService cattleProvinceService;
|
||||||
private final com.example.cattletends.service.CattleNationalService cattleNationalService;
|
private final com.example.cattletends.service.CattleNationalService cattleNationalService;
|
||||||
private final com.example.cattletends.service.CattleBreedService cattleBreedService;
|
private final com.example.cattletends.service.CattleBreedService cattleBreedService;
|
||||||
|
private final com.example.cattletends.service.ProvinceDailyPriceService provinceDailyPriceService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造器注入
|
* 构造器注入
|
||||||
@@ -42,34 +43,49 @@ public class CattleDataController {
|
|||||||
public CattleDataController(CattleDataService cattleDataService,
|
public CattleDataController(CattleDataService cattleDataService,
|
||||||
com.example.cattletends.service.CattleProvinceService cattleProvinceService,
|
com.example.cattletends.service.CattleProvinceService cattleProvinceService,
|
||||||
com.example.cattletends.service.CattleNationalService cattleNationalService,
|
com.example.cattletends.service.CattleNationalService cattleNationalService,
|
||||||
com.example.cattletends.service.CattleBreedService cattleBreedService) {
|
com.example.cattletends.service.CattleBreedService cattleBreedService,
|
||||||
|
com.example.cattletends.service.ProvinceDailyPriceService provinceDailyPriceService) {
|
||||||
this.cattleDataService = cattleDataService;
|
this.cattleDataService = cattleDataService;
|
||||||
this.cattleProvinceService = cattleProvinceService;
|
this.cattleProvinceService = cattleProvinceService;
|
||||||
this.cattleNationalService = cattleNationalService;
|
this.cattleNationalService = cattleNationalService;
|
||||||
this.cattleBreedService = cattleBreedService;
|
this.cattleBreedService = cattleBreedService;
|
||||||
|
this.provinceDailyPriceService = provinceDailyPriceService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有牛只数据(支持按省份或品种筛选)
|
* 获取所有牛只数据(支持按省份或品种筛选,支持按日期查询)
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@ApiOperation(value = "获取所有牛只数据", notes = "返回所有牛只数据的列表,支持按省份或品种筛选,按价格升序排序")
|
@ApiOperation(value = "获取所有牛只数据", notes = "返回牛只数据的列表,支持按省份或品种筛选,支持按日期查询(格式:yyyy-MM-dd),默认查询今天的数据,按价格升序排序")
|
||||||
public Result<List<CattleData>> getAllCattleData(
|
public Result<List<CattleData>> getAllCattleData(
|
||||||
@ApiParam(value = "省份(可选,如果提供则只返回该省份的数据)")
|
@ApiParam(value = "省份(可选,如果提供则只返回该省份的数据)")
|
||||||
@RequestParam(required = false) String province,
|
@RequestParam(required = false) String province,
|
||||||
@ApiParam(value = "品种(可选,如果提供则只返回该品种的数据)")
|
@ApiParam(value = "品种(可选,如果提供则只返回该品种的数据)")
|
||||||
@RequestParam(required = false) String type) {
|
@RequestParam(required = false) String type,
|
||||||
|
@ApiParam(value = "日期(可选,格式:yyyy-MM-dd,如果提供则查询该日期的数据,不提供则查询今天的数据)")
|
||||||
|
@RequestParam(required = false) String date) {
|
||||||
List<CattleData> data;
|
List<CattleData> data;
|
||||||
|
|
||||||
|
// 解析日期参数
|
||||||
|
java.time.LocalDate parsedDate = null;
|
||||||
|
if (date != null && !date.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
parsedDate = java.time.LocalDate.parse(date.trim());
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("日期格式错误: " + date + ", 错误: " + e.getMessage());
|
||||||
|
return Result.error(400, "日期格式错误,请使用 yyyy-MM-dd 格式,例如:2025-12-04");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 优先按品种筛选
|
// 优先按品种筛选
|
||||||
if (type != null && !type.trim().isEmpty()) {
|
if (type != null && !type.trim().isEmpty()) {
|
||||||
data = cattleDataService.getCattleDataByType(type.trim());
|
data = cattleDataService.getCattleDataByType(type.trim(), parsedDate);
|
||||||
} else if (province != null && !province.trim().isEmpty()) {
|
} else if (province != null && !province.trim().isEmpty()) {
|
||||||
// 其次按省份筛选
|
// 其次按省份筛选
|
||||||
data = cattleDataService.getCattleDataByProvince(province.trim());
|
data = cattleDataService.getCattleDataByProvince(province.trim(), parsedDate);
|
||||||
} else {
|
} else {
|
||||||
// 都不提供则返回所有数据
|
// 都不提供则返回所有数据
|
||||||
data = cattleDataService.getAllCattleData();
|
data = cattleDataService.getAllCattleData(parsedDate);
|
||||||
}
|
}
|
||||||
return Result.success(data);
|
return Result.success(data);
|
||||||
}
|
}
|
||||||
@@ -78,21 +94,34 @@ public class CattleDataController {
|
|||||||
* 阿里云大屏接口 - 获取牛只数据(直接返回数据数组,不包含Result包装)
|
* 阿里云大屏接口 - 获取牛只数据(直接返回数据数组,不包含Result包装)
|
||||||
*/
|
*/
|
||||||
@GetMapping("/screen")
|
@GetMapping("/screen")
|
||||||
@ApiOperation(value = "阿里云大屏接口", notes = "返回牛只数据数组,直接返回data数据,不包含Result包装。支持按省份或品种筛选,按价格升序排序")
|
@ApiOperation(value = "阿里云大屏接口", notes = "返回牛只数据数组,直接返回data数据,不包含Result包装。支持按省份或品种筛选,支持按日期查询(格式:yyyy-MM-dd),默认查询今天的数据,按价格升序排序")
|
||||||
public List<CattleData> getCattleDataForScreen(
|
public List<CattleData> getCattleDataForScreen(
|
||||||
@ApiParam(value = "省份(可选,如果提供则只返回该省份的数据)")
|
@ApiParam(value = "省份(可选,如果提供则只返回该省份的数据)")
|
||||||
@RequestParam(required = false) String province,
|
@RequestParam(required = false) String province,
|
||||||
@ApiParam(value = "品种(可选,如果提供则只返回该品种的数据)")
|
@ApiParam(value = "品种(可选,如果提供则只返回该品种的数据)")
|
||||||
@RequestParam(required = false) String type) {
|
@RequestParam(required = false) String type,
|
||||||
|
@ApiParam(value = "日期(可选,格式:yyyy-MM-dd,如果提供则查询该日期的数据,不提供则查询今天的数据)")
|
||||||
|
@RequestParam(required = false) String date) {
|
||||||
|
// 解析日期参数
|
||||||
|
java.time.LocalDate parsedDate = null;
|
||||||
|
if (date != null && !date.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
parsedDate = java.time.LocalDate.parse(date.trim());
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("日期格式错误: " + date + ", 错误: " + e.getMessage());
|
||||||
|
return new java.util.ArrayList<>(); // 返回空列表
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 优先按品种筛选
|
// 优先按品种筛选
|
||||||
if (type != null && !type.trim().isEmpty()) {
|
if (type != null && !type.trim().isEmpty()) {
|
||||||
return cattleDataService.getCattleDataByType(type.trim());
|
return cattleDataService.getCattleDataByType(type.trim(), parsedDate);
|
||||||
} else if (province != null && !province.trim().isEmpty()) {
|
} else if (province != null && !province.trim().isEmpty()) {
|
||||||
// 其次按省份筛选
|
// 其次按省份筛选
|
||||||
return cattleDataService.getCattleDataByProvince(province.trim());
|
return cattleDataService.getCattleDataByProvince(province.trim(), parsedDate);
|
||||||
} else {
|
} else {
|
||||||
// 都不提供则返回所有数据
|
// 都不提供则返回所有数据
|
||||||
return cattleDataService.getAllCattleData();
|
return cattleDataService.getAllCattleData(parsedDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,64 +146,49 @@ public class CattleDataController {
|
|||||||
public Result<List<CattleData>> importCattleData(
|
public Result<List<CattleData>> importCattleData(
|
||||||
@ApiParam(value = "Excel文件", required = true)
|
@ApiParam(value = "Excel文件", required = true)
|
||||||
@RequestParam("file") MultipartFile file) {
|
@RequestParam("file") MultipartFile file) {
|
||||||
System.out.println("========== 收到导入牛只数据请求 ==========");
|
|
||||||
System.out.println("文件名: " + file.getOriginalFilename());
|
|
||||||
System.out.println("文件大小: " + file.getSize());
|
|
||||||
|
|
||||||
if (file.isEmpty()) {
|
if (file.isEmpty()) {
|
||||||
System.out.println("错误: 文件为空");
|
|
||||||
return Result.error(400, "文件不能为空");
|
return Result.error(400, "文件不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
System.out.println("开始解析Excel文件...");
|
|
||||||
List<CattleDataDTO> dataList = parseExcelFile(file);
|
List<CattleDataDTO> dataList = parseExcelFile(file);
|
||||||
System.out.println("解析完成,共 " + (dataList != null ? dataList.size() : 0) + " 条数据");
|
|
||||||
|
|
||||||
if (dataList == null || dataList.isEmpty()) {
|
if (dataList == null || dataList.isEmpty()) {
|
||||||
System.out.println("错误: Excel文件中没有有效数据");
|
|
||||||
return Result.error(400, "Excel文件中没有有效数据,请检查文件格式");
|
return Result.error(400, "Excel文件中没有有效数据,请检查文件格式");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动创建品种
|
// 自动创建品种
|
||||||
System.out.println("========== 开始自动创建品种 ==========");
|
|
||||||
int newBreedCount = 0;
|
|
||||||
int existingBreedCount = 0;
|
|
||||||
java.util.Set<String> processedBreeds = new java.util.HashSet<>();
|
java.util.Set<String> processedBreeds = new java.util.HashSet<>();
|
||||||
|
|
||||||
for (CattleDataDTO dto : dataList) {
|
for (CattleDataDTO dto : dataList) {
|
||||||
if (dto.getType() != null && !dto.getType().trim().isEmpty()) {
|
if (dto.getType() != null && !dto.getType().trim().isEmpty()) {
|
||||||
String breedName = dto.getType().trim();
|
String breedName = dto.getType().trim();
|
||||||
// 避免重复处理相同品种
|
|
||||||
if (!processedBreeds.contains(breedName)) {
|
if (!processedBreeds.contains(breedName)) {
|
||||||
try {
|
try {
|
||||||
com.example.cattletends.entity.CattleBreed existingBreed = cattleBreedService.getBreedByName(breedName);
|
com.example.cattletends.entity.CattleBreed existingBreed = cattleBreedService.getBreedByName(breedName);
|
||||||
if (existingBreed == null) {
|
if (existingBreed == null) {
|
||||||
// 新品种,创建
|
|
||||||
cattleBreedService.createOrGetBreed(breedName);
|
cattleBreedService.createOrGetBreed(breedName);
|
||||||
newBreedCount++;
|
|
||||||
System.out.println("✓ 创建新品种: " + breedName);
|
|
||||||
} else {
|
|
||||||
existingBreedCount++;
|
|
||||||
System.out.println("○ 品种已存在: " + breedName);
|
|
||||||
}
|
}
|
||||||
processedBreeds.add(breedName);
|
processedBreeds.add(breedName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("✗ 创建品种失败: " + breedName + ", 错误: " + e.getMessage());
|
System.err.println("创建品种失败: " + breedName + ", 错误: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("品种处理完成: 新增 " + newBreedCount + " 个,已存在 " + existingBreedCount + " 个,共处理 " + processedBreeds.size() + " 个品种");
|
|
||||||
System.out.println("========== 品种创建完成 ==========");
|
|
||||||
|
|
||||||
System.out.println("开始导入数据到数据库...");
|
System.out.println("========== 开始导入数据,共 " + dataList.size() + " 条 ==========");
|
||||||
com.example.cattletends.dto.ImportResult importResult = cattleDataService.batchImportCattleData(dataList);
|
com.example.cattletends.dto.ImportResult importResult = cattleDataService.batchImportCattleData(dataList);
|
||||||
System.out.println("导入完成: 新增 " + importResult.getNewCount() + " 条,更新 " + importResult.getUpdateCount() + " 条");
|
System.out.println("========== 导入完成: 新增 " + importResult.getNewCount() + " 条,更新 " + importResult.getUpdateCount() + " 条 ==========");
|
||||||
|
|
||||||
String message = String.format("导入成功:新增 %d 条,更新 %d 条,共处理 %d 条数据;自动创建 %d 个新品种,%d 个品种已存在",
|
// 更新品种的逻辑删除状态
|
||||||
importResult.getNewCount(), importResult.getUpdateCount(), importResult.getTotalCount(),
|
try {
|
||||||
newBreedCount, existingBreedCount);
|
cattleBreedService.updateBreedDeleteStatus(processedBreeds);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("更新品种逻辑删除状态失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
String message = String.format("导入成功:新增 %d 条,更新 %d 条,共处理 %d 条数据",
|
||||||
|
importResult.getNewCount(), importResult.getUpdateCount(), importResult.getTotalCount());
|
||||||
return Result.success(message, importResult.getDataList());
|
return Result.success(message, importResult.getDataList());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("========== 导入失败 ==========");
|
System.err.println("========== 导入失败 ==========");
|
||||||
@@ -235,6 +249,62 @@ public class CattleDataController {
|
|||||||
return Result.success(data);
|
return Result.success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取省份每日均价数据(支持按省份和日期查询)
|
||||||
|
*/
|
||||||
|
@GetMapping("/province-daily-prices")
|
||||||
|
@ApiOperation(value = "获取省份每日均价数据", notes = "返回省份的每日均价记录。支持通过province参数按省份查询,支持通过priceDate参数按日期查询(格式:yyyy-MM-dd)。如果同时提供province和priceDate,则返回该省份指定日期的价格;如果只提供priceDate,则返回所有省份指定日期的价格;如果只提供province,则返回该省份所有日期的价格;如果都不提供,则返回所有省份所有日期的价格,按日期降序排序")
|
||||||
|
public Result<List<com.example.cattletends.entity.ProvinceDailyPrice>> getProvinceDailyPrices(
|
||||||
|
@ApiParam(value = "省份名称(可选),如果提供则只返回该省份的每日均价数据", required = false)
|
||||||
|
@RequestParam(required = false) String province,
|
||||||
|
@ApiParam(value = "价格日期(可选),格式:yyyy-MM-dd,如果提供则只返回该日期的价格数据", required = false)
|
||||||
|
@RequestParam(required = false) String priceDate) {
|
||||||
|
System.out.println("========== 收到查询省份每日均价请求 ==========");
|
||||||
|
System.out.println("省份参数: " + (province != null ? province : "全部"));
|
||||||
|
System.out.println("日期参数: " + (priceDate != null ? priceDate : "全部"));
|
||||||
|
|
||||||
|
List<com.example.cattletends.entity.ProvinceDailyPrice> data;
|
||||||
|
|
||||||
|
// 解析日期参数
|
||||||
|
java.time.LocalDate parsedDate = null;
|
||||||
|
if (priceDate != null && !priceDate.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
parsedDate = java.time.LocalDate.parse(priceDate.trim());
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("日期格式错误: " + priceDate + ", 错误: " + e.getMessage());
|
||||||
|
return Result.error(400, "日期格式错误,请使用 yyyy-MM-dd 格式,例如:2025-12-03");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据参数组合进行查询
|
||||||
|
if (province != null && !province.trim().isEmpty() && parsedDate != null) {
|
||||||
|
// 同时提供省份和日期:查询该省份指定日期的价格
|
||||||
|
java.util.Optional<com.example.cattletends.entity.ProvinceDailyPrice> optional =
|
||||||
|
provinceDailyPriceService.getByProvinceAndPriceDate(province.trim(), parsedDate);
|
||||||
|
if (optional.isPresent()) {
|
||||||
|
data = java.util.Collections.singletonList(optional.get());
|
||||||
|
} else {
|
||||||
|
data = new java.util.ArrayList<>();
|
||||||
|
}
|
||||||
|
System.out.println("查询省份: " + province + ", 日期: " + parsedDate + ", 返回 " + (data != null ? data.size() : 0) + " 条记录");
|
||||||
|
} else if (parsedDate != null) {
|
||||||
|
// 只提供日期:查询所有省份指定日期的价格
|
||||||
|
data = provinceDailyPriceService.getByPriceDate(parsedDate);
|
||||||
|
System.out.println("查询日期: " + parsedDate + ", 返回 " + (data != null ? data.size() : 0) + " 条记录");
|
||||||
|
} else if (province != null && !province.trim().isEmpty()) {
|
||||||
|
// 只提供省份:查询该省份所有日期的价格
|
||||||
|
data = provinceDailyPriceService.getByProvince(province.trim());
|
||||||
|
System.out.println("查询省份: " + province + ", 返回 " + (data != null ? data.size() : 0) + " 条记录");
|
||||||
|
} else {
|
||||||
|
// 都不提供:查询所有省份所有日期的价格
|
||||||
|
data = provinceDailyPriceService.getAll();
|
||||||
|
System.out.println("查询全部省份, 返回 " + (data != null ? data.size() : 0) + " 条记录");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("========== 查询完成 ==========");
|
||||||
|
return Result.success(data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取全国总量数据
|
* 获取全国总量数据
|
||||||
* 自动从 cattleprovince 表计算总和
|
* 自动从 cattleprovince 表计算总和
|
||||||
@@ -292,6 +362,11 @@ public class CattleDataController {
|
|||||||
Map<String, Object> result = cattleProvinceService.importProvinceData(provinceDataList);
|
Map<String, Object> result = cattleProvinceService.importProvinceData(provinceDataList);
|
||||||
System.out.println("导入完成: " + result);
|
System.out.println("导入完成: " + result);
|
||||||
|
|
||||||
|
// 注意:不更新 province_daily_price 表的 price 字段
|
||||||
|
// 该字段只通过牛只数据导入时的计算得出(计算该省份所有牛只数据的平均价格)
|
||||||
|
System.out.println("========== 省份数据导入完成 ==========");
|
||||||
|
System.out.println("注意:province_price 和 province_daily_price.price 字段只通过牛只数据导入时的计算得出");
|
||||||
|
|
||||||
String message = String.format("导入成功:新增 %d 条,更新 %d 条,共处理 %d 条省份数据",
|
String message = String.format("导入成功:新增 %d 条,更新 %d 条,共处理 %d 条省份数据",
|
||||||
result.get("createCount"), result.get("updateCount"), result.get("totalCount"));
|
result.get("createCount"), result.get("updateCount"), result.get("totalCount"));
|
||||||
return Result.success(message, result);
|
return Result.success(message, result);
|
||||||
@@ -476,7 +551,7 @@ public class CattleDataController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析价格
|
* 解析价格(用于牛只数据导入,取平均值)
|
||||||
* 支持格式:
|
* 支持格式:
|
||||||
* - 数字:13.5
|
* - 数字:13.5
|
||||||
* - 价格范围:13.41-14.61(取平均值)
|
* - 价格范围:13.41-14.61(取平均值)
|
||||||
@@ -535,6 +610,48 @@ public class CattleDataController {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析省份价格(用于省份数据导入,提取最低价格)
|
||||||
|
* 支持格式:
|
||||||
|
* - 数字:13.5
|
||||||
|
* - 价格范围:13.4-14.9(提取最低价格 13.4)
|
||||||
|
* - 字符串数字:"13.5"
|
||||||
|
* @param priceStr 价格字符串
|
||||||
|
* @param province 省份名称(用于错误日志)
|
||||||
|
* @return 最低价格
|
||||||
|
*/
|
||||||
|
private BigDecimal parseProvincePrice(String priceStr, String province) {
|
||||||
|
try {
|
||||||
|
if (priceStr == null || priceStr.trim().isEmpty()) {
|
||||||
|
System.out.println("解析省份价格失败: " + province + ", 错误: 价格字符串为空");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
priceStr = priceStr.trim();
|
||||||
|
|
||||||
|
// 处理价格范围格式,如 "13.4-14.9"
|
||||||
|
if (priceStr.contains("-")) {
|
||||||
|
String[] parts = priceStr.split("-");
|
||||||
|
if (parts.length == 2) {
|
||||||
|
try {
|
||||||
|
// 提取最低价格(第一部分)
|
||||||
|
BigDecimal minPrice = new BigDecimal(parts[0].trim());
|
||||||
|
return minPrice;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("解析省份价格失败: " + province + ", 价格字符串: " + priceStr + ", 错误: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理普通数字字符串
|
||||||
|
return new BigDecimal(priceStr);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("解析省份价格失败: " + province + ", 价格字符串: " + priceStr + ", 错误: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 截取所在产地到"市"级别
|
* 截取所在产地到"市"级别
|
||||||
*/
|
*/
|
||||||
@@ -642,77 +759,85 @@ public class CattleDataController {
|
|||||||
|
|
||||||
// Excel模板列顺序(根据实际模板):
|
// Excel模板列顺序(根据实际模板):
|
||||||
// A列(索引0):省份
|
// A列(索引0):省份
|
||||||
// B列(索引1):2023存栏(万头) -> inventory_23th
|
// B列(索引1):省份均价(元/斤) -> province_price
|
||||||
// C列(索引2):2023出栏(万头) -> slaughter_23th
|
// C列(索引2):2023存栏(万头) -> inventory_23th
|
||||||
// D列(索引3):2024存栏(万头) -> inventory_24th
|
// D列(索引3):2023出栏(万头) -> slaughter_23th
|
||||||
// E列(索引4):2024出栏(万头) -> slaughter_24th
|
// E列(索引4):2024存栏(万头) -> inventory_24th
|
||||||
// F列(索引5):2025存栏(万头) -> inventory_25th
|
// F列(索引5):2024出栏(万头) -> slaughter_24th
|
||||||
// G列(索引6):2025出栏(万头) -> slaughter_25th
|
// G列(索引6):2025存栏(万头) -> inventory_25th
|
||||||
// H列(索引7):省份均价(元/斤) -> province_price
|
// H列(索引7):2025出栏(万头) -> slaughter_25th
|
||||||
|
|
||||||
// 读取省份(A列,索引0)
|
// 读取省份(A列,索引0)
|
||||||
Cell provinceCell = row.getCell(0);
|
Cell provinceCell = row.getCell(0);
|
||||||
if (provinceCell != null) {
|
if (provinceCell != null) {
|
||||||
String province = getCellValueAsString(provinceCell);
|
String province = getCellValueAsString(provinceCell).trim();
|
||||||
// 处理可能包含"省"、"自治区"等后缀的情况
|
// 完全按照表格内容导入,不做任何截取
|
||||||
province = province.replace("省", "").replace("自治区", "").replace("市", "").trim();
|
|
||||||
dto.setProvince(province);
|
dto.setProvince(province);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取2023存栏(万头)(B列,索引1)-> inventory_23th
|
// 读取省份均价(元/斤)(B列,索引1)-> province_price_range(原始区间)和 province_price(最低价格)
|
||||||
Cell inv23Cell = row.getCell(1);
|
Cell priceCell = row.getCell(1);
|
||||||
if (inv23Cell != null) {
|
if (priceCell != null) {
|
||||||
Integer value = parseInteger(inv23Cell);
|
String priceStr = getCellValueAsString(priceCell);
|
||||||
dto.setInventory23th(value);
|
System.out.println("省份: " + dto.getProvince() + ", 原始价格字符串: [" + priceStr + "]");
|
||||||
|
|
||||||
|
// 保存原始价格区间字符串
|
||||||
|
dto.setProvincePriceRange(priceStr);
|
||||||
|
|
||||||
|
// 解析价格:如果是区间,提取最低价格;否则使用原值
|
||||||
|
BigDecimal price = parseProvincePrice(priceStr, dto.getProvince() != null ? dto.getProvince() : "未知省份");
|
||||||
|
dto.setProvincePrice(price);
|
||||||
|
System.out.println("省份: " + dto.getProvince() + ", 价格区间: " + priceStr + ", 最低价格: " + price);
|
||||||
|
} else {
|
||||||
|
System.out.println("省份: " + dto.getProvince() + ", 价格单元格为空或不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取2023出栏(万头)(C列,索引2)-> slaughter_23th
|
// 读取2023存栏(万头)(C列,索引2)-> inventory_23th
|
||||||
Cell sla23Cell = row.getCell(2);
|
Cell inv23Cell = row.getCell(2);
|
||||||
|
if (inv23Cell != null) {
|
||||||
|
String cellValueStr = getCellValueAsString(inv23Cell);
|
||||||
|
Integer value = parseInteger(inv23Cell);
|
||||||
|
System.out.println("省份: " + dto.getProvince() + ", 2023存栏原始值: [" + cellValueStr + "], 解析后: " + value);
|
||||||
|
dto.setInventory23th(value);
|
||||||
|
} else {
|
||||||
|
System.out.println("省份: " + dto.getProvince() + ", 2023存栏单元格为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取2023出栏(万头)(D列,索引3)-> slaughter_23th
|
||||||
|
Cell sla23Cell = row.getCell(3);
|
||||||
if (sla23Cell != null) {
|
if (sla23Cell != null) {
|
||||||
Integer value = parseInteger(sla23Cell);
|
Integer value = parseInteger(sla23Cell);
|
||||||
dto.setSlaughter23th(value);
|
dto.setSlaughter23th(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取2024存栏(万头)(D列,索引3)-> inventory_24th
|
// 读取2024存栏(万头)(E列,索引4)-> inventory_24th
|
||||||
Cell inv24Cell = row.getCell(3);
|
Cell inv24Cell = row.getCell(4);
|
||||||
if (inv24Cell != null) {
|
if (inv24Cell != null) {
|
||||||
Integer value = parseInteger(inv24Cell);
|
Integer value = parseInteger(inv24Cell);
|
||||||
dto.setInventory24th(value);
|
dto.setInventory24th(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取2024出栏(万头)(E列,索引4)-> slaughter_24th
|
// 读取2024出栏(万头)(F列,索引5)-> slaughter_24th
|
||||||
Cell sla24Cell = row.getCell(4);
|
Cell sla24Cell = row.getCell(5);
|
||||||
if (sla24Cell != null) {
|
if (sla24Cell != null) {
|
||||||
Integer value = parseInteger(sla24Cell);
|
Integer value = parseInteger(sla24Cell);
|
||||||
dto.setSlaughter24th(value);
|
dto.setSlaughter24th(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取2025存栏(万头)(F列,索引5)-> inventory_25th
|
// 读取2025存栏(万头)(G列,索引6)-> inventory_25th
|
||||||
Cell inv25Cell = row.getCell(5);
|
Cell inv25Cell = row.getCell(6);
|
||||||
if (inv25Cell != null) {
|
if (inv25Cell != null) {
|
||||||
Integer value = parseInteger(inv25Cell);
|
Integer value = parseInteger(inv25Cell);
|
||||||
dto.setInventory25th(value);
|
dto.setInventory25th(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取2025出栏(万头)(G列,索引6)-> slaughter_25th
|
// 读取2025出栏(万头)(H列,索引7)-> slaughter_25th
|
||||||
Cell sla25Cell = row.getCell(6);
|
Cell sla25Cell = row.getCell(7);
|
||||||
if (sla25Cell != null) {
|
if (sla25Cell != null) {
|
||||||
Integer value = parseInteger(sla25Cell);
|
Integer value = parseInteger(sla25Cell);
|
||||||
dto.setSlaughter25th(value);
|
dto.setSlaughter25th(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取省份均价(元/斤)(H列,索引7)-> province_price
|
|
||||||
Cell priceCell = row.getCell(7);
|
|
||||||
if (priceCell != null) {
|
|
||||||
String priceStr = getCellValueAsString(priceCell);
|
|
||||||
System.out.println("省份: " + dto.getProvince() + ", 原始价格字符串: [" + priceStr + "]");
|
|
||||||
BigDecimal price = parsePrice(priceCell, dto.getProvince() != null ? dto.getProvince() : "未知省份");
|
|
||||||
dto.setProvincePrice(price);
|
|
||||||
System.out.println("省份: " + dto.getProvince() + ", 解析后价格: " + price);
|
|
||||||
} else {
|
|
||||||
System.out.println("省份: " + dto.getProvince() + ", 价格单元格为空或不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证必填字段
|
// 验证必填字段
|
||||||
if (dto.getProvince() != null && !dto.getProvince().trim().isEmpty()) {
|
if (dto.getProvince() != null && !dto.getProvince().trim().isEmpty()) {
|
||||||
dataList.add(dto);
|
dataList.add(dto);
|
||||||
|
|||||||
@@ -20,10 +20,15 @@ public class ProvinceDataDTO {
|
|||||||
private String province;
|
private String province;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 省份均价
|
* 省份均价(最低价格)
|
||||||
*/
|
*/
|
||||||
private BigDecimal provincePrice;
|
private BigDecimal provincePrice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 省份均价区间(原始字符串,如 "13.4-14.9")
|
||||||
|
*/
|
||||||
|
private String provincePriceRange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 23年存栏(万头)
|
* 23年存栏(万头)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import java.time.LocalDateTime;
|
|||||||
@Table(name = "cattlebreed", indexes = {
|
@Table(name = "cattlebreed", indexes = {
|
||||||
@Index(name = "idx_breed_name", columnList = "breed_name", unique = true),
|
@Index(name = "idx_breed_name", columnList = "breed_name", unique = true),
|
||||||
@Index(name = "idx_create_time", columnList = "create_time"),
|
@Index(name = "idx_create_time", columnList = "create_time"),
|
||||||
@Index(name = "idx_up_time", columnList = "up_time")
|
@Index(name = "idx_up_time", columnList = "up_time"),
|
||||||
|
@Index(name = "idx_is_delet", columnList = "is_delet")
|
||||||
})
|
})
|
||||||
public class CattleBreed {
|
public class CattleBreed {
|
||||||
|
|
||||||
@@ -36,10 +37,19 @@ public class CattleBreed {
|
|||||||
@Column(name = "up_time", nullable = false)
|
@Column(name = "up_time", nullable = false)
|
||||||
private LocalDateTime upTime;
|
private LocalDateTime upTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逻辑删除标志:0=未删除,1=已删除
|
||||||
|
*/
|
||||||
|
@Column(name = "is_delet", nullable = false, columnDefinition = "INT DEFAULT 0")
|
||||||
|
private Integer isDelet = 0;
|
||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
protected void onCreate() {
|
protected void onCreate() {
|
||||||
createTime = LocalDateTime.now();
|
createTime = LocalDateTime.now();
|
||||||
upTime = LocalDateTime.now();
|
upTime = LocalDateTime.now();
|
||||||
|
if (isDelet == null) {
|
||||||
|
isDelet = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreUpdate
|
@PreUpdate
|
||||||
@@ -79,5 +89,13 @@ public class CattleBreed {
|
|||||||
public void setUpTime(LocalDateTime upTime) {
|
public void setUpTime(LocalDateTime upTime) {
|
||||||
this.upTime = upTime;
|
this.upTime = upTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getIsDelet() {
|
||||||
|
return isDelet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsDelet(Integer isDelet) {
|
||||||
|
this.isDelet = isDelet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,11 +42,17 @@ public class CattleProvince {
|
|||||||
private String province;
|
private String province;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 省份均价
|
* 省份均价(存储最低价格)
|
||||||
*/
|
*/
|
||||||
@Column(name = "province_price", precision = 10, scale = 2)
|
@Column(name = "province_price", precision = 10, scale = 2)
|
||||||
private BigDecimal provincePrice;
|
private BigDecimal provincePrice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 省份均价区间(原始字符串,如 "13.4-14.9")
|
||||||
|
*/
|
||||||
|
@Column(name = "province_price_range", length = 50)
|
||||||
|
private String provincePriceRange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 23年存栏(万头)
|
* 23年存栏(万头)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package com.example.cattletends.entity;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 省份每日均价实体类
|
||||||
|
* 存储15个省份每天的省份均价,只累加不覆盖
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "province_daily_price",
|
||||||
|
uniqueConstraints = {
|
||||||
|
@UniqueConstraint(name = "uk_province_date", columnNames = {"province", "price_date"})
|
||||||
|
},
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_province", columnList = "province"),
|
||||||
|
@Index(name = "idx_price_date", columnList = "price_date"),
|
||||||
|
@Index(name = "idx_province_date_composite", columnList = "province,price_date"),
|
||||||
|
@Index(name = "idx_price", columnList = "price"),
|
||||||
|
@Index(name = "idx_date_price", columnList = "price_date,price"),
|
||||||
|
@Index(name = "idx_province_price", columnList = "province,price"),
|
||||||
|
@Index(name = "idx_up_time", columnList = "up_time"),
|
||||||
|
@Index(name = "idx_create_time", columnList = "create_time")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public class ProvinceDailyPrice {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 省份名称
|
||||||
|
*/
|
||||||
|
@Column(name = "province", nullable = false, length = 100)
|
||||||
|
private String province;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 省份均价(元/斤)
|
||||||
|
*/
|
||||||
|
@Column(name = "price", precision = 10, scale = 2, nullable = false)
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 价格日期(只记录日期,不包含时间)
|
||||||
|
*/
|
||||||
|
@Column(name = "price_date", nullable = false)
|
||||||
|
private LocalDate priceDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@Column(name = "create_time")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@Column(name = "up_time", nullable = false)
|
||||||
|
private LocalDateTime upTime;
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
createTime = LocalDateTime.now();
|
||||||
|
upTime = LocalDateTime.now();
|
||||||
|
if (priceDate == null) {
|
||||||
|
priceDate = LocalDate.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
upTime = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProvince() {
|
||||||
|
return province;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvince(String province) {
|
||||||
|
this.province = province;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrice(BigDecimal price) {
|
||||||
|
this.price = price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getPriceDate() {
|
||||||
|
return priceDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPriceDate(LocalDate priceDate) {
|
||||||
|
this.priceDate = priceDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(LocalDateTime createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpTime() {
|
||||||
|
return upTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpTime(LocalDateTime upTime) {
|
||||||
|
this.upTime = upTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -2,8 +2,10 @@ package com.example.cattletends.repository;
|
|||||||
|
|
||||||
import com.example.cattletends.entity.CattleBreed;
|
import com.example.cattletends.entity.CattleBreed;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,5 +23,11 @@ public interface CattleBreedRepository extends JpaRepository<CattleBreed, Intege
|
|||||||
* 检查品种名称是否存在
|
* 检查品种名称是否存在
|
||||||
*/
|
*/
|
||||||
boolean existsByBreedName(String breedName);
|
boolean existsByBreedName(String breedName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有未删除的品种(is_delet=0)
|
||||||
|
*/
|
||||||
|
@Query("SELECT b FROM CattleBreed b WHERE b.isDelet = 0 OR b.isDelet IS NULL")
|
||||||
|
List<CattleBreed> findAllNotDeleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -35,14 +37,87 @@ public interface CattleDataRepository extends JpaRepository<CattleData, Integer>
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据品种、省份、产地、价格查询牛只数据(用于判断重复)
|
* 根据品种、省份、产地、价格查询牛只数据(用于判断重复)
|
||||||
|
* 注意:使用 findFirstBy,避免因为存在多条相同记录导致 NonUniqueResultException
|
||||||
*
|
*
|
||||||
* @param type 品种
|
* @param type 品种
|
||||||
* @param province 省份
|
* @param province 省份
|
||||||
* @param location 产地
|
* @param location 产地
|
||||||
* @param price 价格
|
* @param price 价格
|
||||||
* @return 牛只数据(如果存在)
|
* @return 第一条匹配的牛只数据(如果存在)
|
||||||
*/
|
*/
|
||||||
Optional<CattleData> findByTypeAndProvinceAndLocationAndPrice(
|
Optional<CattleData> findFirstByTypeAndProvinceAndLocationAndPrice(
|
||||||
String type, String province, String location, BigDecimal price);
|
String type, String province, String location, BigDecimal price);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据品种和产地查询牛只数据(用于判断重复)
|
||||||
|
* 注意:使用 findFirstBy,避免因为存在多条相同记录导致 NonUniqueResultException
|
||||||
|
*
|
||||||
|
* @param type 品种
|
||||||
|
* @param location 产地
|
||||||
|
* @return 第一条匹配的牛只数据(如果存在)
|
||||||
|
*/
|
||||||
|
Optional<CattleData> findFirstByTypeAndLocation(String type, String location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据品种和产地查询所有匹配的牛只数据(用于删除重复记录)
|
||||||
|
* 使用 TRIM 函数来匹配,忽略前后空格
|
||||||
|
*
|
||||||
|
* @param type 品种
|
||||||
|
* @param location 产地
|
||||||
|
* @return 所有匹配的牛只数据列表
|
||||||
|
*/
|
||||||
|
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE TRIM(c.type) = TRIM(?1) AND TRIM(c.location) = TRIM(?2)")
|
||||||
|
List<CattleData> findByTypeAndLocation(String type, String location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有不重复的省份列表
|
||||||
|
*
|
||||||
|
* @return 省份列表
|
||||||
|
*/
|
||||||
|
@org.springframework.data.jpa.repository.Query("SELECT DISTINCT c.province FROM CattleData c WHERE c.province IS NOT NULL AND c.province != ''")
|
||||||
|
List<String> findAllDistinctProvinces();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据日期范围查询牛只数据(用于删除当天数据)
|
||||||
|
*
|
||||||
|
* @param startTime 开始时间(当天00:00:00)
|
||||||
|
* @param endTime 结束时间(当天23:59:59)
|
||||||
|
* @return 该日期范围内的所有数据
|
||||||
|
*/
|
||||||
|
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE c.createTime >= ?1 AND c.createTime <= ?2")
|
||||||
|
List<CattleData> findByCreateTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据日期查询当天的牛只数据(用于查询接口)
|
||||||
|
* 使用DATE()函数比较日期部分,忽略时间部分
|
||||||
|
*
|
||||||
|
* @param date 日期(只比较日期部分,忽略时间)
|
||||||
|
* @param sort 排序规则
|
||||||
|
* @return 当天的数据列表
|
||||||
|
*/
|
||||||
|
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE DATE(c.createTime) = DATE(?1)")
|
||||||
|
List<CattleData> findByCreateTimeDate(LocalDate date, Sort sort);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据省份和日期查询当天的牛只数据
|
||||||
|
*
|
||||||
|
* @param province 省份
|
||||||
|
* @param date 日期
|
||||||
|
* @param sort 排序规则
|
||||||
|
* @return 该省份当天的数据列表
|
||||||
|
*/
|
||||||
|
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE c.province = ?1 AND DATE(c.createTime) = DATE(?2)")
|
||||||
|
List<CattleData> findByProvinceAndCreateTimeDate(String province, LocalDate date, Sort sort);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据品种和日期查询当天的牛只数据
|
||||||
|
*
|
||||||
|
* @param type 品种
|
||||||
|
* @param date 日期
|
||||||
|
* @param sort 排序规则
|
||||||
|
* @return 该品种当天的数据列表
|
||||||
|
*/
|
||||||
|
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE c.type = ?1 AND DATE(c.createTime) = DATE(?2)")
|
||||||
|
List<CattleData> findByTypeAndCreateTimeDate(String type, LocalDate date, Sort sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import java.util.Optional;
|
|||||||
public interface CattleProvinceRepository extends JpaRepository<CattleProvince, Integer> {
|
public interface CattleProvinceRepository extends JpaRepository<CattleProvince, Integer> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据省份名称查询
|
* 根据省份名称查询(返回第一条记录)
|
||||||
*
|
*
|
||||||
* @param province 省份名称
|
* @param province 省份名称
|
||||||
* @return 省份数据
|
* @return 省份数据
|
||||||
*/
|
*/
|
||||||
Optional<CattleProvince> findByProvince(String province);
|
Optional<CattleProvince> findFirstByProvince(String province);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据省份名称查询(支持排序)
|
* 根据省份名称查询(支持排序)
|
||||||
@@ -30,5 +30,13 @@ public interface CattleProvinceRepository extends JpaRepository<CattleProvince,
|
|||||||
* @return 省份数据列表
|
* @return 省份数据列表
|
||||||
*/
|
*/
|
||||||
List<CattleProvince> findByProvince(String province, Sort sort);
|
List<CattleProvince> findByProvince(String province, Sort sort);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据省份名称查询所有记录(用于去重处理)
|
||||||
|
*
|
||||||
|
* @param province 省份名称
|
||||||
|
* @return 省份数据列表
|
||||||
|
*/
|
||||||
|
List<CattleProvince> findAllByProvince(String province);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.example.cattletends.repository;
|
||||||
|
|
||||||
|
import com.example.cattletends.entity.ProvinceDailyPrice;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 省份每日均价Repository
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface ProvinceDailyPriceRepository extends JpaRepository<ProvinceDailyPrice, Integer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据省份和日期查询(用于检查是否已存在,避免重复)
|
||||||
|
*/
|
||||||
|
Optional<ProvinceDailyPrice> findByProvinceAndPriceDate(String province, LocalDate priceDate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查省份和日期是否存在
|
||||||
|
*/
|
||||||
|
boolean existsByProvinceAndPriceDate(String province, LocalDate priceDate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据省份查询所有记录,按日期降序排序
|
||||||
|
*/
|
||||||
|
List<ProvinceDailyPrice> findByProvinceOrderByPriceDateDesc(String province);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有记录,按日期降序排序
|
||||||
|
*/
|
||||||
|
List<ProvinceDailyPrice> findAllByOrderByPriceDateDesc();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据省份和日期范围查询
|
||||||
|
*/
|
||||||
|
@Query("SELECT p FROM ProvinceDailyPrice p WHERE p.province = :province AND p.priceDate BETWEEN :startDate AND :endDate ORDER BY p.priceDate DESC")
|
||||||
|
List<ProvinceDailyPrice> findByProvinceAndDateRange(
|
||||||
|
@Param("province") String province,
|
||||||
|
@Param("startDate") LocalDate startDate,
|
||||||
|
@Param("endDate") LocalDate endDate
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据日期查询所有省份的每日均价记录,按省份名称升序排序
|
||||||
|
*/
|
||||||
|
List<ProvinceDailyPrice> findByPriceDateOrderByProvinceAsc(LocalDate priceDate);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -28,5 +28,11 @@ public interface CattleBreedService {
|
|||||||
* @return 品种实体
|
* @return 品种实体
|
||||||
*/
|
*/
|
||||||
CattleBreed getBreedByName(String breedName);
|
CattleBreed getBreedByName(String breedName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新品种的逻辑删除状态
|
||||||
|
* @param breedNamesInExcel Excel中存在的品种名称集合
|
||||||
|
*/
|
||||||
|
void updateBreedDeleteStatus(java.util.Set<String> breedNamesInExcel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.example.cattletends.dto.CattleDataDTO;
|
|||||||
import com.example.cattletends.dto.ImportResult;
|
import com.example.cattletends.dto.ImportResult;
|
||||||
import com.example.cattletends.entity.CattleData;
|
import com.example.cattletends.entity.CattleData;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -15,25 +16,28 @@ public interface CattleDataService {
|
|||||||
/**
|
/**
|
||||||
* 获取所有牛只数据
|
* 获取所有牛只数据
|
||||||
*
|
*
|
||||||
|
* @param date 日期(可选,如果为null则查询今天的数据)
|
||||||
* @return 牛只数据列表
|
* @return 牛只数据列表
|
||||||
*/
|
*/
|
||||||
List<CattleData> getAllCattleData();
|
List<CattleData> getAllCattleData(LocalDate date);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据省份获取牛只数据
|
* 根据省份获取牛只数据
|
||||||
*
|
*
|
||||||
* @param province 省份
|
* @param province 省份
|
||||||
|
* @param date 日期(可选,如果为null则查询今天的数据)
|
||||||
* @return 牛只数据列表
|
* @return 牛只数据列表
|
||||||
*/
|
*/
|
||||||
List<CattleData> getCattleDataByProvince(String province);
|
List<CattleData> getCattleDataByProvince(String province, LocalDate date);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据品种获取牛只数据
|
* 根据品种获取牛只数据
|
||||||
*
|
*
|
||||||
* @param type 品种
|
* @param type 品种
|
||||||
|
* @param date 日期(可选,如果为null则查询今天的数据)
|
||||||
* @return 牛只数据列表
|
* @return 牛只数据列表
|
||||||
*/
|
*/
|
||||||
List<CattleData> getCattleDataByType(String type);
|
List<CattleData> getCattleDataByType(String type, LocalDate date);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据ID获取牛只数据
|
* 根据ID获取牛只数据
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.example.cattletends.service;
|
||||||
|
|
||||||
|
import com.example.cattletends.entity.ProvinceDailyPrice;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 省份每日均价服务接口
|
||||||
|
*/
|
||||||
|
public interface ProvinceDailyPriceService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存或更新省份每日均价(如果当天该省份已有数据,则更新价格)
|
||||||
|
* @param province 省份名称
|
||||||
|
* @param price 省份均价
|
||||||
|
* @param priceDate 价格日期(如果为null,则使用当前日期)
|
||||||
|
* @return 保存或更新的实体,isNew=true表示新建,isNew=false表示更新
|
||||||
|
*/
|
||||||
|
ProvinceDailyPrice saveOrUpdate(String province, java.math.BigDecimal price, LocalDate priceDate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据省份查询所有每日均价记录,按日期降序排序
|
||||||
|
* @param province 省份名称
|
||||||
|
* @return 省份每日均价列表
|
||||||
|
*/
|
||||||
|
List<ProvinceDailyPrice> getByProvince(String province);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有省份每日均价记录,按日期降序排序
|
||||||
|
* @return 所有省份每日均价列表
|
||||||
|
*/
|
||||||
|
List<ProvinceDailyPrice> getAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据省份和日期范围查询
|
||||||
|
* @param province 省份名称
|
||||||
|
* @param startDate 开始日期
|
||||||
|
* @param endDate 结束日期
|
||||||
|
* @return 省份每日均价列表
|
||||||
|
*/
|
||||||
|
List<ProvinceDailyPrice> getByProvinceAndDateRange(String province, LocalDate startDate, LocalDate endDate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据日期查询所有省份的每日均价记录
|
||||||
|
* @param priceDate 价格日期
|
||||||
|
* @return 省份每日均价列表
|
||||||
|
*/
|
||||||
|
List<ProvinceDailyPrice> getByPriceDate(LocalDate priceDate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据省份和日期查询
|
||||||
|
* @param province 省份名称
|
||||||
|
* @param priceDate 价格日期
|
||||||
|
* @return 省份每日均价(如果存在)
|
||||||
|
*/
|
||||||
|
java.util.Optional<ProvinceDailyPrice> getByProvinceAndPriceDate(String province, LocalDate priceDate);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -31,21 +31,139 @@ public class CattleBreedServiceImpl implements CattleBreedService {
|
|||||||
// 先查询是否已存在
|
// 先查询是否已存在
|
||||||
Optional<CattleBreed> existing = cattleBreedRepository.findByBreedName(trimmedName);
|
Optional<CattleBreed> existing = cattleBreedRepository.findByBreedName(trimmedName);
|
||||||
if (existing.isPresent()) {
|
if (existing.isPresent()) {
|
||||||
System.out.println("品种已存在: " + trimmedName);
|
CattleBreed breed = existing.get();
|
||||||
return existing.get();
|
// 如果品种被标记为删除,恢复它(设置为未删除)
|
||||||
|
if (breed.getIsDelet() != null && breed.getIsDelet() == 1) {
|
||||||
|
breed.setIsDelet(0);
|
||||||
|
breed = cattleBreedRepository.save(breed);
|
||||||
|
System.out.println("恢复已删除的品种: " + trimmedName);
|
||||||
|
} else {
|
||||||
|
System.out.println("品种已存在: " + trimmedName);
|
||||||
|
}
|
||||||
|
return breed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不存在则创建
|
// 不存在则创建
|
||||||
CattleBreed breed = new CattleBreed();
|
CattleBreed breed = new CattleBreed();
|
||||||
breed.setBreedName(trimmedName);
|
breed.setBreedName(trimmedName);
|
||||||
|
breed.setIsDelet(0); // 新创建的品种默认为未删除
|
||||||
CattleBreed saved = cattleBreedRepository.save(breed);
|
CattleBreed saved = cattleBreedRepository.save(breed);
|
||||||
System.out.println("创建新品种: " + trimmedName + " (ID: " + saved.getId() + ")");
|
System.out.println("创建新品种: " + trimmedName + " (ID: " + saved.getId() + ")");
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public List<CattleBreed> getAllBreeds() {
|
public List<CattleBreed> getAllBreeds() {
|
||||||
return cattleBreedRepository.findAll();
|
// 只查询未删除的品种(is_delet=0)
|
||||||
|
List<CattleBreed> dataList = cattleBreedRepository.findAllNotDeleted();
|
||||||
|
|
||||||
|
// 去重处理:如果同一个品种名称有多条记录,保留最新的一条(up_time最大),更新其他记录
|
||||||
|
if (dataList != null && !dataList.isEmpty()) {
|
||||||
|
System.out.println("========== 开始品种去重处理 ==========");
|
||||||
|
System.out.println("去重前记录数: " + dataList.size());
|
||||||
|
|
||||||
|
// 按品种名称分组,找出每个品种的最新记录
|
||||||
|
java.util.Map<String, CattleBreed> breedMap = new java.util.HashMap<>();
|
||||||
|
java.util.List<CattleBreed> duplicatesToUpdate = new java.util.ArrayList<>();
|
||||||
|
|
||||||
|
for (CattleBreed item : dataList) {
|
||||||
|
String breedName = item.getBreedName();
|
||||||
|
if (breedName == null || breedName.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimmedName = breedName.trim();
|
||||||
|
|
||||||
|
if (!breedMap.containsKey(trimmedName)) {
|
||||||
|
// 第一次遇到该品种,直接添加
|
||||||
|
breedMap.put(trimmedName, item);
|
||||||
|
} else {
|
||||||
|
// 已存在该品种,比较更新时间,保留最新的
|
||||||
|
CattleBreed existing = breedMap.get(trimmedName);
|
||||||
|
if (item.getUpTime() != null && existing.getUpTime() != null) {
|
||||||
|
if (item.getUpTime().isAfter(existing.getUpTime())) {
|
||||||
|
// 当前记录更新,标记旧记录需要更新
|
||||||
|
duplicatesToUpdate.add(existing);
|
||||||
|
breedMap.put(trimmedName, item);
|
||||||
|
} else {
|
||||||
|
// 现有记录更新,标记当前记录需要更新
|
||||||
|
duplicatesToUpdate.add(item);
|
||||||
|
}
|
||||||
|
} else if (item.getUpTime() != null) {
|
||||||
|
// 当前记录有更新时间,保留它
|
||||||
|
duplicatesToUpdate.add(existing);
|
||||||
|
breedMap.put(trimmedName, item);
|
||||||
|
} else if (existing.getUpTime() != null) {
|
||||||
|
// 现有记录有更新时间,保留现有记录
|
||||||
|
duplicatesToUpdate.add(item);
|
||||||
|
} else {
|
||||||
|
// 都没有更新时间,保留ID较小的(通常是最早创建的)
|
||||||
|
if (item.getId() < existing.getId()) {
|
||||||
|
duplicatesToUpdate.add(existing);
|
||||||
|
breedMap.put(trimmedName, item);
|
||||||
|
} else {
|
||||||
|
duplicatesToUpdate.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新重复记录:将旧记录的数据更新为最新记录的数据(实际上品种只有名称,所以只需要更新 up_time)
|
||||||
|
int updatedCount = 0;
|
||||||
|
for (CattleBreed duplicate : duplicatesToUpdate) {
|
||||||
|
CattleBreed latest = breedMap.get(duplicate.getBreedName().trim());
|
||||||
|
if (latest != null && !latest.getId().equals(duplicate.getId())) {
|
||||||
|
// 更新旧记录的 up_time 为最新记录的 up_time
|
||||||
|
duplicate.setUpTime(latest.getUpTime());
|
||||||
|
cattleBreedRepository.save(duplicate);
|
||||||
|
updatedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果更新了重复记录,重新查询
|
||||||
|
if (updatedCount > 0) {
|
||||||
|
System.out.println("更新了 " + updatedCount + " 条重复品种记录");
|
||||||
|
dataList = cattleBreedRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终去重:按品种名称去重,每个品种只保留一条(保留最新的)
|
||||||
|
java.util.Map<String, CattleBreed> finalMap = new java.util.LinkedHashMap<>();
|
||||||
|
for (CattleBreed item : dataList) {
|
||||||
|
String breedName = item.getBreedName();
|
||||||
|
if (breedName == null || breedName.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimmedName = breedName.trim();
|
||||||
|
|
||||||
|
if (!finalMap.containsKey(trimmedName)) {
|
||||||
|
finalMap.put(trimmedName, item);
|
||||||
|
} else {
|
||||||
|
CattleBreed existing = finalMap.get(trimmedName);
|
||||||
|
if (item.getUpTime() != null && existing.getUpTime() != null) {
|
||||||
|
if (item.getUpTime().isAfter(existing.getUpTime())) {
|
||||||
|
finalMap.put(trimmedName, item);
|
||||||
|
}
|
||||||
|
} else if (item.getUpTime() != null) {
|
||||||
|
finalMap.put(trimmedName, item);
|
||||||
|
} else if (existing.getUpTime() != null) {
|
||||||
|
// 保持现有记录
|
||||||
|
} else {
|
||||||
|
// 都没有更新时间,保留ID较小的
|
||||||
|
if (item.getId() < existing.getId()) {
|
||||||
|
finalMap.put(trimmedName, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataList = new java.util.ArrayList<>(finalMap.values());
|
||||||
|
System.out.println("去重后记录数: " + dataList.size());
|
||||||
|
System.out.println("========== 品种去重处理完成 ==========");
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,5 +174,64 @@ public class CattleBreedServiceImpl implements CattleBreedService {
|
|||||||
return cattleBreedRepository.findByBreedName(breedName.trim())
|
return cattleBreedRepository.findByBreedName(breedName.trim())
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void updateBreedDeleteStatus(java.util.Set<String> breedNamesInExcel) {
|
||||||
|
System.out.println("========== 开始更新品种逻辑删除状态 ==========");
|
||||||
|
|
||||||
|
// 获取所有品种(包括已删除的)
|
||||||
|
List<CattleBreed> allBreeds = cattleBreedRepository.findAll();
|
||||||
|
System.out.println("数据库中总品种数: " + (allBreeds != null ? allBreeds.size() : 0));
|
||||||
|
System.out.println("Excel中的品种: " + (breedNamesInExcel != null ? breedNamesInExcel.size() : 0) + " 个");
|
||||||
|
|
||||||
|
if (allBreeds == null || allBreeds.isEmpty()) {
|
||||||
|
System.out.println("数据库中没有品种数据,跳过逻辑删除状态更新");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int restoredCount = 0; // 恢复的品种数(从已删除改为未删除)
|
||||||
|
int deletedCount = 0; // 标记为删除的品种数
|
||||||
|
|
||||||
|
// 遍历所有品种,更新逻辑删除状态
|
||||||
|
for (CattleBreed breed : allBreeds) {
|
||||||
|
if (breed == null || breed.getBreedName() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String breedName = breed.getBreedName().trim();
|
||||||
|
boolean existsInExcel = breedNamesInExcel != null && breedNamesInExcel.contains(breedName);
|
||||||
|
|
||||||
|
Integer currentIsDelet = breed.getIsDelet();
|
||||||
|
if (currentIsDelet == null) {
|
||||||
|
currentIsDelet = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsInExcel) {
|
||||||
|
// Excel中存在该品种,确保is_delet=0(未删除)
|
||||||
|
if (currentIsDelet != 0) {
|
||||||
|
breed.setIsDelet(0);
|
||||||
|
cattleBreedRepository.save(breed);
|
||||||
|
restoredCount++;
|
||||||
|
System.out.println("✓ 恢复品种: " + breedName + " (从已删除改为未删除)");
|
||||||
|
} else {
|
||||||
|
System.out.println("○ 品种已存在且未删除: " + breedName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Excel中不存在该品种,设置is_delet=1(已删除)
|
||||||
|
if (currentIsDelet != 1) {
|
||||||
|
breed.setIsDelet(1);
|
||||||
|
cattleBreedRepository.save(breed);
|
||||||
|
deletedCount++;
|
||||||
|
System.out.println("✗ 标记品种为已删除: " + breedName);
|
||||||
|
} else {
|
||||||
|
System.out.println("○ 品种已标记为删除: " + breedName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("品种逻辑删除状态更新完成: 恢复 " + restoredCount + " 个,标记删除 " + deletedCount + " 个");
|
||||||
|
System.out.println("========== 品种逻辑删除状态更新完成 ==========");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ import com.example.cattletends.dto.ImportResult;
|
|||||||
import com.example.cattletends.entity.CattleData;
|
import com.example.cattletends.entity.CattleData;
|
||||||
import com.example.cattletends.repository.CattleDataRepository;
|
import com.example.cattletends.repository.CattleDataRepository;
|
||||||
import com.example.cattletends.service.CattleDataService;
|
import com.example.cattletends.service.CattleDataService;
|
||||||
|
import com.example.cattletends.service.ProvinceDailyPriceService;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -22,33 +24,193 @@ import java.util.Map;
|
|||||||
public class CattleDataServiceImpl implements CattleDataService {
|
public class CattleDataServiceImpl implements CattleDataService {
|
||||||
|
|
||||||
private final CattleDataRepository cattleDataRepository;
|
private final CattleDataRepository cattleDataRepository;
|
||||||
|
private final ProvinceDailyPriceService provinceDailyPriceService;
|
||||||
|
private final com.example.cattletends.service.CattleProvinceService cattleProvinceService;
|
||||||
|
private final com.example.cattletends.repository.CattleProvinceRepository cattleProvinceRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造器注入
|
* 构造器注入
|
||||||
*/
|
*/
|
||||||
public CattleDataServiceImpl(CattleDataRepository cattleDataRepository) {
|
public CattleDataServiceImpl(CattleDataRepository cattleDataRepository,
|
||||||
|
ProvinceDailyPriceService provinceDailyPriceService,
|
||||||
|
com.example.cattletends.service.CattleProvinceService cattleProvinceService,
|
||||||
|
com.example.cattletends.repository.CattleProvinceRepository cattleProvinceRepository) {
|
||||||
this.cattleDataRepository = cattleDataRepository;
|
this.cattleDataRepository = cattleDataRepository;
|
||||||
|
this.provinceDailyPriceService = provinceDailyPriceService;
|
||||||
|
this.cattleProvinceService = cattleProvinceService;
|
||||||
|
this.cattleProvinceRepository = cattleProvinceRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional
|
||||||
public List<CattleData> getAllCattleData() {
|
public List<CattleData> getAllCattleData(LocalDate date) {
|
||||||
// 按价格升序排序
|
// 如果date为null,使用今天
|
||||||
return cattleDataRepository.findAll(Sort.by(Sort.Direction.ASC, "price"));
|
LocalDate queryDate = date != null ? date : LocalDate.now();
|
||||||
|
Sort sort = Sort.by(Sort.Direction.ASC, "price");
|
||||||
|
|
||||||
|
// 查询指定日期的数据
|
||||||
|
List<CattleData> dataList = cattleDataRepository.findByCreateTimeDate(queryDate, sort);
|
||||||
|
return dataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional
|
||||||
public List<CattleData> getCattleDataByProvince(String province) {
|
public List<CattleData> getCattleDataByProvince(String province, LocalDate date) {
|
||||||
// 按价格升序排序
|
// 如果date为null,使用今天
|
||||||
return cattleDataRepository.findByProvince(province, Sort.by(Sort.Direction.ASC, "price"));
|
LocalDate queryDate = date != null ? date : LocalDate.now();
|
||||||
|
Sort sort = Sort.by(Sort.Direction.ASC, "price");
|
||||||
|
|
||||||
|
// 查询指定日期和省份的数据
|
||||||
|
List<CattleData> dataList = cattleDataRepository.findByProvinceAndCreateTimeDate(province.trim(), queryDate, sort);
|
||||||
|
return dataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional
|
||||||
public List<CattleData> getCattleDataByType(String type) {
|
public List<CattleData> getCattleDataByType(String type, LocalDate date) {
|
||||||
// 按价格升序排序
|
// 如果date为null,使用今天
|
||||||
return cattleDataRepository.findByType(type, Sort.by(Sort.Direction.ASC, "price"));
|
LocalDate queryDate = date != null ? date : LocalDate.now();
|
||||||
|
Sort sort = Sort.by(Sort.Direction.ASC, "price");
|
||||||
|
|
||||||
|
// 查询指定日期和品种的数据
|
||||||
|
List<CattleData> dataList = cattleDataRepository.findByTypeAndCreateTimeDate(type.trim(), queryDate, sort);
|
||||||
|
return dataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 location 字段去重处理
|
||||||
|
* 如果同一个 location 有多条记录,保留最新的一条(up_time最大),更新其他记录
|
||||||
|
* @param dataList 原始数据列表
|
||||||
|
* @param province 省份筛选条件(可选,用于重新查询时保持筛选)
|
||||||
|
* @param type 品种筛选条件(可选,用于重新查询时保持筛选)
|
||||||
|
*/
|
||||||
|
private List<CattleData> deduplicateByLocation(List<CattleData> dataList, String province, String type) {
|
||||||
|
if (dataList == null || dataList.isEmpty()) {
|
||||||
|
return dataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("[查询去重] 开始按 type + location 去重处理");
|
||||||
|
System.out.println("[查询去重] 去重前记录数: " + dataList.size());
|
||||||
|
|
||||||
|
// 按 type + location 分组,找出每个 type + location 组合的最新记录
|
||||||
|
java.util.Map<String, CattleData> typeLocationMap = new java.util.HashMap<>();
|
||||||
|
java.util.List<CattleData> duplicatesToUpdate = new java.util.ArrayList<>();
|
||||||
|
|
||||||
|
for (CattleData item : dataList) {
|
||||||
|
String itemType = item.getType();
|
||||||
|
String location = item.getLocation();
|
||||||
|
if ((itemType == null || itemType.trim().isEmpty()) ||
|
||||||
|
(location == null || location.trim().isEmpty())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimmedType = itemType.trim();
|
||||||
|
String trimmedLocation = location.trim();
|
||||||
|
String key = trimmedType + "|" + trimmedLocation; // 使用 | 作为分隔符
|
||||||
|
|
||||||
|
if (!typeLocationMap.containsKey(key)) {
|
||||||
|
// 第一次遇到该 type + location 组合,直接添加
|
||||||
|
typeLocationMap.put(key, item);
|
||||||
|
} else {
|
||||||
|
// 已存在该 type + location 组合,比较更新时间,保留最新的
|
||||||
|
CattleData existing = typeLocationMap.get(key);
|
||||||
|
if (item.getUpTime() != null && existing.getUpTime() != null) {
|
||||||
|
if (item.getUpTime().isAfter(existing.getUpTime())) {
|
||||||
|
// 当前记录更新,标记旧记录需要更新
|
||||||
|
duplicatesToUpdate.add(existing);
|
||||||
|
typeLocationMap.put(key, item);
|
||||||
|
} else {
|
||||||
|
// 现有记录更新,标记当前记录需要更新
|
||||||
|
duplicatesToUpdate.add(item);
|
||||||
|
}
|
||||||
|
} else if (item.getUpTime() != null) {
|
||||||
|
// 当前记录有更新时间,保留它
|
||||||
|
duplicatesToUpdate.add(existing);
|
||||||
|
typeLocationMap.put(key, item);
|
||||||
|
} else if (existing.getUpTime() != null) {
|
||||||
|
// 现有记录有更新时间,保留现有记录
|
||||||
|
duplicatesToUpdate.add(item);
|
||||||
|
} else {
|
||||||
|
// 都没有更新时间,保留ID较小的(通常是最早创建的)
|
||||||
|
if (item.getId() < existing.getId()) {
|
||||||
|
duplicatesToUpdate.add(existing);
|
||||||
|
typeLocationMap.put(key, item);
|
||||||
|
} else {
|
||||||
|
duplicatesToUpdate.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除重复记录:只保留最新的记录,删除其他所有重复记录
|
||||||
|
int deletedCount = 0;
|
||||||
|
for (CattleData duplicate : duplicatesToUpdate) {
|
||||||
|
String duplicateType = duplicate.getType() != null ? duplicate.getType().trim() : "";
|
||||||
|
String duplicateLocation = duplicate.getLocation() != null ? duplicate.getLocation().trim() : "";
|
||||||
|
String key = duplicateType + "|" + duplicateLocation;
|
||||||
|
CattleData latest = typeLocationMap.get(key);
|
||||||
|
if (latest != null && !latest.getId().equals(duplicate.getId())) {
|
||||||
|
// 删除重复记录,只保留最新的
|
||||||
|
System.out.println("[查询去重] 删除重复记录: ID=" + duplicate.getId() + ", type=[" + duplicateType + "], location=[" + duplicateLocation + "]");
|
||||||
|
cattleDataRepository.delete(duplicate);
|
||||||
|
deletedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果删除了重复记录,重新查询(保持原有的筛选条件)
|
||||||
|
if (deletedCount > 0) {
|
||||||
|
System.out.println("[查询去重] 删除了 " + deletedCount + " 条重复记录");
|
||||||
|
// 重新查询数据(保持原有排序和筛选条件)
|
||||||
|
Sort sort = Sort.by(Sort.Direction.ASC, "price");
|
||||||
|
if (type != null && !type.trim().isEmpty()) {
|
||||||
|
dataList = cattleDataRepository.findByType(type.trim(), sort);
|
||||||
|
} else if (province != null && !province.trim().isEmpty()) {
|
||||||
|
dataList = cattleDataRepository.findByProvince(province.trim(), sort);
|
||||||
|
} else {
|
||||||
|
dataList = cattleDataRepository.findAll(sort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终去重:按 type + location 去重,每个 type + location 组合只保留一条(保留最新的)
|
||||||
|
java.util.Map<String, CattleData> finalMap = new java.util.LinkedHashMap<>();
|
||||||
|
for (CattleData item : dataList) {
|
||||||
|
String itemType = item.getType();
|
||||||
|
String location = item.getLocation();
|
||||||
|
if ((itemType == null || itemType.trim().isEmpty()) ||
|
||||||
|
(location == null || location.trim().isEmpty())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimmedType = itemType.trim();
|
||||||
|
String trimmedLocation = location.trim();
|
||||||
|
String key = trimmedType + "|" + trimmedLocation;
|
||||||
|
|
||||||
|
if (!finalMap.containsKey(key)) {
|
||||||
|
finalMap.put(key, item);
|
||||||
|
} else {
|
||||||
|
CattleData existing = finalMap.get(key);
|
||||||
|
if (item.getUpTime() != null && existing.getUpTime() != null) {
|
||||||
|
if (item.getUpTime().isAfter(existing.getUpTime())) {
|
||||||
|
finalMap.put(key, item);
|
||||||
|
}
|
||||||
|
} else if (item.getUpTime() != null) {
|
||||||
|
finalMap.put(key, item);
|
||||||
|
} else if (existing.getUpTime() != null) {
|
||||||
|
// 保持现有记录
|
||||||
|
} else {
|
||||||
|
// 都没有更新时间,保留ID较小的
|
||||||
|
if (item.getId() < existing.getId()) {
|
||||||
|
finalMap.put(key, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataList = new java.util.ArrayList<>(finalMap.values());
|
||||||
|
System.out.println("[查询去重] 去重后记录数: " + dataList.size());
|
||||||
|
System.out.println("[查询去重] 去重处理完成");
|
||||||
|
|
||||||
|
return dataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -97,40 +259,215 @@ public class CattleDataServiceImpl implements CattleDataService {
|
|||||||
public ImportResult batchImportCattleData(List<CattleDataDTO> dataList) {
|
public ImportResult batchImportCattleData(List<CattleDataDTO> dataList) {
|
||||||
List<CattleData> savedList = new ArrayList<>();
|
List<CattleData> savedList = new ArrayList<>();
|
||||||
int newCount = 0;
|
int newCount = 0;
|
||||||
int updateCount = 0;
|
int updateCount = 0; // 保留字段以兼容返回类型,但始终为0
|
||||||
|
|
||||||
|
// 确定导入日期:统一使用导入时的当前日期
|
||||||
|
LocalDate importDate = LocalDate.now();
|
||||||
|
LocalDateTime startOfDay = importDate.atStartOfDay(); // 当天 00:00:00
|
||||||
|
LocalDateTime endOfDay = importDate.atTime(23, 59, 59, 999999999); // 当天 23:59:59.999999999
|
||||||
|
|
||||||
|
System.out.println("========== 开始导入数据,导入日期: " + importDate + " ==========");
|
||||||
|
|
||||||
|
// 删除当天所有已存在的数据
|
||||||
|
List<CattleData> todayData = cattleDataRepository.findByCreateTimeBetween(startOfDay, endOfDay);
|
||||||
|
if (todayData != null && !todayData.isEmpty()) {
|
||||||
|
System.out.println("========== 删除当天已存在的数据: " + todayData.size() + " 条 ==========");
|
||||||
|
cattleDataRepository.deleteAll(todayData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只新增,不做去重和更新
|
||||||
|
LocalDateTime importDateTime = LocalDateTime.now(); // 统一使用导入时的当前时间
|
||||||
|
|
||||||
for (CattleDataDTO dto : dataList) {
|
for (CattleDataDTO dto : dataList) {
|
||||||
// 检查是否存在重复数据(type + province + location + price)
|
// 验证必填字段
|
||||||
CattleData existingData = cattleDataRepository
|
String type = dto.getType() != null ? dto.getType().trim() : null;
|
||||||
.findByTypeAndProvinceAndLocationAndPrice(
|
String location = dto.getLocation() != null ? dto.getLocation().trim() : null;
|
||||||
dto.getType(),
|
|
||||||
dto.getProvince(),
|
|
||||||
dto.getLocation(),
|
|
||||||
dto.getPrice()
|
|
||||||
)
|
|
||||||
.orElse(null);
|
|
||||||
|
|
||||||
if (existingData != null) {
|
if (type == null || type.isEmpty() || location == null || location.isEmpty()) {
|
||||||
// 如果存在重复数据,只更新 up_time(保留原有的 create_time)
|
continue; // 跳过无效数据
|
||||||
existingData.setUpTime(LocalDateTime.now());
|
|
||||||
savedList.add(cattleDataRepository.save(existingData));
|
|
||||||
updateCount++;
|
|
||||||
} else {
|
|
||||||
// 如果不存在,创建新记录
|
|
||||||
CattleData cattleData = new CattleData();
|
|
||||||
cattleData.setType(dto.getType());
|
|
||||||
cattleData.setProvince(dto.getProvince());
|
|
||||||
cattleData.setLocation(dto.getLocation());
|
|
||||||
cattleData.setPrice(dto.getPrice());
|
|
||||||
if (dto.getCreateTime() != null) {
|
|
||||||
cattleData.setCreateTime(dto.getCreateTime());
|
|
||||||
}
|
|
||||||
savedList.add(cattleDataRepository.save(cattleData));
|
|
||||||
newCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建新记录
|
||||||
|
CattleData cattleData = new CattleData();
|
||||||
|
cattleData.setType(type);
|
||||||
|
cattleData.setProvince(dto.getProvince() != null ? dto.getProvince().trim() : null);
|
||||||
|
cattleData.setLocation(location);
|
||||||
|
cattleData.setPrice(dto.getPrice());
|
||||||
|
|
||||||
|
// 统一设置create_time为导入时的当前时间
|
||||||
|
cattleData.setCreateTime(importDateTime);
|
||||||
|
|
||||||
|
CattleData saved = cattleDataRepository.save(cattleData);
|
||||||
|
savedList.add(saved);
|
||||||
|
newCount++;
|
||||||
|
System.out.println("[新增] 创建新记录: ID=" + saved.getId() + ", type=[" + type + "], location=[" + location + "], price=" + dto.getPrice());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.out.println("========== 导入完成: 新增 " + newCount + " 条数据 ==========");
|
||||||
|
|
||||||
|
// 导入完成后,计算当天省份的平均价格并更新省份每日均价
|
||||||
|
System.out.println("========== 开始计算当天省份平均价格并更新每日均价 ==========");
|
||||||
|
calculateAndUpdateProvinceDailyPrices(importDate);
|
||||||
|
System.out.println("========== 省份平均价格计算完成 ==========");
|
||||||
|
|
||||||
return new ImportResult(savedList, newCount, updateCount);
|
return new ImportResult(savedList, newCount, updateCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算当天省份的平均价格并更新省份每日均价
|
||||||
|
* 只计算当天导入的数据
|
||||||
|
*
|
||||||
|
* @param importDate 导入日期
|
||||||
|
*/
|
||||||
|
private void calculateAndUpdateProvinceDailyPrices(LocalDate importDate) {
|
||||||
|
try {
|
||||||
|
System.out.println("导入日期: " + importDate);
|
||||||
|
|
||||||
|
// 获取所有15个省份(从 cattleprovince 表)
|
||||||
|
List<com.example.cattletends.entity.CattleProvince> allProvinces = cattleProvinceService.getAllProvinceData(null);
|
||||||
|
System.out.println("获取到 " + (allProvinces != null ? allProvinces.size() : 0) + " 个省份");
|
||||||
|
|
||||||
|
if (allProvinces == null || allProvinces.isEmpty()) {
|
||||||
|
System.out.println("没有找到省份数据,跳过省份平均价格计算");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只查询当天导入的数据
|
||||||
|
Sort sort = Sort.by(Sort.Direction.ASC, "price");
|
||||||
|
List<CattleData> todayDataList = cattleDataRepository.findByCreateTimeDate(importDate, sort);
|
||||||
|
|
||||||
|
if (todayDataList == null || todayDataList.isEmpty()) {
|
||||||
|
System.out.println("当天没有导入数据,跳过省份平均价格计算");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("当天导入的数据条数: " + todayDataList.size());
|
||||||
|
|
||||||
|
int updatedCount = 0;
|
||||||
|
int createdCount = 0;
|
||||||
|
|
||||||
|
// 按省份分组,计算每个省份的平均价格
|
||||||
|
java.util.Map<String, java.util.Map<String, Object>> provinceStats = new java.util.HashMap<>();
|
||||||
|
|
||||||
|
for (CattleData data : todayDataList) {
|
||||||
|
if (data.getProvince() == null || data.getProvince().trim().isEmpty() || data.getPrice() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalProvince = data.getProvince().trim();
|
||||||
|
String canonicalProvince = resolveProvinceNameForMatch(originalProvince);
|
||||||
|
|
||||||
|
if (provinceStats.containsKey(canonicalProvince)) {
|
||||||
|
java.util.Map<String, Object> existing = provinceStats.get(canonicalProvince);
|
||||||
|
java.math.BigDecimal existingSum = (java.math.BigDecimal) existing.get("sum");
|
||||||
|
int existingCount = (Integer) existing.get("count");
|
||||||
|
existingSum = existingSum.add(data.getPrice());
|
||||||
|
existingCount++;
|
||||||
|
existing.put("sum", existingSum);
|
||||||
|
existing.put("count", existingCount);
|
||||||
|
} else {
|
||||||
|
java.util.Map<String, Object> stats = new java.util.HashMap<>();
|
||||||
|
stats.put("sum", data.getPrice());
|
||||||
|
stats.put("count", 1);
|
||||||
|
provinceStats.put(canonicalProvince, stats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算每个省份的平均价格并更新
|
||||||
|
for (java.util.Map.Entry<String, java.util.Map<String, Object>> entry : provinceStats.entrySet()) {
|
||||||
|
String province = entry.getKey();
|
||||||
|
java.util.Map<String, Object> stats = entry.getValue();
|
||||||
|
java.math.BigDecimal totalSum = (java.math.BigDecimal) stats.get("sum");
|
||||||
|
int totalCount = (Integer) stats.get("count");
|
||||||
|
|
||||||
|
// 计算平均值
|
||||||
|
java.math.BigDecimal averagePrice = totalSum.divide(
|
||||||
|
java.math.BigDecimal.valueOf(totalCount),
|
||||||
|
2,
|
||||||
|
java.math.RoundingMode.HALF_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println("省份: " + province + ", 当天数据条数: " + totalCount + ", 平均价格: " + averagePrice);
|
||||||
|
|
||||||
|
// 更新 cattleprovince 表的 province_price 字段
|
||||||
|
java.util.Optional<com.example.cattletends.entity.CattleProvince> cattleProvinceOpt =
|
||||||
|
cattleProvinceRepository.findFirstByProvince(province);
|
||||||
|
if (cattleProvinceOpt.isPresent()) {
|
||||||
|
com.example.cattletends.entity.CattleProvince cattleProvince = cattleProvinceOpt.get();
|
||||||
|
cattleProvince.setProvincePrice(averagePrice);
|
||||||
|
cattleProvinceRepository.save(cattleProvince);
|
||||||
|
System.out.println("✓ 更新 cattleprovince 表的 province_price: " + province + ", 价格: " + averagePrice);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新或创建省份每日均价记录
|
||||||
|
provinceDailyPriceService.saveOrUpdate(province, averagePrice, importDate);
|
||||||
|
updatedCount++;
|
||||||
|
System.out.println("✓ 更新省份每日均价: " + province + ", 日期: " + importDate + ", 价格: " + averagePrice);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对于没有数据的省份,使用省份表中的省份均价(如果有)
|
||||||
|
for (com.example.cattletends.entity.CattleProvince province : allProvinces) {
|
||||||
|
String provinceName = province.getProvince();
|
||||||
|
if (provinceName == null || provinceName.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimmedProvince = provinceName.trim();
|
||||||
|
if (!provinceStats.containsKey(trimmedProvince)) {
|
||||||
|
// 没有当天数据,使用省份表中的省份均价
|
||||||
|
if (province.getProvincePrice() != null) {
|
||||||
|
provinceDailyPriceService.saveOrUpdate(trimmedProvince, province.getProvincePrice(), importDate);
|
||||||
|
createdCount++;
|
||||||
|
System.out.println("✓ 创建省份每日均价(使用省份均价): " + trimmedProvince + ", 日期: " + importDate + ", 价格: " + province.getProvincePrice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("省份每日均价处理完成: 更新 " + updatedCount + " 条,创建 " + createdCount + " 条");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("计算省份平均价格失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将牛只数据中的省份名称,转换为与 cattleprovince / province_daily_price 表中一致的标准省份名称
|
||||||
|
* 例如:把“安徽省”“河北省”“新疆维吾尔自治区”等映射为“安徽”“河北”“新疆”等
|
||||||
|
*/
|
||||||
|
private String resolveProvinceNameForMatch(String province) {
|
||||||
|
if (province == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = province.trim();
|
||||||
|
|
||||||
|
// 1. 先按原样查找
|
||||||
|
java.util.Optional<com.example.cattletends.entity.CattleProvince> exact =
|
||||||
|
cattleProvinceRepository.findFirstByProvince(trimmed);
|
||||||
|
if (exact.isPresent()) {
|
||||||
|
return exact.get().getProvince();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 尝试去掉常见后缀(省、市、自治区、回族、壮族、维吾尔)
|
||||||
|
String shortName = trimmed
|
||||||
|
.replace("维吾尔", "")
|
||||||
|
.replace("回族", "")
|
||||||
|
.replace("壮族", "")
|
||||||
|
.replace("自治区", "")
|
||||||
|
.replace("省", "")
|
||||||
|
.replace("市", "")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
if (!shortName.equals(trimmed)) {
|
||||||
|
java.util.Optional<com.example.cattletends.entity.CattleProvince> shortMatch =
|
||||||
|
cattleProvinceRepository.findFirstByProvince(shortName);
|
||||||
|
if (shortMatch.isPresent()) {
|
||||||
|
return shortMatch.get().getProvince();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 找不到匹配时,退回原始名称(保证不会中断流程)
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,16 @@ public class CattleNationalServiceImpl implements CattleNationalService {
|
|||||||
int nationalSlaughter25th = 0;
|
int nationalSlaughter25th = 0;
|
||||||
|
|
||||||
// 累加所有省份的数据
|
// 累加所有省份的数据
|
||||||
|
System.out.println("========== 开始计算全国总量 ==========");
|
||||||
|
System.out.println("省份总数: " + allProvinces.size());
|
||||||
for (CattleProvince province : allProvinces) {
|
for (CattleProvince province : allProvinces) {
|
||||||
|
System.out.println("省份: " + province.getProvince() +
|
||||||
|
", 23年存栏: " + province.getInventory23th() +
|
||||||
|
", 23年出栏: " + province.getSlaughter23th() +
|
||||||
|
", 24年存栏: " + province.getInventory24th() +
|
||||||
|
", 24年出栏: " + province.getSlaughter24th() +
|
||||||
|
", 25年存栏: " + province.getInventory25th() +
|
||||||
|
", 25年出栏: " + province.getSlaughter25th());
|
||||||
if (province.getInventory23th() != null) {
|
if (province.getInventory23th() != null) {
|
||||||
nationalInventory23th += province.getInventory23th();
|
nationalInventory23th += province.getInventory23th();
|
||||||
}
|
}
|
||||||
@@ -78,6 +87,14 @@ public class CattleNationalServiceImpl implements CattleNationalService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.out.println("计算结果: 23年存栏=" + nationalInventory23th +
|
||||||
|
", 23年出栏=" + nationalSlaughter23th +
|
||||||
|
", 24年存栏=" + nationalInventory24th +
|
||||||
|
", 24年出栏=" + nationalSlaughter24th +
|
||||||
|
", 25年存栏=" + nationalInventory25th +
|
||||||
|
", 25年出栏=" + nationalSlaughter25th);
|
||||||
|
System.out.println("========== 计算完成 ==========");
|
||||||
|
|
||||||
// 查找是否已存在全国总量记录
|
// 查找是否已存在全国总量记录
|
||||||
CattleNational nationalData = cattleNationalRepository.findFirstByOrderByIdAsc()
|
CattleNational nationalData = cattleNationalRepository.findFirstByOrderByIdAsc()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
|||||||
@@ -47,12 +47,13 @@ public class CattleProvinceServiceImpl implements CattleProvinceService {
|
|||||||
continue; // 跳过无效数据
|
continue; // 跳过无效数据
|
||||||
}
|
}
|
||||||
// 查找该省份是否已存在
|
// 查找该省份是否已存在
|
||||||
CattleProvince provinceData = cattleProvinceRepository.findByProvince(dto.getProvince())
|
CattleProvince provinceData = cattleProvinceRepository.findFirstByProvince(dto.getProvince())
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
|
||||||
if (provinceData != null) {
|
if (provinceData != null) {
|
||||||
// 更新现有数据
|
// 更新现有数据
|
||||||
provinceData.setProvincePrice(dto.getProvincePrice());
|
// 注意:不更新 provincePrice 字段,该字段只通过牛只数据导入时的计算得出
|
||||||
|
provinceData.setProvincePriceRange(dto.getProvincePriceRange());
|
||||||
provinceData.setInventory23th(dto.getInventory23th());
|
provinceData.setInventory23th(dto.getInventory23th());
|
||||||
provinceData.setSlaughter23th(dto.getSlaughter23th());
|
provinceData.setSlaughter23th(dto.getSlaughter23th());
|
||||||
provinceData.setInventory24th(dto.getInventory24th());
|
provinceData.setInventory24th(dto.getInventory24th());
|
||||||
@@ -65,7 +66,8 @@ public class CattleProvinceServiceImpl implements CattleProvinceService {
|
|||||||
// 创建新数据
|
// 创建新数据
|
||||||
provinceData = new CattleProvince();
|
provinceData = new CattleProvince();
|
||||||
provinceData.setProvince(dto.getProvince());
|
provinceData.setProvince(dto.getProvince());
|
||||||
provinceData.setProvincePrice(dto.getProvincePrice());
|
// 注意:不设置 provincePrice 字段,该字段只通过牛只数据导入时的计算得出
|
||||||
|
provinceData.setProvincePriceRange(dto.getProvincePriceRange());
|
||||||
provinceData.setInventory23th(dto.getInventory23th());
|
provinceData.setInventory23th(dto.getInventory23th());
|
||||||
provinceData.setSlaughter23th(dto.getSlaughter23th());
|
provinceData.setSlaughter23th(dto.getSlaughter23th());
|
||||||
provinceData.setInventory24th(dto.getInventory24th());
|
provinceData.setInventory24th(dto.getInventory24th());
|
||||||
@@ -109,24 +111,189 @@ public class CattleProvinceServiceImpl implements CattleProvinceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional
|
||||||
public List<CattleProvince> getAllProvinceData(String province) {
|
public List<CattleProvince> getAllProvinceData(String province) {
|
||||||
// 按省份均价降序排序
|
// 按省份均价降序排序
|
||||||
Sort sort = Sort.by(Sort.Direction.DESC, "provincePrice");
|
Sort sort = Sort.by(Sort.Direction.DESC, "provincePrice");
|
||||||
|
|
||||||
|
List<CattleProvince> dataList;
|
||||||
|
|
||||||
// 如果提供了省份参数,则只查询该省份数据
|
// 如果提供了省份参数,则只查询该省份数据
|
||||||
if (province != null && !province.trim().isEmpty()) {
|
if (province != null && !province.trim().isEmpty()) {
|
||||||
return cattleProvinceRepository.findByProvince(province.trim(), sort);
|
dataList = cattleProvinceRepository.findByProvince(province.trim(), sort);
|
||||||
|
} else {
|
||||||
|
// 否则返回所有省份数据,按省份均价降序排序
|
||||||
|
dataList = cattleProvinceRepository.findAll(sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 否则返回所有省份数据,按省份均价降序排序
|
// 去重处理:如果同一个省份有多条记录,保留最新的一条(up_time最大),更新其他记录
|
||||||
return cattleProvinceRepository.findAll(sort);
|
if (dataList != null && !dataList.isEmpty()) {
|
||||||
|
System.out.println("========== 开始去重处理 ==========");
|
||||||
|
System.out.println("去重前记录数: " + dataList.size());
|
||||||
|
|
||||||
|
// 按省份分组,找出每个省份的最新记录
|
||||||
|
java.util.Map<String, CattleProvince> provinceMap = new java.util.HashMap<>();
|
||||||
|
java.util.List<CattleProvince> duplicatesToUpdate = new java.util.ArrayList<>();
|
||||||
|
|
||||||
|
for (CattleProvince item : dataList) {
|
||||||
|
String provinceName = item.getProvince();
|
||||||
|
if (provinceName == null || provinceName.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!provinceMap.containsKey(provinceName)) {
|
||||||
|
// 第一次遇到该省份,直接添加
|
||||||
|
provinceMap.put(provinceName, item);
|
||||||
|
} else {
|
||||||
|
// 已存在该省份,比较更新时间,保留最新的
|
||||||
|
CattleProvince existing = provinceMap.get(provinceName);
|
||||||
|
if (item.getUpTime() != null && existing.getUpTime() != null) {
|
||||||
|
if (item.getUpTime().isAfter(existing.getUpTime())) {
|
||||||
|
// 当前记录更新,标记旧记录需要更新
|
||||||
|
duplicatesToUpdate.add(existing);
|
||||||
|
provinceMap.put(provinceName, item);
|
||||||
|
} else {
|
||||||
|
// 现有记录更新,标记当前记录需要更新
|
||||||
|
duplicatesToUpdate.add(item);
|
||||||
|
}
|
||||||
|
} else if (item.getUpTime() != null) {
|
||||||
|
// 当前记录有更新时间,保留它
|
||||||
|
duplicatesToUpdate.add(existing);
|
||||||
|
provinceMap.put(provinceName, item);
|
||||||
|
} else if (existing.getUpTime() != null) {
|
||||||
|
// 现有记录有更新时间,保留现有记录
|
||||||
|
duplicatesToUpdate.add(item);
|
||||||
|
} else {
|
||||||
|
// 都没有更新时间,保留ID较小的(通常是最早创建的)
|
||||||
|
if (item.getId() < existing.getId()) {
|
||||||
|
duplicatesToUpdate.add(existing);
|
||||||
|
provinceMap.put(provinceName, item);
|
||||||
|
} else {
|
||||||
|
duplicatesToUpdate.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新重复记录:将旧记录的数据更新为最新记录的数据
|
||||||
|
int updatedCount = 0;
|
||||||
|
for (CattleProvince duplicate : duplicatesToUpdate) {
|
||||||
|
CattleProvince latest = provinceMap.get(duplicate.getProvince());
|
||||||
|
if (latest != null && !latest.getId().equals(duplicate.getId())) {
|
||||||
|
// 更新旧记录的数据为最新记录的数据
|
||||||
|
duplicate.setProvincePrice(latest.getProvincePrice());
|
||||||
|
duplicate.setProvincePriceRange(latest.getProvincePriceRange());
|
||||||
|
duplicate.setInventory23th(latest.getInventory23th());
|
||||||
|
duplicate.setSlaughter23th(latest.getSlaughter23th());
|
||||||
|
duplicate.setInventory24th(latest.getInventory24th());
|
||||||
|
duplicate.setSlaughter24th(latest.getSlaughter24th());
|
||||||
|
duplicate.setInventory25th(latest.getInventory25th());
|
||||||
|
duplicate.setSlaughter25th(latest.getSlaughter25th());
|
||||||
|
cattleProvinceRepository.save(duplicate);
|
||||||
|
updatedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果更新了重复记录,重新查询
|
||||||
|
if (updatedCount > 0) {
|
||||||
|
System.out.println("更新了 " + updatedCount + " 条重复记录");
|
||||||
|
if (province != null && !province.trim().isEmpty()) {
|
||||||
|
dataList = cattleProvinceRepository.findByProvince(province.trim(), sort);
|
||||||
|
} else {
|
||||||
|
dataList = cattleProvinceRepository.findAll(sort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终去重:按省份去重,每个省份只保留一条(保留最新的)
|
||||||
|
java.util.Map<String, CattleProvince> finalMap = new java.util.LinkedHashMap<>();
|
||||||
|
for (CattleProvince item : dataList) {
|
||||||
|
String provinceName = item.getProvince();
|
||||||
|
if (provinceName == null || provinceName.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!finalMap.containsKey(provinceName)) {
|
||||||
|
finalMap.put(provinceName, item);
|
||||||
|
} else {
|
||||||
|
CattleProvince existing = finalMap.get(provinceName);
|
||||||
|
if (item.getUpTime() != null && existing.getUpTime() != null) {
|
||||||
|
if (item.getUpTime().isAfter(existing.getUpTime())) {
|
||||||
|
finalMap.put(provinceName, item);
|
||||||
|
}
|
||||||
|
} else if (item.getUpTime() != null) {
|
||||||
|
finalMap.put(provinceName, item);
|
||||||
|
} else if (existing.getUpTime() != null) {
|
||||||
|
// 保持现有记录
|
||||||
|
} else {
|
||||||
|
// 都没有更新时间,保留ID较小的
|
||||||
|
if (item.getId() < existing.getId()) {
|
||||||
|
finalMap.put(provinceName, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataList = new java.util.ArrayList<>(finalMap.values());
|
||||||
|
System.out.println("去重后记录数: " + dataList.size());
|
||||||
|
System.out.println("========== 去重处理完成 ==========");
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional
|
||||||
public CattleProvince getProvinceDataByName(String province) {
|
public CattleProvince getProvinceDataByName(String province) {
|
||||||
return cattleProvinceRepository.findByProvince(province)
|
// 查询该省份的所有记录
|
||||||
.orElseThrow(() -> new RuntimeException("省份数据不存在:" + province));
|
List<CattleProvince> provinceList = cattleProvinceRepository.findAllByProvince(province);
|
||||||
|
|
||||||
|
if (provinceList == null || provinceList.isEmpty()) {
|
||||||
|
throw new RuntimeException("省份数据不存在:" + province);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有多条记录,进行去重处理
|
||||||
|
if (provinceList.size() > 1) {
|
||||||
|
System.out.println("========== 发现重复省份记录: " + province + " ==========");
|
||||||
|
System.out.println("重复记录数: " + provinceList.size());
|
||||||
|
|
||||||
|
// 找出最新的记录(up_time最大)
|
||||||
|
CattleProvince latest = provinceList.get(0);
|
||||||
|
for (CattleProvince item : provinceList) {
|
||||||
|
if (item.getUpTime() != null && latest.getUpTime() != null) {
|
||||||
|
if (item.getUpTime().isAfter(latest.getUpTime())) {
|
||||||
|
latest = item;
|
||||||
|
}
|
||||||
|
} else if (item.getUpTime() != null) {
|
||||||
|
latest = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新其他重复记录
|
||||||
|
int updatedCount = 0;
|
||||||
|
for (CattleProvince item : provinceList) {
|
||||||
|
if (!item.getId().equals(latest.getId())) {
|
||||||
|
// 更新旧记录的数据为最新记录的数据
|
||||||
|
item.setProvincePrice(latest.getProvincePrice());
|
||||||
|
item.setProvincePriceRange(latest.getProvincePriceRange());
|
||||||
|
item.setInventory23th(latest.getInventory23th());
|
||||||
|
item.setSlaughter23th(latest.getSlaughter23th());
|
||||||
|
item.setInventory24th(latest.getInventory24th());
|
||||||
|
item.setSlaughter24th(latest.getSlaughter24th());
|
||||||
|
item.setInventory25th(latest.getInventory25th());
|
||||||
|
item.setSlaughter25th(latest.getSlaughter25th());
|
||||||
|
cattleProvinceRepository.save(item);
|
||||||
|
updatedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedCount > 0) {
|
||||||
|
System.out.println("更新了 " + updatedCount + " 条重复记录");
|
||||||
|
}
|
||||||
|
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有一条记录,直接返回
|
||||||
|
return provinceList.get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.example.cattletends.service.impl;
|
||||||
|
|
||||||
|
import com.example.cattletends.entity.ProvinceDailyPrice;
|
||||||
|
import com.example.cattletends.repository.ProvinceDailyPriceRepository;
|
||||||
|
import com.example.cattletends.service.ProvinceDailyPriceService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 省份每日均价服务实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ProvinceDailyPriceServiceImpl implements ProvinceDailyPriceService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProvinceDailyPriceRepository provinceDailyPriceRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public ProvinceDailyPrice saveOrUpdate(String province, java.math.BigDecimal price, LocalDate priceDate) {
|
||||||
|
if (province == null || province.trim().isEmpty() || price == null) {
|
||||||
|
System.out.println("保存省份每日均价失败: 参数不完整 - 省份: " + province + ", 价格: " + price);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimmedProvince = province.trim();
|
||||||
|
LocalDate date = priceDate != null ? priceDate : LocalDate.now();
|
||||||
|
|
||||||
|
// 检查是否已存在(同一天同一省份)
|
||||||
|
java.util.Optional<ProvinceDailyPrice> existing = provinceDailyPriceRepository.findByProvinceAndPriceDate(trimmedProvince, date);
|
||||||
|
|
||||||
|
if (existing.isPresent()) {
|
||||||
|
// 已存在,更新价格
|
||||||
|
ProvinceDailyPrice dailyPrice = existing.get();
|
||||||
|
java.math.BigDecimal oldPrice = dailyPrice.getPrice();
|
||||||
|
dailyPrice.setPrice(price);
|
||||||
|
ProvinceDailyPrice updated = provinceDailyPriceRepository.save(dailyPrice);
|
||||||
|
System.out.println("✓ 更新省份每日均价: " + trimmedProvince + ", 日期: " + date + ", 旧价格: " + oldPrice + " -> 新价格: " + price + " (ID: " + updated.getId() + ")");
|
||||||
|
return updated;
|
||||||
|
} else {
|
||||||
|
// 不存在则创建新记录
|
||||||
|
ProvinceDailyPrice dailyPrice = new ProvinceDailyPrice();
|
||||||
|
dailyPrice.setProvince(trimmedProvince);
|
||||||
|
dailyPrice.setPrice(price);
|
||||||
|
dailyPrice.setPriceDate(date);
|
||||||
|
|
||||||
|
ProvinceDailyPrice saved = provinceDailyPriceRepository.save(dailyPrice);
|
||||||
|
System.out.println("✓ 保存省份每日均价: " + trimmedProvince + ", 日期: " + date + ", 价格: " + price + " (ID: " + saved.getId() + ")");
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProvinceDailyPrice> getByProvince(String province) {
|
||||||
|
if (province == null || province.trim().isEmpty()) {
|
||||||
|
return provinceDailyPriceRepository.findAllByOrderByPriceDateDesc();
|
||||||
|
}
|
||||||
|
return provinceDailyPriceRepository.findByProvinceOrderByPriceDateDesc(province.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProvinceDailyPrice> getAll() {
|
||||||
|
return provinceDailyPriceRepository.findAllByOrderByPriceDateDesc();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProvinceDailyPrice> getByProvinceAndDateRange(String province, LocalDate startDate, LocalDate endDate) {
|
||||||
|
if (province == null || province.trim().isEmpty()) {
|
||||||
|
return getAll();
|
||||||
|
}
|
||||||
|
return provinceDailyPriceRepository.findByProvinceAndDateRange(province.trim(), startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProvinceDailyPrice> getByPriceDate(LocalDate priceDate) {
|
||||||
|
if (priceDate == null) {
|
||||||
|
return getAll();
|
||||||
|
}
|
||||||
|
return provinceDailyPriceRepository.findByPriceDateOrderByProvinceAsc(priceDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.util.Optional<ProvinceDailyPrice> getByProvinceAndPriceDate(String province, LocalDate priceDate) {
|
||||||
|
if (province == null || province.trim().isEmpty() || priceDate == null) {
|
||||||
|
return java.util.Optional.empty();
|
||||||
|
}
|
||||||
|
return provinceDailyPriceRepository.findByProvinceAndPriceDate(province.trim(), priceDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
-- 为 province_daily_price 表添加索引以优化 SQL 查询速度
|
||||||
|
-- 注意:如果索引已存在,执行会报错(Duplicate key name),可以忽略或先检查再创建
|
||||||
|
|
||||||
|
-- 检查索引是否存在的存储过程(可选,用于安全创建)
|
||||||
|
-- 如果不想使用存储过程,可以直接执行下面的 CREATE INDEX 语句
|
||||||
|
-- 如果索引已存在,会报错但不会影响其他索引的创建
|
||||||
|
|
||||||
|
-- 1. 复合索引:省份 + 日期(用于按省份和日期范围查询和排序)
|
||||||
|
-- 注意:虽然已有 UNIQUE KEY `uk_province_date`,但添加单独的索引可以优化某些查询场景
|
||||||
|
CREATE INDEX `idx_province_date_composite` ON `province_daily_price` (`province`, `price_date` DESC);
|
||||||
|
|
||||||
|
-- 2. 价格索引(用于按价格范围查询和排序)
|
||||||
|
CREATE INDEX `idx_price` ON `province_daily_price` (`price`);
|
||||||
|
|
||||||
|
-- 3. 复合索引:日期 + 价格(用于按日期和价格范围查询)
|
||||||
|
CREATE INDEX `idx_date_price` ON `province_daily_price` (`price_date`, `price`);
|
||||||
|
|
||||||
|
-- 4. 复合索引:省份 + 价格(用于按省份和价格范围查询)
|
||||||
|
CREATE INDEX `idx_province_price` ON `province_daily_price` (`province`, `price`);
|
||||||
|
|
||||||
|
-- 5. 更新时间索引(用于按更新时间排序和查询)
|
||||||
|
CREATE INDEX `idx_up_time` ON `province_daily_price` (`up_time`);
|
||||||
|
|
||||||
|
-- 6. 创建时间索引(用于按创建时间排序和查询)
|
||||||
|
CREATE INDEX `idx_create_time` ON `province_daily_price` (`create_time`);
|
||||||
|
|
||||||
|
-- 验证索引
|
||||||
|
-- SHOW INDEX FROM province_daily_price;
|
||||||
|
|
||||||
|
-- 查看表结构
|
||||||
|
-- SHOW CREATE TABLE province_daily_price;
|
||||||
|
|
||||||
|
-- 查看索引使用情况(需要先执行一些查询)
|
||||||
|
-- EXPLAIN SELECT * FROM province_daily_price WHERE province = '内蒙古' ORDER BY price_date DESC;
|
||||||
|
-- EXPLAIN SELECT * FROM province_daily_price WHERE price_date BETWEEN '2025-12-01' AND '2025-12-07';
|
||||||
|
-- EXPLAIN SELECT * FROM province_daily_price WHERE province = '内蒙古' AND price BETWEEN 13.0 AND 15.0;
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
-- 为 cattlebreed 表添加 is_delet 字段(逻辑删除标志)
|
||||||
|
-- 0 = 未删除,1 = 已删除
|
||||||
|
|
||||||
|
-- 检查字段是否存在,如果不存在则添加
|
||||||
|
-- 注意:MySQL 不支持 IF NOT EXISTS,需要先检查
|
||||||
|
-- 如果字段已存在,执行此脚本会报错,可以忽略
|
||||||
|
|
||||||
|
ALTER TABLE cattlebreed
|
||||||
|
ADD COLUMN is_delet INT NOT NULL DEFAULT 0 COMMENT '逻辑删除标志:0=未删除,1=已删除';
|
||||||
|
|
||||||
|
-- 为 is_delet 字段添加索引,优化查询性能
|
||||||
|
CREATE INDEX idx_is_delet ON cattlebreed(is_delet);
|
||||||
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
-- 创建品种表
|
-- 创建品种表
|
||||||
CREATE TABLE IF NOT EXISTS `cattlebreed` (
|
CREATE TABLE IF NOT EXISTS `cattlebreed` (
|
||||||
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
|
||||||
`breed_name` VARCHAR(100) NOT NULL UNIQUE COMMENT '品种名称',
|
|
||||||
`create_time` DATETIME COMMENT '创建时间',
|
|
||||||
`up_time` DATETIME NOT NULL COMMENT '更新时间',
|
|
||||||
-- 索引定义
|
-- 索引定义
|
||||||
INDEX `idx_breed_name` (`breed_name`),
|
INDEX `idx_breed_name` (`breed_name`),
|
||||||
INDEX `idx_create_time` (`create_time`),
|
INDEX `idx_create_time` (`create_time`),
|
||||||
INDEX `idx_up_time` (`up_time`)
|
INDEX `idx_up_time` (`up_time`),
|
||||||
|
INDEX `idx_is_delet` (`is_delet`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='品种表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='品种表';
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
-- 创建省份每日均价表
|
||||||
|
-- 存储15个省份每天的省份均价,只累加不覆盖
|
||||||
|
CREATE TABLE IF NOT EXISTS `province_daily_price` (
|
||||||
|
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||||
|
`province` VARCHAR(100) NOT NULL COMMENT '省份名称',
|
||||||
|
`price` DECIMAL(10, 2) NOT NULL COMMENT '省份均价(元/斤)',
|
||||||
|
`price_date` DATE NOT NULL COMMENT '价格日期',
|
||||||
|
`create_time` DATETIME COMMENT '创建时间',
|
||||||
|
`up_time` DATETIME NOT NULL COMMENT '更新时间',
|
||||||
|
-- 索引定义
|
||||||
|
UNIQUE KEY `uk_province_date` (`province`, `price_date`),
|
||||||
|
INDEX `idx_province` (`province`),
|
||||||
|
INDEX `idx_price_date` (`price_date`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='省份每日均价表(只累加不覆盖)';
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
-- 为 province_daily_price 表生成前7天的15个省份数据
|
||||||
|
-- 价格在基准价格(从 cattleprovince 表获取)上下浮动1元之内
|
||||||
|
-- 使用 INSERT IGNORE 避免重复插入(如果某天某省份已有数据则跳过)
|
||||||
|
|
||||||
|
-- 生成前7天的数据(包括今天,共7天:今天、昨天、前天...)
|
||||||
|
INSERT IGNORE INTO `province_daily_price` (`province`, `price`, `price_date`, `create_time`, `up_time`)
|
||||||
|
SELECT
|
||||||
|
cp.province,
|
||||||
|
-- 价格浮动:基准价格 + (-1.00 到 +1.00 之间的随机值)
|
||||||
|
-- 使用 ROUND() 保留2位小数
|
||||||
|
-- 使用 GREATEST() 确保价格不小于 0.01
|
||||||
|
ROUND(
|
||||||
|
GREATEST(0.01,
|
||||||
|
cp.province_price + (RAND() * 2 - 1) -- RAND() * 2 - 1 生成 -1 到 +1 之间的值
|
||||||
|
),
|
||||||
|
2
|
||||||
|
) as price,
|
||||||
|
DATE_SUB(CURDATE(), INTERVAL day_offset DAY) as price_date,
|
||||||
|
NOW() as create_time,
|
||||||
|
NOW() as up_time
|
||||||
|
FROM
|
||||||
|
(SELECT DISTINCT province, province_price
|
||||||
|
FROM cattleprovince
|
||||||
|
WHERE province_price IS NOT NULL
|
||||||
|
ORDER BY province) cp
|
||||||
|
CROSS JOIN
|
||||||
|
(SELECT 0 as day_offset UNION ALL
|
||||||
|
SELECT 1 UNION ALL
|
||||||
|
SELECT 2 UNION ALL
|
||||||
|
SELECT 3 UNION ALL
|
||||||
|
SELECT 4 UNION ALL
|
||||||
|
SELECT 5 UNION ALL
|
||||||
|
SELECT 6) days
|
||||||
|
ORDER BY cp.province, day_offset;
|
||||||
|
|
||||||
|
-- 验证生成的数据
|
||||||
|
-- 查看前7天每个省份的价格和与基准价格的差值
|
||||||
|
SELECT
|
||||||
|
pdp.province,
|
||||||
|
pdp.price_date,
|
||||||
|
pdp.price,
|
||||||
|
cp.province_price as base_price,
|
||||||
|
ROUND(pdp.price - cp.province_price, 2) as price_diff,
|
||||||
|
CASE
|
||||||
|
WHEN ABS(pdp.price - cp.province_price) <= 1.0 THEN '✓ 在范围内'
|
||||||
|
ELSE '✗ 超出范围'
|
||||||
|
END as status
|
||||||
|
FROM province_daily_price pdp
|
||||||
|
INNER JOIN cattleprovince cp ON pdp.province = cp.province
|
||||||
|
WHERE pdp.price_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
|
||||||
|
AND pdp.price_date <= CURDATE()
|
||||||
|
ORDER BY pdp.province, pdp.price_date DESC;
|
||||||
|
|
||||||
|
-- 统计信息
|
||||||
|
-- SELECT
|
||||||
|
-- COUNT(*) as total_records,
|
||||||
|
-- COUNT(DISTINCT province) as province_count,
|
||||||
|
-- MIN(price_date) as earliest_date,
|
||||||
|
-- MAX(price_date) as latest_date
|
||||||
|
-- FROM province_daily_price
|
||||||
|
-- WHERE price_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY);
|
||||||
|
|
||||||
93
province_daily_price_insert.sql
Normal file
93
province_daily_price_insert.sql
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
-- 生成12月3日之前5天的省份每日均价数据
|
||||||
|
-- 价格在12月3日价格基础上上下浮动不超过1元
|
||||||
|
|
||||||
|
-- 11月28日数据
|
||||||
|
INSERT INTO province_daily_price (province, price, price_date, create_time, up_time) VALUES
|
||||||
|
('新疆', 12.85, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('河北', 8.15, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('山东', 9.68, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('甘肃', 10.35, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('吉林', 10.82, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('青海', 11.25, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('黑龙江', 11.45, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('安徽', 11.58, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('贵州', 11.92, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('内蒙古', 12.28, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('西藏', 12.45, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('四川', 12.48, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('宁夏', 12.55, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('云南', 12.85, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00'),
|
||||||
|
('广西', 13.35, '2025-11-28', '2025-11-28 09:00:00', '2025-11-28 09:00:00');
|
||||||
|
|
||||||
|
-- 11月29日数据
|
||||||
|
INSERT INTO province_daily_price (province, price, price_date, create_time, up_time) VALUES
|
||||||
|
('新疆', 13.05, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('河北', 7.65, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('山东', 9.15, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('甘肃', 9.85, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('吉林', 10.25, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('青海', 10.65, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('黑龙江', 10.95, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('安徽', 11.05, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('贵州', 11.35, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('内蒙古', 11.65, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('西藏', 11.85, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('四川', 11.88, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('宁夏', 11.95, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('云南', 12.25, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00'),
|
||||||
|
('广西', 12.75, '2025-11-29', '2025-11-29 09:00:00', '2025-11-29 09:00:00');
|
||||||
|
|
||||||
|
-- 11月30日数据
|
||||||
|
INSERT INTO province_daily_price (province, price, price_date, create_time, up_time) VALUES
|
||||||
|
('新疆', 13.28, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('河北', 8.25, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('山东', 9.45, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('甘肃', 9.95, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('吉林', 10.35, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('青海', 10.75, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('黑龙江', 11.05, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('安徽', 11.15, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('贵州', 11.45, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('内蒙古', 11.75, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('西藏', 11.95, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('四川', 11.98, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('宁夏', 12.05, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('云南', 12.35, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00'),
|
||||||
|
('广西', 12.85, '2025-11-30', '2025-11-30 09:00:00', '2025-11-30 09:00:00');
|
||||||
|
|
||||||
|
-- 12月1日数据
|
||||||
|
INSERT INTO province_daily_price (province, price, price_date, create_time, up_time) VALUES
|
||||||
|
('新疆', 13.05, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('河北', 7.75, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('山东', 9.25, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('甘肃', 9.85, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('吉林', 10.25, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('青海', 10.65, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('黑龙江', 10.95, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('安徽', 11.05, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('贵州', 11.35, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('内蒙古', 11.65, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('西藏', 11.85, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('四川', 11.88, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('宁夏', 11.95, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('云南', 12.25, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00'),
|
||||||
|
('广西', 12.75, '2025-12-01', '2025-12-01 09:00:00', '2025-12-01 09:00:00');
|
||||||
|
|
||||||
|
-- 12月2日数据
|
||||||
|
INSERT INTO province_daily_price (province, price, price_date, create_time, up_time) VALUES
|
||||||
|
('新疆', 13.18, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('河北', 8.05, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('山东', 9.38, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('甘肃', 9.88, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('吉林', 10.38, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('青海', 10.78, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('黑龙江', 11.08, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('安徽', 11.18, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('贵州', 11.48, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('内蒙古', 11.78, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('西藏', 11.98, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('四川', 12.03, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('宁夏', 12.10, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('云南', 12.40, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00'),
|
||||||
|
('广西', 12.90, '2025-12-02', '2025-12-02 09:00:00', '2025-12-02 09:00:00');
|
||||||
|
|
||||||
Reference in New Issue
Block a user