配种计划重新设计

This commit is contained in:
zyk
2026-03-03 10:21:46 +08:00
parent 204bbc5ee2
commit 4818ed5fc5
5 changed files with 519 additions and 240 deletions

View File

@@ -10,6 +10,7 @@ import com.zhyc.module.produce.breed.service.IScBreedPlanGenerateService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.zhyc.common.annotation.Log;
import com.zhyc.common.core.controller.BaseController;
import com.zhyc.common.core.domain.AjaxResult;
@@ -65,7 +66,89 @@ public class ScBreedPlanGenerateController extends BaseController
}
/**
* 自动生成配种计划
* 导出已选母羊公羊配对表(供用户下载后手动填写/确认配对)
*/
@PreAuthorize("@ss.hasPermi('mating_plan:generate:export')")
@Log(title = "导出配对模板", businessType = BusinessType.EXPORT)
@PostMapping("/exportPairs")
public void exportSelectedPairs(HttpServletResponse response, @RequestBody Map<String, Object> params)
{
try {
List<?> eweIdsRaw = (List<?>) params.get("eweIds");
List<?> ramIdsRaw = (List<?>) params.get("ramIds");
if (eweIdsRaw == null || ramIdsRaw == null) {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":500,\"msg\":\"参数不能为空\"}");
return;
}
List<Long> eweIds = eweIdsRaw.stream()
.map(obj -> obj instanceof Integer ? ((Integer) obj).longValue()
: obj instanceof Long ? (Long) obj : Long.valueOf(obj.toString()))
.collect(Collectors.toList());
List<Long> ramIds = ramIdsRaw.stream()
.map(obj -> obj instanceof Integer ? ((Integer) obj).longValue()
: obj instanceof Long ? (Long) obj : Long.valueOf(obj.toString()))
.collect(Collectors.toList());
scBreedPlanGenerateService.exportSelectedPairs(response, eweIds, ramIds);
} catch (Exception e) {
logger.error("导出配对表失败", e);
try {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
} catch (Exception ex) {
logger.error("返回错误信息失败", ex);
}
}
}
/**
* 解析导入的配对Excel返回配对预览数据不生成计划
*/
@PreAuthorize("@ss.hasPermi('mating_plan:generate:add')")
@PostMapping("/parsePairs")
public AjaxResult parsePairsFromExcel(@RequestParam("file") MultipartFile file)
{
try {
List<Map<String, Object>> pairs = scBreedPlanGenerateService.parsePairsFromExcel(file);
return success(pairs);
} catch (Exception e) {
logger.error("解析配对Excel失败", e);
return error("解析失败:" + e.getMessage());
}
}
/**
* 根据导入的配对数据生成配种计划
*/
@PreAuthorize("@ss.hasPermi('mating_plan:generate:auto')")
@PostMapping("/generateFromPairs")
@Log(title = "导入生成配种计划", businessType = BusinessType.INSERT)
public AjaxResult generateBreedPlanFromPairs(@RequestBody Map<String, Object> params)
{
try {
Integer planType = params.get("planType") != null ? (Integer) params.get("planType") : 1;
@SuppressWarnings("unchecked")
List<Map<String, Object>> pairs = (List<Map<String, Object>>) params.get("pairs");
if (pairs == null || pairs.isEmpty()) {
return error("配对数据不能为空");
}
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateService.autoGenerateBreedPlanFromPairs(planType, pairs);
return success(planGenerate);
} catch (Exception e) {
logger.error("导入生成配种计划失败", e);
return error("生成失败:" + e.getMessage());
}
}
/**
* 自动生成配种计划(原有逻辑保留,按比例自动分配)
*/
@PreAuthorize("@ss.hasPermi('mating_plan:generate:auto')")
@PostMapping("/auto")
@@ -73,13 +156,9 @@ public class ScBreedPlanGenerateController extends BaseController
public AjaxResult autoGenerateBreedPlan(@RequestBody Map<String, Object> params)
{
try {
// 获取计划类型
Integer planType = params.get("planType") != null ? (Integer) params.get("planType") : 1;
// 计划名称由系统自动生成,不再从前端传入
String planName = null;
// 安全的类型转换
List<?> eweIdsRaw = (List<?>) params.get("eweIds");
List<?> ramIdsRaw = (List<?>) params.get("ramIds");
@@ -88,27 +167,13 @@ public class ScBreedPlanGenerateController extends BaseController
}
List<Long> eweIds = eweIdsRaw.stream()
.map(obj -> {
if (obj instanceof Integer) {
return ((Integer) obj).longValue();
} else if (obj instanceof Long) {
return (Long) obj;
} else {
return Long.valueOf(obj.toString());
}
})
.map(obj -> obj instanceof Integer ? ((Integer) obj).longValue()
: obj instanceof Long ? (Long) obj : Long.valueOf(obj.toString()))
.collect(Collectors.toList());
List<Long> ramIds = ramIdsRaw.stream()
.map(obj -> {
if (obj instanceof Integer) {
return ((Integer) obj).longValue();
} else if (obj instanceof Long) {
return (Long) obj;
} else {
return Long.valueOf(obj.toString());
}
})
.map(obj -> obj instanceof Integer ? ((Integer) obj).longValue()
: obj instanceof Long ? (Long) obj : Long.valueOf(obj.toString()))
.collect(Collectors.toList());
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateService.autoGenerateBreedPlan(planType, planName, eweIds, ramIds);
@@ -222,7 +287,6 @@ public class ScBreedPlanGenerateController extends BaseController
scBreedPlanGenerateService.exportBreedPlanDetails(response, id);
} catch (Exception e) {
logger.error("导出配种计划详情失败", e);
// 在出错时返回错误信息给前端
try {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
@@ -246,7 +310,7 @@ public class ScBreedPlanGenerateController extends BaseController
/**
* 模糊查询母羊耳号列表
*/
@PreAuthorize("@ss.hasPermi('mating_plan:generate:query')") // 根据实际权限修改
@PreAuthorize("@ss.hasPermi('mating_plan:generate:query')")
@GetMapping("/search_ear_numbers")
public AjaxResult searchEarNumbers(@RequestParam("query") String query) {
try {

View File

@@ -136,4 +136,26 @@ public interface ScBreedPlanGenerateMapper
* @return 耳号列表
*/
List<String> searchEarNumbers(@Param("query") String query);
/**
* 根据ID列表查询羊只耳号
*
* @param ids 羊只ID列表
* @param gender 性别1=母羊2=公羊
* @return 包含 id、manage_tags 的 Map 列表
*/
List<Map<String, Object>> selectEarNumbersByIds(
@Param("ids") List<Long> ids,
@Param("gender") int gender);
/**
* 根据管理耳号查询羊只ID
*
* @param manageTags 管理耳号
* @param gender 性别1=母羊2=公羊
* @return 羊只ID
*/
Long selectSheepIdByManageTags(
@Param("manageTags") String manageTags,
@Param("gender") int gender);
}

View File

@@ -4,7 +4,9 @@ import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import com.zhyc.module.produce.breed.domain.ScBreedPlanGenerate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
/**
* 配种计划生成Service接口
@@ -44,6 +46,13 @@ public interface IScBreedPlanGenerateService
*/
public List<Map<String, Object>> selectEligibleRam(@RequestParam(value = "manageTags", required = false) String manageTags);
void exportSelectedPairs(HttpServletResponse response, List<Long> eweIds, List<Long> ramIds);
List<Map<String, Object>> parsePairsFromExcel(MultipartFile file);
@Transactional
ScBreedPlanGenerate autoGenerateBreedPlanFromPairs(Integer planType, List<Map<String, Object>> pairs);
/**
* 自动生成配种计划
*

View File

@@ -3,9 +3,11 @@ package com.zhyc.module.produce.breed.service.impl;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletResponse;
import com.zhyc.module.produce.breed.domain.ScBreedPlan;
@@ -16,6 +18,7 @@ import com.zhyc.module.produce.breed.service.IScBreedPlanGenerateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import com.zhyc.common.utils.SecurityUtils;
import org.springframework.util.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
@@ -40,9 +43,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/**
* 查询配种计划生成
*
* @param id 配种计划生成主键
* @return 配种计划生成
*/
@Override
public ScBreedPlanGenerate selectScBreedPlanGenerateById(Long id)
@@ -52,9 +52,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/**
* 查询配种计划生成列表
*
* @param scBreedPlanGenerate 配种计划生成
* @return 配种计划生成
*/
@Override
public List<ScBreedPlanGenerate> selectScBreedPlanGenerateList(ScBreedPlanGenerate scBreedPlanGenerate)
@@ -64,8 +61,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/**
* 筛选符合条件的母羊
*
* @return 符合条件的母羊列表
*/
@Override
public List<Map<String, Object>> selectEligibleEwe(@RequestParam(value = "manageTags", required = false) String manageTags)
@@ -75,8 +70,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/**
* 筛选符合条件的公羊
*
* @return 符合条件的公羊列表
*/
@Override
public List<Map<String, Object>> selectEligibleRam(@RequestParam(value = "manageTags", required = false) String manageTags)
@@ -85,26 +78,302 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
}
/**
* 自动生成配种计划
* 导出已选母羊公羊配对表
* 按轮询方式将母羊分配给公羊导出Excel两列母羊耳号、公羊耳号
*
* @param response HTTP响应
* @param eweIds 已选母羊ID列表
* @param ramIds 已选公羊ID列表
*/
@Override
public void exportSelectedPairs(HttpServletResponse response, List<Long> eweIds, List<Long> ramIds)
{
try {
// 查询母羊耳号gender=1
List<Map<String, Object>> eweList = scBreedPlanGenerateMapper.selectEarNumbersByIds(eweIds, 1);
// 查询公羊耳号gender=2
List<Map<String, Object>> ramList = scBreedPlanGenerateMapper.selectEarNumbersByIds(ramIds, 2);
// 建立ID->耳号 的快速查找
Map<Long, String> eweEarMap = new HashMap<>();
for (Map<String, Object> e : eweList) {
eweEarMap.put(Long.valueOf(e.get("id").toString()), e.get("manage_tags").toString());
}
Map<Long, String> ramEarMap = new HashMap<>();
for (Map<String, Object> r : ramList) {
ramEarMap.put(Long.valueOf(r.get("id").toString()), r.get("manage_tags").toString());
}
// 母羊列、公羊列各自独立写入,行数取两者最大值,不做轮询配对
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("配种配对表");
// 母羊表头样式(粉色)
CellStyle eweHeaderStyle = workbook.createCellStyle();
Font eweFont = workbook.createFont();
eweFont.setBold(true);
eweFont.setFontHeightInPoints((short) 12);
eweHeaderStyle.setFont(eweFont);
eweHeaderStyle.setAlignment(HorizontalAlignment.CENTER);
eweHeaderStyle.setFillForegroundColor(IndexedColors.ROSE.getIndex());
eweHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
setBorder(eweHeaderStyle);
// 公羊表头样式(浅蓝)
CellStyle ramHeaderStyle = workbook.createCellStyle();
Font ramFont = workbook.createFont();
ramFont.setBold(true);
ramFont.setFontHeightInPoints((short) 12);
ramHeaderStyle.setFont(ramFont);
ramHeaderStyle.setAlignment(HorizontalAlignment.CENTER);
ramHeaderStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
ramHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
setBorder(ramHeaderStyle);
CellStyle dataStyle = workbook.createCellStyle();
dataStyle.setAlignment(HorizontalAlignment.CENTER);
setBorder(dataStyle);
// 写入表头
Row headerRow = sheet.createRow(0);
Cell c1 = headerRow.createCell(0);
c1.setCellValue("母羊耳号");
c1.setCellStyle(eweHeaderStyle);
Cell c2 = headerRow.createCell(1);
c2.setCellValue("公羊耳号");
c2.setCellStyle(ramHeaderStyle);
// 母羊列和公羊列各自独立写入,行数取两者最大值
int maxRows = Math.max(eweIds.size(), ramIds.size());
for (int i = 0; i < maxRows; i++) {
Row row = sheet.createRow(i + 1);
Cell dc1 = row.createCell(0);
if (i < eweIds.size()) {
dc1.setCellValue(eweEarMap.getOrDefault(eweIds.get(i), ""));
}
dc1.setCellStyle(dataStyle);
Cell dc2 = row.createCell(1);
if (i < ramIds.size()) {
dc2.setCellValue(ramEarMap.getOrDefault(ramIds.get(i), ""));
}
dc2.setCellStyle(dataStyle);
}
sheet.setColumnWidth(0, 5000);
sheet.setColumnWidth(1, 5000);
// 响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = java.net.URLEncoder.encode("配种配对表", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
workbook.write(response.getOutputStream());
workbook.close();
} catch (IOException e) {
throw new RuntimeException("导出配对表失败", e);
}
}
/** 给样式设置边框 */
private void setBorder(CellStyle style) {
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
}
/**
* 解析导入的配对Excel返回配对预览列表
* Excel格式第一列=母羊耳号,第二列=公羊耳号,第一行为表头(跳过)
*
* @param file 上传的Excel文件
* @return 配对列表,每项包含 eweEarNo、ramEarNo、eweId可能null、ramId可能null
*/
@Override
public List<Map<String, Object>> parsePairsFromExcel(MultipartFile file)
{
List<Map<String, Object>> result = new ArrayList<>();
try (InputStream is = file.getInputStream();
Workbook workbook = WorkbookFactory.create(is)) {
Sheet sheet = workbook.getSheetAt(0);
int lastRow = sheet.getLastRowNum();
// 从第2行index=1开始跳过表头
for (int i = 1; i <= lastRow; i++) {
Row row = sheet.getRow(i);
if (row == null) continue;
String eweEarNo = getCellStringValue(row.getCell(0));
String ramEarNo = getCellStringValue(row.getCell(1));
if (eweEarNo.isEmpty() && ramEarNo.isEmpty()) continue;
Map<String, Object> pair = new HashMap<>();
pair.put("eweEarNo", eweEarNo);
pair.put("ramEarNo", ramEarNo);
pair.put("rowIndex", i);
// 尝试查找对应的ID用于校验
Long eweId = null;
Long ramId = null;
if (!eweEarNo.isEmpty()) {
eweId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(eweEarNo, 1); // 母羊 gender=1
}
if (!ramEarNo.isEmpty()) {
ramId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(ramEarNo, 2); // 公羊 gender=2
}
pair.put("eweId", eweId);
pair.put("ramId", ramId);
// 校验状态
if (eweEarNo.isEmpty()) {
pair.put("valid", false);
pair.put("error", "母羊耳号不能为空");
} else if (ramEarNo.isEmpty()) {
pair.put("valid", false);
pair.put("error", "公羊耳号不能为空");
} else if (eweId == null) {
pair.put("valid", false);
pair.put("error", "母羊耳号[" + eweEarNo + "]未找到");
} else if (ramId == null) {
pair.put("valid", false);
pair.put("error", "公羊耳号[" + ramEarNo + "]未找到");
} else {
pair.put("valid", true);
pair.put("error", "");
}
result.add(pair);
}
} catch (Exception e) {
throw new RuntimeException("解析Excel失败" + e.getMessage(), e);
}
return result;
}
/** 安全读取单元格字符串值 */
private String getCellStringValue(Cell cell) {
if (cell == null) return "";
switch (cell.getCellType()) {
case STRING: return cell.getStringCellValue().trim();
case NUMERIC: return String.valueOf((long) cell.getNumericCellValue());
default: return "";
}
}
/**
* 根据导入的配对数据(耳号列表)生成配种计划
* pairs 中每项包含 eweEarNo 和 ramEarNo或 eweId/ramId
*
* @param planType 计划类型
* @param planName 计划名称
* @param eweIds 母羊ID列表
* @param ramIds 公羊ID列表
* @param pairs 配对数据列表
* @return 生成的配种计划
*/
@Override
@Transactional
public ScBreedPlanGenerate autoGenerateBreedPlanFromPairs(Integer planType, List<Map<String, Object>> pairs)
{
// 收集所有有效的 eweId / ramId去重统计用
List<Long> eweIds = new ArrayList<>();
List<Long> ramIds = new ArrayList<>();
for (Map<String, Object> pair : pairs) {
Long eweId = null;
Long ramId = null;
// 优先用已解析的 ID其次用耳号现查
if (pair.get("eweId") != null) {
eweId = Long.valueOf(pair.get("eweId").toString());
} else if (pair.get("eweEarNo") != null) {
eweId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(pair.get("eweEarNo").toString(), 1); // 母羊 gender=1
}
if (pair.get("ramId") != null) {
ramId = Long.valueOf(pair.get("ramId").toString());
} else if (pair.get("ramEarNo") != null) {
ramId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(pair.get("ramEarNo").toString(), 2); // 公羊 gender=2
}
if (eweId == null || ramId == null) continue; // 跳过无效行
eweIds.add(eweId);
if (!ramIds.contains(ramId)) ramIds.add(ramId);
}
if (eweIds.isEmpty()) {
throw new RuntimeException("没有有效的配对数据,请检查耳号是否存在");
}
// 创建计划汇总记录
ScBreedPlanGenerate planGenerate = new ScBreedPlanGenerate();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = sdf.format(new Date());
String planTypeName;
switch (planType) {
case 1: planTypeName = "供体母羊配种计划"; break;
case 2: planTypeName = "同期发情人工授精计划"; break;
case 3: planTypeName = "本交配种计划"; break;
case 4: planTypeName = "自然发情人工授精计划"; break;
default: planTypeName = "配种计划"; break;
}
planGenerate.setPlanName(dateStr + planTypeName);
planGenerate.setPlanType(planType);
planGenerate.setPlanDate(new Date());
planGenerate.setTotalEweCount(eweIds.size());
planGenerate.setTotalRamCount(ramIds.size());
double ratio = ramIds.isEmpty() ? 0 : (double) eweIds.size() / ramIds.size();
planGenerate.setBreedRatio(String.format("%.1f:1", ratio));
planGenerate.setStatus(0); // 待审批
planGenerate.setCreateBy(SecurityUtils.getUsername());
planGenerate.setCreateTime(new Date());
scBreedPlanGenerateMapper.insertScBreedPlanGenerate(planGenerate);
// 按导入的配对逐条插入临时计划(保留用户指定的配对关系)
for (Map<String, Object> pair : pairs) {
Long eweId = null;
Long ramId = null;
if (pair.get("eweId") != null) {
eweId = Long.valueOf(pair.get("eweId").toString());
} else if (pair.get("eweEarNo") != null) {
eweId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(pair.get("eweEarNo").toString(), 1); // 母羊 gender=1
}
if (pair.get("ramId") != null) {
ramId = Long.valueOf(pair.get("ramId").toString());
} else if (pair.get("ramEarNo") != null) {
ramId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(pair.get("ramEarNo").toString(), 2); // 公羊 gender=2
}
if (eweId == null || ramId == null) continue;
ScBreedPlan breedPlan = new ScBreedPlan();
breedPlan.setRamId(ramId.toString());
breedPlan.setEweId(eweId.toString());
breedPlan.setBreedType(Long.valueOf(planType));
scBreedPlanGenerateMapper.insertTempBreedPlan(planGenerate.getId(), breedPlan);
}
return planGenerate;
}
/**
* 自动生成配种计划(按比例自动分配,原有逻辑不变)
*/
@Override
@Transactional
public ScBreedPlanGenerate autoGenerateBreedPlan(Integer planType, String planName, List<Long> eweIds, List<Long> ramIds)
{
// 创建配种计划生成记录
ScBreedPlanGenerate planGenerate = new ScBreedPlanGenerate();
// 自动生成计划名称:日期+计划类型
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = sdf.format(new Date());
String planTypeName = "";
String planTypeName;
switch (planType) {
case 1: planTypeName = "供体母羊配种计划"; break;
case 2: planTypeName = "同期发情人工授精计划"; break;
@@ -112,7 +381,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
case 4: planTypeName = "自然发情人工授精计划"; break;
default: planTypeName = "未知配种计划"; break;
}
planName = dateStr + planTypeName;
planGenerate.setPlanName(planName);
@@ -120,26 +388,18 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
planGenerate.setPlanDate(new Date());
planGenerate.setTotalEweCount(eweIds.size());
planGenerate.setTotalRamCount(ramIds.size());
// 计算配种比例
double ratio = (double) eweIds.size() / ramIds.size();
planGenerate.setBreedRatio(String.format("%.1f:1", ratio));
planGenerate.setStatus(0); // 待审批
planGenerate.setStatus(0);
planGenerate.setCreateBy(SecurityUtils.getUsername());
planGenerate.setCreateTime(new Date());
// 保存配种计划生成记录
scBreedPlanGenerateMapper.insertScBreedPlanGenerate(planGenerate);
// 生成具体的配种计划
generateBreedPlanDetails(planGenerate.getId(), eweIds, ramIds, planType);
return planGenerate;
}
/**
* 生成具体的配种计划详情
*/
private void generateBreedPlanDetails(Long planGenerateId, List<Long> eweIds, List<Long> ramIds, Integer planType)
{
int ramIndex = 0;
@@ -149,9 +409,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
ScBreedPlan breedPlan = new ScBreedPlan();
breedPlan.setRamId(ramIds.get(ramIndex).toString());
breedPlan.setEweId(eweIds.get(i).toString());
// 2. 修改:直接使用 planType 作为 breedType (假设一一对应)
// 1=供体母羊配种, 2=同期发情人工授精, 3=本交, 4=自然发情人工授精
breedPlan.setBreedType(Long.valueOf(planType));
scBreedPlanGenerateMapper.insertTempBreedPlan(planGenerateId, breedPlan);
@@ -164,9 +421,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/**
* 新增配种计划生成
*
* @param scBreedPlanGenerate 配种计划生成
* @return 结果
*/
@Override
public int insertScBreedPlanGenerate(ScBreedPlanGenerate scBreedPlanGenerate)
@@ -177,9 +431,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/**
* 修改配种计划生成
*
* @param scBreedPlanGenerate 配种计划生成
* @return 结果
*/
@Override
public int updateScBreedPlanGenerate(ScBreedPlanGenerate scBreedPlanGenerate)
@@ -190,44 +441,27 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/**
* 获取审批配种计划详情
*
* @param id 配种计划ID
* @return 审批配种计划详情
*/
@Override
public Map<String, Object> getApproveBreedPlanDetails(Long id)
{
Map<String, Object> result = new HashMap<>();
// 获取配种计划基本信息
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateMapper.selectScBreedPlanGenerateById(id);
result.put("planInfo", planGenerate);
// 获取详细的配种计划信息
List<Map<String, Object>> planDetails = scBreedPlanGenerateMapper.selectApproveBreedPlanDetails(id);
result.put("planDetails", planDetails);
// 获取可选择的公羊列表
List<Map<String, Object>> availableRams = scBreedPlanGenerateMapper.selectEligibleRam();
result.put("availableRams", availableRams);
return result;
}
/**
* 确认审批配种计划
*
* @param planId 配种计划ID
* @param planDetails 配种计划详情
* @param status 审批状态
* @param approveRemark 审批意见
* @return 结果
*/
@Override
@Transactional
public int confirmApproveBreedPlan(Long planId, List<Map<String, Object>> planDetails, Integer status, String approveRemark)
{
// 更新审批状态
ScBreedPlanGenerate planGenerate = new ScBreedPlanGenerate();
planGenerate.setId(planId);
planGenerate.setStatus(status);
@@ -238,16 +472,13 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
int result = scBreedPlanGenerateMapper.updateScBreedPlanGenerate(planGenerate);
// 如果审批通过,更新临时配种计划并转为正式计划
if (result > 0 && status == 1) {
// 更新临时配种计划中的公羊分配
if (planDetails != null && !planDetails.isEmpty()) {
for (Map<String, Object> detail : planDetails) {
Long tempId = Long.valueOf(detail.get("id").toString());
Long ramId = Long.valueOf(detail.get("ram_id").toString());
Long breedType = Long.valueOf(detail.get("breed_type").toString());
// 更新临时配种计划
Map<String, Object> updateParams = new HashMap<>();
updateParams.put("id", tempId);
updateParams.put("ramId", ramId);
@@ -255,65 +486,41 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
scBreedPlanGenerateMapper.updateTempBreedPlan(updateParams);
}
}
// 将临时配种计划转为正式配种计划
scBreedPlanGenerateMapper.transferTempToFormal(planId);
}
return result;
}
/**
* 获取配种计划详情
*
* @param id 配种计划ID
* @return 配种计划详情
*/
@Override
public Map<String, Object> getBreedPlanDetails(Long id)
{
Map<String, Object> result = new HashMap<>();
// 获取配种计划基本信息
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateMapper.selectScBreedPlanGenerateById(id);
result.put("planInfo", planGenerate);
// 获取配种计划详情列表
List<Map<String, Object>> planDetails = scBreedPlanGenerateMapper.selectBreedPlanDetails(id);
result.put("planDetails", planDetails);
return result;
}
/**
* 导出配种计划详情
*
* @param response HTTP响应
* @param id 配种计划ID
* 导出配种计划详情(已审批)
*/
@Override
public void exportBreedPlanDetails(HttpServletResponse response, Long id)
{
try {
// 获取配种计划基本信息
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateMapper.selectScBreedPlanGenerateById(id);
if (planGenerate == null) {
throw new RuntimeException("配种计划不存在");
}
if (planGenerate == null) throw new RuntimeException("配种计划不存在");
if (planGenerate.getStatus() != 1) throw new RuntimeException("只有已审批的配种计划才能导出");
// 检查是否已审批
if (planGenerate.getStatus() != 1) {
throw new RuntimeException("只有已审批的配种计划才能导出");
}
// 获取配种计划详情
List<Map<String, Object>> planDetails = scBreedPlanGenerateMapper.selectBreedPlanDetails(id);
// 创建工作簿
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("配种计划详情");
// 创建样式
CellStyle titleStyle = workbook.createCellStyle();
Font titleFont = workbook.createFont();
titleFont.setBold(true);
@@ -350,28 +557,14 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
int rowNum = 0;
// 标题
Row titleRow = sheet.createRow(rowNum++);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue(planGenerate.getPlanName() + " - 配种计划详情");
titleCell.setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 19));
// 空行
rowNum++;
String typeName = "";
switch (planGenerate.getPlanType()) {
case 1: typeName = "供体母羊配种计划"; break;
case 2: typeName = "同期发情人工授精计划"; break;
case 3: typeName = "本交配种计划"; break;
case 4: typeName = "自然发情人工授精计划"; break;
default: typeName = "未知"; break;
}
// 基本信息
Row infoRow1 = sheet.createRow(rowNum++);
infoRow1.createCell(0).setCellValue("计划类型:");
String planTypeNameStr;
Integer type = planGenerate.getPlanType();
if (type != null) {
@@ -385,6 +578,9 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
} else {
planTypeNameStr = "";
}
Row infoRow1 = sheet.createRow(rowNum++);
infoRow1.createCell(0).setCellValue("计划类型:");
infoRow1.createCell(1).setCellValue(planTypeNameStr);
infoRow1.createCell(3).setCellValue("计划日期:");
infoRow1.createCell(4).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(planGenerate.getPlanDate()));
@@ -401,55 +597,39 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
infoRow3.createCell(3).setCellValue("创建人:");
infoRow3.createCell(4).setCellValue(planGenerate.getCreateBy());
// 空行
rowNum++;
// 分组表头
Row groupHeaderRow = sheet.createRow(rowNum++);
Cell groupHeaderCell1 = groupHeaderRow.createCell(1);
groupHeaderCell1.setCellValue("母羊信息");
groupHeaderCell1.setCellStyle(eweHeaderStyle);
Cell gc1 = groupHeaderRow.createCell(1);
gc1.setCellValue("母羊信息");
gc1.setCellStyle(eweHeaderStyle);
sheet.addMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 1, 12));
Cell groupHeaderCell2 = groupHeaderRow.createCell(13);
groupHeaderCell2.setCellValue("公羊信息");
groupHeaderCell2.setCellStyle(ramHeaderStyle);
Cell gc2 = groupHeaderRow.createCell(13);
gc2.setCellValue("公羊信息");
gc2.setCellStyle(ramHeaderStyle);
sheet.addMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 13, 19));
// 详细表头
Row headerRow = sheet.createRow(rowNum++);
String[] headers = {
"序号",
// 母羊信息
"母羊耳号", "母羊品种", "母羊家系", "母羊类别", "繁育状态", "胎次", "月龄", "体重", "核心羊群", "是否种用", "羊舍", "备注",
// 公羊信息
"公羊耳号", "公羊品种", "公羊家系", "公羊类别", "生日", "月龄", "体重",
// 配种信息
"配种类型"
};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
if (i == 0 || i == headers.length - 1) {
cell.setCellStyle(headerStyle);
} else if (i <= 12) {
cell.setCellStyle(eweHeaderStyle);
} else {
cell.setCellStyle(ramHeaderStyle);
}
if (i == 0 || i == headers.length - 1) cell.setCellStyle(headerStyle);
else if (i <= 12) cell.setCellStyle(eweHeaderStyle);
else cell.setCellStyle(ramHeaderStyle);
}
// 数据行
for (int i = 0; i < planDetails.size(); i++) {
Map<String, Object> detail = planDetails.get(i);
Row dataRow = sheet.createRow(rowNum++);
int colNum = 0;
// 序号
dataRow.createCell(colNum++).setCellValue(i + 1);
// 母羊信息
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_manage_tags"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_variety"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_family"));
@@ -462,8 +642,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
dataRow.createCell(colNum++).setCellValue(getBooleanValue(detail, "ewe_is_breeding") ? "" : "");
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_sheepfold_name"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_comment"));
// 公羊信息
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_manage_tags"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_variety"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_family"));
@@ -472,48 +650,36 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_month_age"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_current_weight"));
// 配种类型
Object breedTypeObj = detail.get("breed_type");
String breedTypeName = "未知类型";
if (breedTypeObj != null) {
try {
int typeValue = Integer.parseInt(breedTypeObj.toString());
switch (typeValue) {
int tv = Integer.parseInt(breedTypeObj.toString());
switch (tv) {
case 1: breedTypeName = "供体母羊配种"; break;
case 2: breedTypeName = "同期发情人工授精"; break;
case 3: breedTypeName = "本交"; break;
case 4: breedTypeName = "自然发情人工授精"; break;
default: breedTypeName = "未知类型";
}
} catch (NumberFormatException e) {
// ignore
} catch (NumberFormatException ignored) {}
}
}
dataRow.createCell(colNum++).setCellValue(breedTypeName);
// 应用数据样式
dataRow.createCell(colNum).setCellValue(breedTypeName);
for (int j = 0; j < headers.length; j++) {
if (dataRow.getCell(j) != null) {
dataRow.getCell(j).setCellStyle(dataStyle);
}
if (dataRow.getCell(j) != null) dataRow.getCell(j).setCellStyle(dataStyle);
}
}
// 自动调整列宽
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
// 设置最小宽度
if (sheet.getColumnWidth(i) < 2000) {
sheet.setColumnWidth(i, 2000);
}
if (sheet.getColumnWidth(i) < 2000) sheet.setColumnWidth(i, 2000);
}
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = java.net.URLEncoder.encode(planGenerate.getPlanName() + "_配种计划详情", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 输出到响应流
workbook.write(response.getOutputStream());
workbook.close();
@@ -522,17 +688,11 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
}
}
/**
* 安全获取字符串值
*/
private String getStringValue(Map<String, Object> map, String key) {
Object value = map.get(key);
return value != null ? value.toString() : "";
}
/**
* 安全获取布尔值
*/
private boolean getBooleanValue(Map<String, Object> map, String key) {
Object value = map.get(key);
if (value == null) return false;
@@ -543,15 +703,11 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/**
* 批量删除配种计划生成
*
* @param ids 需要删除的配种计划生成主键
* @return 结果
*/
@Override
@Transactional
public int deleteScBreedPlanGenerateByIds(Long[] ids)
{
// 删除相关的临时配种计划
for (Long id : ids) {
scBreedPlanGenerateMapper.deleteTempBreedPlanByPlanId(id);
}
@@ -560,18 +716,15 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/**
* 删除配种计划生成信息
*
* @param id 配种计划生成主键
* @return 结果
*/
@Override
@Transactional
public int deleteScBreedPlanGenerateById(Long id)
{
// 先删除相关的临时配种计划
scBreedPlanGenerateMapper.deleteTempBreedPlanByPlanId(id);
return scBreedPlanGenerateMapper.deleteScBreedPlanGenerateById(id);
}
@Override
public List<String> searchEarNumbers(String query) {
return scBreedPlanGenerateMapper.searchEarNumbers(query);

View File

@@ -325,4 +325,35 @@
ORDER BY sf.bs_manage_tags
LIMIT 50
</select>
<!-- ========== 导出配对表 / 导入配对表 相关查询 ========== -->
<!--
根据ID列表查询羊只耳号
gender: 1=母羊 2=公羊
-->
<select id="selectEarNumbersByIds" resultType="java.util.Map">
SELECT id, manage_tags
FROM bas_sheep
WHERE gender = #{gender}
AND is_delete = 0
AND id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!--
根据管理耳号查询羊只ID
gender: 1=母羊 2=公羊
-->
<select id="selectSheepIdByManageTags" resultType="java.lang.Long">
SELECT id
FROM bas_sheep
WHERE manage_tags = #{manageTags}
AND gender = #{gender}
AND is_delete = 0
LIMIT 1
</select>
</mapper>