基本完成牛只数据的导入接口

This commit is contained in:
shenquanyi
2025-12-04 14:11:49 +08:00
parent 96b97399ee
commit 1ba3fb0c85
23 changed files with 1651 additions and 146 deletions

View File

@@ -35,6 +35,7 @@ public class CattleDataController {
private final com.example.cattletends.service.CattleProvinceService cattleProvinceService;
private final com.example.cattletends.service.CattleNationalService cattleNationalService;
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,
com.example.cattletends.service.CattleProvinceService cattleProvinceService,
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.cattleProvinceService = cattleProvinceService;
this.cattleNationalService = cattleNationalService;
this.cattleBreedService = cattleBreedService;
this.provinceDailyPriceService = provinceDailyPriceService;
}
/**
* 获取所有牛只数据(支持按省份或品种筛选)
* 获取所有牛只数据(支持按省份或品种筛选,支持按日期查询
*/
@GetMapping
@ApiOperation(value = "获取所有牛只数据", notes = "返回所有牛只数据的列表,支持按省份或品种筛选,按价格升序排序")
@ApiOperation(value = "获取所有牛只数据", notes = "返回牛只数据的列表,支持按省份或品种筛选,支持按日期查询格式yyyy-MM-dd默认查询今天的数据按价格升序排序")
public Result<List<CattleData>> getAllCattleData(
@ApiParam(value = "省份(可选,如果提供则只返回该省份的数据)")
@RequestParam(required = false) String province,
@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;
// 解析日期参数
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()) {
data = cattleDataService.getCattleDataByType(type.trim());
data = cattleDataService.getCattleDataByType(type.trim(), parsedDate);
} else if (province != null && !province.trim().isEmpty()) {
// 其次按省份筛选
data = cattleDataService.getCattleDataByProvince(province.trim());
data = cattleDataService.getCattleDataByProvince(province.trim(), parsedDate);
} else {
// 都不提供则返回所有数据
data = cattleDataService.getAllCattleData();
data = cattleDataService.getAllCattleData(parsedDate);
}
return Result.success(data);
}
@@ -78,21 +94,34 @@ public class CattleDataController {
* 阿里云大屏接口 - 获取牛只数据直接返回数据数组不包含Result包装
*/
@GetMapping("/screen")
@ApiOperation(value = "阿里云大屏接口", notes = "返回牛只数据数组直接返回data数据不包含Result包装。支持按省份或品种筛选按价格升序排序")
@ApiOperation(value = "阿里云大屏接口", notes = "返回牛只数据数组直接返回data数据不包含Result包装。支持按省份或品种筛选支持按日期查询格式yyyy-MM-dd默认查询今天的数据按价格升序排序")
public List<CattleData> getCattleDataForScreen(
@ApiParam(value = "省份(可选,如果提供则只返回该省份的数据)")
@RequestParam(required = false) String province,
@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()) {
return cattleDataService.getCattleDataByType(type.trim());
return cattleDataService.getCattleDataByType(type.trim(), parsedDate);
} else if (province != null && !province.trim().isEmpty()) {
// 其次按省份筛选
return cattleDataService.getCattleDataByProvince(province.trim());
return cattleDataService.getCattleDataByProvince(province.trim(), parsedDate);
} else {
// 都不提供则返回所有数据
return cattleDataService.getAllCattleData();
return cattleDataService.getAllCattleData(parsedDate);
}
}
@@ -117,64 +146,49 @@ public class CattleDataController {
public Result<List<CattleData>> importCattleData(
@ApiParam(value = "Excel文件", required = true)
@RequestParam("file") MultipartFile file) {
System.out.println("========== 收到导入牛只数据请求 ==========");
System.out.println("文件名: " + file.getOriginalFilename());
System.out.println("文件大小: " + file.getSize());
if (file.isEmpty()) {
System.out.println("错误: 文件为空");
return Result.error(400, "文件不能为空");
}
try {
System.out.println("开始解析Excel文件...");
List<CattleDataDTO> dataList = parseExcelFile(file);
System.out.println("解析完成,共 " + (dataList != null ? dataList.size() : 0) + " 条数据");
if (dataList == null || dataList.isEmpty()) {
System.out.println("错误: Excel文件中没有有效数据");
return Result.error(400, "Excel文件中没有有效数据请检查文件格式");
}
// 自动创建品种
System.out.println("========== 开始自动创建品种 ==========");
int newBreedCount = 0;
int existingBreedCount = 0;
java.util.Set<String> processedBreeds = new java.util.HashSet<>();
for (CattleDataDTO dto : dataList) {
if (dto.getType() != null && !dto.getType().trim().isEmpty()) {
String breedName = dto.getType().trim();
// 避免重复处理相同品种
if (!processedBreeds.contains(breedName)) {
try {
com.example.cattletends.entity.CattleBreed existingBreed = cattleBreedService.getBreedByName(breedName);
if (existingBreed == null) {
// 新品种,创建
cattleBreedService.createOrGetBreed(breedName);
newBreedCount++;
System.out.println("✓ 创建新品种: " + breedName);
} else {
existingBreedCount++;
System.out.println("○ 品种已存在: " + breedName);
}
processedBreeds.add(breedName);
} 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);
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(),
newBreedCount, existingBreedCount);
// 更新品种的逻辑删除状态
try {
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());
} catch (Exception e) {
System.err.println("========== 导入失败 ==========");
@@ -235,6 +249,62 @@ public class CattleDataController {
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 表计算总和
@@ -292,6 +362,11 @@ public class CattleDataController {
Map<String, Object> result = cattleProvinceService.importProvinceData(provinceDataList);
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 条省份数据",
result.get("createCount"), result.get("updateCount"), result.get("totalCount"));
return Result.success(message, result);
@@ -476,7 +551,7 @@ public class CattleDataController {
}
/**
* 解析价格
* 解析价格(用于牛只数据导入,取平均值)
* 支持格式:
* - 数字13.5
* - 价格范围13.41-14.61(取平均值)
@@ -535,6 +610,48 @@ public class CattleDataController {
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模板列顺序根据实际模板
// A列索引0省份
// B列索引12023存栏(万头) -> inventory_23th
// C列索引22023栏(万头) -> slaughter_23th
// D列索引32024存栏(万头) -> inventory_24th
// E列索引42024栏(万头) -> slaughter_24th
// F列索引52025存栏(万头) -> inventory_25th
// G列索引62025栏(万头) -> slaughter_25th
// H列索引7省份均价(元/斤) -> province_price
// B列索引1省份均价(元/斤) -> province_price
// C列索引22023栏(万头) -> inventory_23th
// D列索引32023出栏(万头) -> slaughter_23th
// E列索引42024栏(万头) -> inventory_24th
// F列索引52024出栏(万头) -> slaughter_24th
// G列索引62025栏(万头) -> inventory_25th
// H列索引72025出栏(万头) -> slaughter_25th
// 读取省份A列索引0
Cell provinceCell = row.getCell(0);
if (provinceCell != null) {
String province = getCellValueAsString(provinceCell);
// 处理可能包含"省"、"自治区"等后缀的情况
province = province.replace("", "").replace("自治区", "").replace("", "").trim();
String province = getCellValueAsString(provinceCell).trim();
// 完全按照表格内容导入,不做任何截取
dto.setProvince(province);
}
// 读取2023存栏(万头)B列索引1-> inventory_23th
Cell inv23Cell = row.getCell(1);
if (inv23Cell != null) {
Integer value = parseInteger(inv23Cell);
dto.setInventory23th(value);
// 读取省份均价(元/斤)B列索引1-> province_price_range原始区间和 province_price最低价格
Cell priceCell = row.getCell(1);
if (priceCell != null) {
String priceStr = getCellValueAsString(priceCell);
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
Cell sla23Cell = row.getCell(2);
// 读取2023栏(万头)C列索引2-> inventory_23th
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) {
Integer value = parseInteger(sla23Cell);
dto.setSlaughter23th(value);
}
// 读取2024存栏(万头)D列,索引3-> inventory_24th
Cell inv24Cell = row.getCell(3);
// 读取2024存栏(万头)E列,索引4-> inventory_24th
Cell inv24Cell = row.getCell(4);
if (inv24Cell != null) {
Integer value = parseInteger(inv24Cell);
dto.setInventory24th(value);
}
// 读取2024出栏(万头)E列,索引4-> slaughter_24th
Cell sla24Cell = row.getCell(4);
// 读取2024出栏(万头)F列,索引5-> slaughter_24th
Cell sla24Cell = row.getCell(5);
if (sla24Cell != null) {
Integer value = parseInteger(sla24Cell);
dto.setSlaughter24th(value);
}
// 读取2025存栏(万头)F列,索引5-> inventory_25th
Cell inv25Cell = row.getCell(5);
// 读取2025存栏(万头)G列,索引6-> inventory_25th
Cell inv25Cell = row.getCell(6);
if (inv25Cell != null) {
Integer value = parseInteger(inv25Cell);
dto.setInventory25th(value);
}
// 读取2025出栏(万头)G列,索引6-> slaughter_25th
Cell sla25Cell = row.getCell(6);
// 读取2025出栏(万头)H列,索引7-> slaughter_25th
Cell sla25Cell = row.getCell(7);
if (sla25Cell != null) {
Integer value = parseInteger(sla25Cell);
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()) {
dataList.add(dto);

View File

@@ -20,9 +20,14 @@ public class ProvinceDataDTO {
private String province;
/**
* 省份均价
* 省份均价(最低价格)
*/
private BigDecimal provincePrice;
/**
* 省份均价区间(原始字符串,如 "13.4-14.9"
*/
private String provincePriceRange;
/**
* 23年存栏万头

View File

@@ -10,7 +10,8 @@ import java.time.LocalDateTime;
@Table(name = "cattlebreed", indexes = {
@Index(name = "idx_breed_name", columnList = "breed_name", unique = true),
@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 {
@@ -36,10 +37,19 @@ public class CattleBreed {
@Column(name = "up_time", nullable = false)
private LocalDateTime upTime;
/**
* 逻辑删除标志0=未删除1=已删除
*/
@Column(name = "is_delet", nullable = false, columnDefinition = "INT DEFAULT 0")
private Integer isDelet = 0;
@PrePersist
protected void onCreate() {
createTime = LocalDateTime.now();
upTime = LocalDateTime.now();
if (isDelet == null) {
isDelet = 0;
}
}
@PreUpdate
@@ -79,5 +89,13 @@ public class CattleBreed {
public void setUpTime(LocalDateTime upTime) {
this.upTime = upTime;
}
public Integer getIsDelet() {
return isDelet;
}
public void setIsDelet(Integer isDelet) {
this.isDelet = isDelet;
}
}

View File

@@ -42,10 +42,16 @@ public class CattleProvince {
private String province;
/**
* 省份均价
* 省份均价(存储最低价格)
*/
@Column(name = "province_price", precision = 10, scale = 2)
private BigDecimal provincePrice;
/**
* 省份均价区间(原始字符串,如 "13.4-14.9"
*/
@Column(name = "province_price_range", length = 50)
private String provincePriceRange;
/**
* 23年存栏万头

View File

@@ -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;
}
}

View File

@@ -2,8 +2,10 @@ package com.example.cattletends.repository;
import com.example.cattletends.entity.CattleBreed;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
@@ -21,5 +23,11 @@ public interface CattleBreedRepository extends JpaRepository<CattleBreed, Intege
* 检查品种名称是否存在
*/
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();
}

View File

@@ -6,6 +6,8 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@@ -35,14 +37,87 @@ public interface CattleDataRepository extends JpaRepository<CattleData, Integer>
/**
* 根据品种、省份、产地、价格查询牛只数据(用于判断重复)
* 注意:使用 findFirstBy避免因为存在多条相同记录导致 NonUniqueResultException
*
* @param type 品种
* @param province 省份
* @param location 产地
* @param price 价格
* @return 牛只数据(如果存在)
* @return 第一条匹配的牛只数据(如果存在)
*/
Optional<CattleData> findByTypeAndProvinceAndLocationAndPrice(
Optional<CattleData> findFirstByTypeAndProvinceAndLocationAndPrice(
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);
}

View File

@@ -15,12 +15,12 @@ import java.util.Optional;
public interface CattleProvinceRepository extends JpaRepository<CattleProvince, Integer> {
/**
* 根据省份名称查询
* 根据省份名称查询(返回第一条记录)
*
* @param province 省份名称
* @return 省份数据
*/
Optional<CattleProvince> findByProvince(String province);
Optional<CattleProvince> findFirstByProvince(String province);
/**
* 根据省份名称查询(支持排序)
@@ -30,5 +30,13 @@ public interface CattleProvinceRepository extends JpaRepository<CattleProvince,
* @return 省份数据列表
*/
List<CattleProvince> findByProvince(String province, Sort sort);
/**
* 根据省份名称查询所有记录(用于去重处理)
*
* @param province 省份名称
* @return 省份数据列表
*/
List<CattleProvince> findAllByProvince(String province);
}

View File

@@ -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);
}

View File

@@ -28,5 +28,11 @@ public interface CattleBreedService {
* @return 品种实体
*/
CattleBreed getBreedByName(String breedName);
/**
* 更新品种的逻辑删除状态
* @param breedNamesInExcel Excel中存在的品种名称集合
*/
void updateBreedDeleteStatus(java.util.Set<String> breedNamesInExcel);
}

View File

@@ -4,6 +4,7 @@ import com.example.cattletends.dto.CattleDataDTO;
import com.example.cattletends.dto.ImportResult;
import com.example.cattletends.entity.CattleData;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
@@ -15,25 +16,28 @@ public interface CattleDataService {
/**
* 获取所有牛只数据
*
* @param date 日期可选如果为null则查询今天的数据
* @return 牛只数据列表
*/
List<CattleData> getAllCattleData();
List<CattleData> getAllCattleData(LocalDate date);
/**
* 根据省份获取牛只数据
*
* @param province 省份
* @param date 日期可选如果为null则查询今天的数据
* @return 牛只数据列表
*/
List<CattleData> getCattleDataByProvince(String province);
List<CattleData> getCattleDataByProvince(String province, LocalDate date);
/**
* 根据品种获取牛只数据
*
* @param type 品种
* @param date 日期可选如果为null则查询今天的数据
* @return 牛只数据列表
*/
List<CattleData> getCattleDataByType(String type);
List<CattleData> getCattleDataByType(String type, LocalDate date);
/**
* 根据ID获取牛只数据

View File

@@ -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);
}

View File

@@ -31,21 +31,139 @@ public class CattleBreedServiceImpl implements CattleBreedService {
// 先查询是否已存在
Optional<CattleBreed> existing = cattleBreedRepository.findByBreedName(trimmedName);
if (existing.isPresent()) {
System.out.println("品种已存在: " + trimmedName);
return existing.get();
CattleBreed breed = 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();
breed.setBreedName(trimmedName);
breed.setIsDelet(0); // 新创建的品种默认为未删除
CattleBreed saved = cattleBreedRepository.save(breed);
System.out.println("创建新品种: " + trimmedName + " (ID: " + saved.getId() + ")");
return saved;
}
@Override
@Transactional
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
@@ -56,5 +174,64 @@ public class CattleBreedServiceImpl implements CattleBreedService {
return cattleBreedRepository.findByBreedName(breedName.trim())
.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("========== 品种逻辑删除状态更新完成 ==========");
}
}

View File

@@ -5,10 +5,12 @@ import com.example.cattletends.dto.ImportResult;
import com.example.cattletends.entity.CattleData;
import com.example.cattletends.repository.CattleDataRepository;
import com.example.cattletends.service.CattleDataService;
import com.example.cattletends.service.ProvinceDailyPriceService;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
@@ -22,33 +24,193 @@ import java.util.Map;
public class CattleDataServiceImpl implements CattleDataService {
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.provinceDailyPriceService = provinceDailyPriceService;
this.cattleProvinceService = cattleProvinceService;
this.cattleProvinceRepository = cattleProvinceRepository;
}
@Override
@Transactional(readOnly = true)
public List<CattleData> getAllCattleData() {
// 按价格升序排序
return cattleDataRepository.findAll(Sort.by(Sort.Direction.ASC, "price"));
@Transactional
public List<CattleData> getAllCattleData(LocalDate date) {
// 如果date为null使用今天
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
@Transactional(readOnly = true)
public List<CattleData> getCattleDataByProvince(String province) {
// 按价格升序排序
return cattleDataRepository.findByProvince(province, Sort.by(Sort.Direction.ASC, "price"));
@Transactional
public List<CattleData> getCattleDataByProvince(String province, LocalDate date) {
// 如果date为null使用今天
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
@Transactional(readOnly = true)
public List<CattleData> getCattleDataByType(String type) {
// 按价格升序排序
return cattleDataRepository.findByType(type, Sort.by(Sort.Direction.ASC, "price"));
@Transactional
public List<CattleData> getCattleDataByType(String type, LocalDate date) {
// 如果date为null使用今天
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
@@ -97,40 +259,215 @@ public class CattleDataServiceImpl implements CattleDataService {
public ImportResult batchImportCattleData(List<CattleDataDTO> dataList) {
List<CattleData> savedList = new ArrayList<>();
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) {
// 检查是否存在重复数据type + province + location + price
CattleData existingData = cattleDataRepository
.findByTypeAndProvinceAndLocationAndPrice(
dto.getType(),
dto.getProvince(),
dto.getLocation(),
dto.getPrice()
)
.orElse(null);
if (existingData != null) {
// 如果存在重复数据,只更新 up_time保留原有的 create_time
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++;
// 验证必填字段
String type = dto.getType() != null ? dto.getType().trim() : null;
String location = dto.getLocation() != null ? dto.getLocation().trim() : null;
if (type == null || type.isEmpty() || location == null || location.isEmpty()) {
continue; // 跳过无效数据
}
// 创建新记录
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);
}
/**
* 计算当天省份的平均价格并更新省份每日均价
* 只计算当天导入的数据
*
* @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;
}
}

View File

@@ -57,7 +57,16 @@ public class CattleNationalServiceImpl implements CattleNationalService {
int nationalSlaughter25th = 0;
// 累加所有省份的数据
System.out.println("========== 开始计算全国总量 ==========");
System.out.println("省份总数: " + allProvinces.size());
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) {
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()
.orElse(null);

View File

@@ -47,12 +47,13 @@ public class CattleProvinceServiceImpl implements CattleProvinceService {
continue; // 跳过无效数据
}
// 查找该省份是否已存在
CattleProvince provinceData = cattleProvinceRepository.findByProvince(dto.getProvince())
CattleProvince provinceData = cattleProvinceRepository.findFirstByProvince(dto.getProvince())
.orElse(null);
if (provinceData != null) {
// 更新现有数据
provinceData.setProvincePrice(dto.getProvincePrice());
// 注意:不更新 provincePrice 字段,该字段只通过牛只数据导入时的计算得出
provinceData.setProvincePriceRange(dto.getProvincePriceRange());
provinceData.setInventory23th(dto.getInventory23th());
provinceData.setSlaughter23th(dto.getSlaughter23th());
provinceData.setInventory24th(dto.getInventory24th());
@@ -65,7 +66,8 @@ public class CattleProvinceServiceImpl implements CattleProvinceService {
// 创建新数据
provinceData = new CattleProvince();
provinceData.setProvince(dto.getProvince());
provinceData.setProvincePrice(dto.getProvincePrice());
// 注意:不设置 provincePrice 字段,该字段只通过牛只数据导入时的计算得出
provinceData.setProvincePriceRange(dto.getProvincePriceRange());
provinceData.setInventory23th(dto.getInventory23th());
provinceData.setSlaughter23th(dto.getSlaughter23th());
provinceData.setInventory24th(dto.getInventory24th());
@@ -109,24 +111,189 @@ public class CattleProvinceServiceImpl implements CattleProvinceService {
}
@Override
@Transactional(readOnly = true)
@Transactional
public List<CattleProvince> getAllProvinceData(String province) {
// 按省份均价降序排序
Sort sort = Sort.by(Sort.Direction.DESC, "provincePrice");
List<CattleProvince> dataList;
// 如果提供了省份参数,则只查询该省份数据
if (province != null && !province.trim().isEmpty()) {
return cattleProvinceRepository.findByProvince(province.trim(), sort);
dataList = cattleProvinceRepository.findByProvince(province.trim(), sort);
} else {
// 否则返回所有省份数据,按省份均价降序排序
dataList = cattleProvinceRepository.findAll(sort);
}
// 否则返回所有省份数据,按省份均价降序排序
return cattleProvinceRepository.findAll(sort);
// 去重处理如果同一个省份有多条记录保留最新的一条up_time最大更新其他记录
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
@Transactional(readOnly = true)
@Transactional
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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -1,12 +1,10 @@
-- 创建品种表
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_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='品种表';

View File

@@ -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='省份每日均价表(只累加不覆盖)';

View File

@@ -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);

View 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');