farm模块-字典优化

This commit is contained in:
顾君禹
2025-09-02 16:09:39 +08:00
parent e76630a4a1
commit 8f3f2827b9
8 changed files with 495 additions and 39 deletions

View File

@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.farm.controller.admin.farmingnamemaintenance.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 农事名称维护树形结构 Response VO")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FarmingNameMaintenanceTreeRespVO {
@Schema(description = "农事类型编码", example = "FT001")
private String farmingTypeCode;
@Schema(description = "农事类型名称", example = "播种")
private String farmingTypeName;
@Schema(description = "农事名称编码", example = "FN001")
private String farmingCode;
@Schema(description = "农事名称", example = "玉米播种")
private String farmingName;
@Schema(description = "启用状态", example = "1")
private Integer useStatus;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "子节点列表")
private List<FarmingNameMaintenanceTreeRespVO> children;
@Schema(description = "是否为叶子节点", example = "true")
private Boolean leaf;
@Schema(description = "层级", example = "1")
private Integer level;
}

View File

@@ -19,12 +19,12 @@ public class ModelFarmingOperationInputPageReqVO extends PageParam {
@Schema(description = "投入品id", example = "5852")
private String inputsId;
private String inputsName;
@Schema(description = "用量")
private BigDecimal dose;
@Schema(description = "单位")
private String unit;
private Integer unit;
@Schema(description = "稀释倍液")
private String diluted;
@@ -50,6 +50,6 @@ public class ModelFarmingOperationInputPageReqVO extends PageParam {
@Schema(description = "租户id", example = "3440")
private String tenantId;
private String assetType;
private String packageUnit;
private Integer assetType;
private Integer packageUnit;
}

View File

@@ -24,14 +24,14 @@ public class ModelFarmingOperationInputRespVO {
@Schema(description = "投入品id", example = "5852")
@ExcelProperty("投入品id")
private String inputsId;
private String inputsName;
@Schema(description = "用量")
@ExcelProperty("用量")
private BigDecimal dose;
@Schema(description = "单位")
@ExcelProperty("单位")
private String unit;
private Integer unit;
@Schema(description = "稀释倍液")
@ExcelProperty("稀释倍液")
@@ -65,5 +65,5 @@ public class ModelFarmingOperationInputRespVO {
@ExcelProperty("租户id")
private String tenantId;
private String assetType;
private String packageUnit;
private Integer packageUnit;
}

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.farm.controller.admin.modelfarmingoperationinput.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.Data;
import java.math.BigDecimal;
@@ -17,12 +17,12 @@ public class ModelFarmingOperationInputSaveReqVO {
@Schema(description = "投入品id", example = "5852")
private String inputsId;
private String inputsName;
@Schema(description = "用量")
private BigDecimal dose;
@Schema(description = "单位")
private String unit;
private Integer unit;
@Schema(description = "稀释倍液")
private String diluted;
@@ -45,5 +45,5 @@ public class ModelFarmingOperationInputSaveReqVO {
@Schema(description = "租户id", example = "3440")
private String tenantId;
private String assetType;
private String packageUnit;
private Integer packageUnit;
}

View File

@@ -0,0 +1,263 @@
package cn.iocoder.yudao.module.farm.controller.admin.websocket;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;
import cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.socket.WebSocketSession;
import java.util.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* WebSocket连接测试 Controller
*
* @author 芋道源码
*/
@Tag(name = "管理后台 - WebSocket连接测试")
@RestController
@RequestMapping("/farm/websocket-connection")
@Validated
@Slf4j
public class WebSocketConnectionTestController {
@Resource
private WebSocketSessionManager webSocketSessionManager;
@GetMapping("/status")
@Operation(summary = "获取WebSocket连接状态")
@PreAuthorize("@ss.hasPermission('farm:websocket:push')")
public CommonResult<Map<String, Object>> getConnectionStatus() {
log.info("[getConnectionStatus][获取WebSocket连接状态]");
Map<String, Object> result = new HashMap<>();
try {
// 获取当前登录用户信息
LoginUser currentUser = SecurityFrameworkUtils.getLoginUser();
Map<String, Object> userInfo = new HashMap<>();
if (currentUser != null) {
userInfo.put("userId", currentUser.getId());
userInfo.put("userType", currentUser.getUserType());
userInfo.put("tenantId", currentUser.getTenantId());
userInfo.put("username", currentUser.getInfo() != null ? currentUser.getInfo().get("nickname") : "未知");
} else {
userInfo.put("message", "当前无登录用户");
}
result.put("currentUser", userInfo);
// 获取所有用户类型的session统计
Map<String, Object> sessionStats = new HashMap<>();
int totalSessions = 0;
for (int userType = 1; userType <= 3; userType++) {
Collection<WebSocketSession> sessions = webSocketSessionManager.getSessionList(userType);
int sessionCount = sessions.size();
totalSessions += sessionCount;
Map<String, Object> userTypeStats = new HashMap<>();
userTypeStats.put("count", sessionCount);
userTypeStats.put("userTypeName", getUserTypeName(userType));
// 详细session信息
List<Map<String, Object>> sessionDetails = new ArrayList<>();
for (WebSocketSession session : sessions) {
Map<String, Object> sessionInfo = new HashMap<>();
sessionInfo.put("sessionId", session.getId());
sessionInfo.put("isOpen", session.isOpen());
sessionInfo.put("remoteAddress", session.getRemoteAddress());
sessionInfo.put("localAddress", session.getLocalAddress());
LoginUser loginUser = WebSocketFrameworkUtils.getLoginUser(session);
if (loginUser != null) {
sessionInfo.put("userId", loginUser.getId());
sessionInfo.put("userType", loginUser.getUserType());
sessionInfo.put("tenantId", loginUser.getTenantId());
sessionInfo.put("username", loginUser.getInfo() != null ? loginUser.getInfo().get("nickname") : "未知");
} else {
sessionInfo.put("userInfo", "用户信息为空");
}
sessionDetails.add(sessionInfo);
}
userTypeStats.put("sessions", sessionDetails);
sessionStats.put("userType_" + userType, userTypeStats);
}
sessionStats.put("totalSessions", totalSessions);
result.put("sessionStats", sessionStats);
// WebSocket配置信息
Map<String, Object> configInfo = new HashMap<>();
configInfo.put("path", "/infra/ws");
configInfo.put("enable", true);
configInfo.put("senderType", "local");
result.put("configInfo", configInfo);
// 连接状态评估
Map<String, Object> statusAssessment = new HashMap<>();
if (totalSessions == 0) {
statusAssessment.put("status", "无连接");
statusAssessment.put("message", "当前没有WebSocket连接,可能的原因:1.前端未建立连接 2.连接路径不正确 3.用户认证失败");
statusAssessment.put("level", "warning");
} else {
statusAssessment.put("status", "正常");
statusAssessment.put("message", "WebSocket连接正常,共有" + totalSessions + "个活跃连接");
statusAssessment.put("level", "success");
}
result.put("statusAssessment", statusAssessment);
result.put("success", true);
result.put("timestamp", new Date());
} catch (Exception e) {
log.error("[getConnectionStatus][获取连接状态失败]", e);
result.put("success", false);
result.put("error", e.getMessage());
}
return success(result);
}
@GetMapping("/test-connection")
@Operation(summary = "测试WebSocket连接")
@PreAuthorize("@ss.hasPermission('farm:websocket:push')")
public CommonResult<String> testConnection() {
log.info("[testConnection][测试WebSocket连接]");
StringBuilder result = new StringBuilder();
result.append("=== WebSocket连接测试报告 ===\n\n");
// 检查当前用户
LoginUser currentUser = SecurityFrameworkUtils.getLoginUser();
if (currentUser != null) {
String nickname = currentUser.getInfo() != null ? currentUser.getInfo().get("nickname") : "未知";
result.append("当前登录用户: ").append(nickname)
.append(" (ID: ").append(currentUser.getId())
.append(", Type: ").append(currentUser.getUserType())
.append(", Tenant: ").append(currentUser.getTenantId()).append(")\n\n");
} else {
result.append("当前无登录用户\n\n");
}
// 检查各用户类型的session
int totalSessions = 0;
for (int userType = 1; userType <= 3; userType++) {
Collection<WebSocketSession> sessions = webSocketSessionManager.getSessionList(userType);
result.append("用户类型").append(userType).append(" (").append(getUserTypeName(userType)).append("): ")
.append(sessions.size()).append(" 个连接\n");
if (sessions.isEmpty()) {
result.append(" - 无连接\n");
} else {
for (WebSocketSession session : sessions) {
LoginUser loginUser = WebSocketFrameworkUtils.getLoginUser(session);
if (loginUser != null) {
String nickname = loginUser.getInfo() != null ? loginUser.getInfo().get("nickname") : "未知";
result.append(" - Session ID: ").append(session.getId())
.append(", User: ").append(nickname)
.append(", Status: ").append(session.isOpen() ? "在线" : "离线")
.append("\n");
} else {
result.append(" - Session ID: ").append(session.getId())
.append(", User: 未知")
.append(", Status: ").append(session.isOpen() ? "在线" : "离线")
.append("\n");
}
}
}
totalSessions += sessions.size();
}
result.append("\n总计: ").append(totalSessions).append(" 个WebSocket连接\n\n");
if (totalSessions == 0) {
result.append("=== 连接问题诊断 ===\n");
result.append("1. 检查前端是否正确建立WebSocket连接\n");
result.append("2. 确认连接路径为: ws://your-domain/infra/ws?token=your-token\n");
result.append("3. 验证用户token是否有效\n");
result.append("4. 检查浏览器控制台是否有连接错误\n");
result.append("5. 确认WebSocket服务已启用 (yudao.websocket.enable=true)\n");
} else {
result.append("=== 连接状态正常 ===\n");
result.append("WebSocket连接工作正常,可以进行消息推送测试\n");
}
return success(result.toString());
}
@GetMapping("/connection-details")
@Operation(summary = "获取详细连接信息")
@PreAuthorize("@ss.hasPermission('farm:websocket:push')")
public CommonResult<Map<String, Object>> getConnectionDetails() {
log.info("[getConnectionDetails][获取详细连接信息]");
Map<String, Object> result = new HashMap<>();
try {
// 获取所有session的详细信息
List<Map<String, Object>> allSessions = new ArrayList<>();
for (int userType = 1; userType <= 3; userType++) {
Collection<WebSocketSession> sessions = webSocketSessionManager.getSessionList(userType);
for (WebSocketSession session : sessions) {
Map<String, Object> sessionDetail = new HashMap<>();
sessionDetail.put("sessionId", session.getId());
sessionDetail.put("userType", userType);
sessionDetail.put("userTypeName", getUserTypeName(userType));
sessionDetail.put("isOpen", session.isOpen());
sessionDetail.put("remoteAddress", session.getRemoteAddress() != null ? session.getRemoteAddress().toString() : "未知");
sessionDetail.put("localAddress", session.getLocalAddress() != null ? session.getLocalAddress().toString() : "未知");
sessionDetail.put("protocol", session.getAcceptedProtocol());
sessionDetail.put("handshakeHeaders", session.getHandshakeHeaders());
LoginUser loginUser = WebSocketFrameworkUtils.getLoginUser(session);
if (loginUser != null) {
sessionDetail.put("userId", loginUser.getId());
sessionDetail.put("tenantId", loginUser.getTenantId());
sessionDetail.put("username", loginUser.getInfo() != null ? loginUser.getInfo().get("nickname") : "未知");
sessionDetail.put("userInfo", loginUser.getInfo());
} else {
sessionDetail.put("userInfo", "用户信息为空");
}
allSessions.add(sessionDetail);
}
}
result.put("sessions", allSessions);
result.put("totalCount", allSessions.size());
result.put("success", true);
} catch (Exception e) {
log.error("[getConnectionDetails][获取详细连接信息失败]", e);
result.put("success", false);
result.put("error", e.getMessage());
}
return success(result);
}
/**
* 获取用户类型名称
*/
private String getUserTypeName(int userType) {
switch (userType) {
case 1: return "管理后台用户";
case 2: return "用户APP用户";
case 3: return "其他类型用户";
default: return "未知用户类型(" + userType + ")";
}
}
}

View File

@@ -0,0 +1,142 @@
package cn.iocoder.yudao.module.farm.controller.admin.websocket;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;
import cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.socket.WebSocketSession;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* WebSocket测试 Controller
*
* @author 芋道源码
*/
@Tag(name = "管理后台 - WebSocket测试")
@RestController
@RequestMapping("/farm/websocket-test")
@Validated
@Slf4j
public class WebSocketTestController {
@Resource
private WebSocketSessionManager webSocketSessionManager;
@GetMapping("/connection-info")
@Operation(summary = "获取WebSocket连接信息")
@PreAuthorize("@ss.hasPermission('farm:websocket:push')")
public CommonResult<Map<String, Object>> getConnectionInfo() {
log.info("[getConnectionInfo][获取WebSocket连接信息]");
Map<String, Object> result = new HashMap<>();
try {
// 获取当前登录用户信息
LoginUser currentUser = SecurityFrameworkUtils.getLoginUser();
if (currentUser != null) {
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("userId", currentUser.getId());
userInfo.put("userType", currentUser.getUserType());
userInfo.put("tenantId", currentUser.getTenantId());
userInfo.put("username", currentUser.getInfo() != null ? currentUser.getInfo().get("nickname") : "未知");
result.put("currentUser", userInfo);
} else {
result.put("currentUser", null);
}
// 获取所有session信息
Map<String, Object> sessionInfo = new HashMap<>();
for (int userType = 1; userType <= 3; userType++) {
Collection<WebSocketSession> sessions = webSocketSessionManager.getSessionList(userType);
sessionInfo.put("userType_" + userType, sessions.size());
// 详细session信息
for (WebSocketSession session : sessions) {
LoginUser loginUser = WebSocketFrameworkUtils.getLoginUser(session);
if (loginUser != null) {
log.info("[getConnectionInfo][Session详情][sessionId: {}][userId: {}][userType: {}][tenantId: {}]",
session.getId(), loginUser.getId(), loginUser.getUserType(), loginUser.getTenantId());
}
}
}
result.put("sessionInfo", sessionInfo);
// WebSocket配置信息
Map<String, Object> wsConfig = new HashMap<>();
wsConfig.put("path", "/infra/ws");
wsConfig.put("enable", true);
wsConfig.put("senderType", "local");
result.put("websocketConfig", wsConfig);
result.put("success", true);
result.put("message", "获取连接信息成功");
} catch (Exception e) {
log.error("[getConnectionInfo][获取连接信息失败]", e);
result.put("success", false);
result.put("message", "获取连接信息失败: " + e.getMessage());
}
return success(result);
}
@GetMapping("/test-connection")
@Operation(summary = "测试WebSocket连接")
@PreAuthorize("@ss.hasPermission('farm:websocket:push')")
public CommonResult<String> testConnection() {
log.info("[testConnection][测试WebSocket连接]");
StringBuilder result = new StringBuilder();
result.append("WebSocket连接测试结果:\n");
// 检查当前用户
LoginUser currentUser = SecurityFrameworkUtils.getLoginUser();
if (currentUser != null) {
String nickname = currentUser.getInfo() != null ? currentUser.getInfo().get("nickname") : "未知";
result.append("当前登录用户: ").append(nickname)
.append(" (ID: ").append(currentUser.getId())
.append(", Type: ").append(currentUser.getUserType())
.append(", Tenant: ").append(currentUser.getTenantId()).append(")\n");
} else {
result.append("当前无登录用户\n");
}
// 检查session
Collection<WebSocketSession> sessions = webSocketSessionManager.getSessionList(1);
result.append("用户类型1的session数量: ").append(sessions.size()).append("\n");
if (sessions.isEmpty()) {
result.append("没有找到WebSocket连接,可能的原因:\n");
result.append("1. 前端未建立WebSocket连接\n");
result.append("2. 连接路径不正确 (应该是 /infra/ws)\n");
result.append("3. 用户认证失败\n");
result.append("4. 连接已断开\n");
} else {
result.append("找到WebSocket连接:\n");
for (WebSocketSession session : sessions) {
LoginUser loginUser = WebSocketFrameworkUtils.getLoginUser(session);
if (loginUser != null) {
String nickname = loginUser.getInfo() != null ? loginUser.getInfo().get("nickname") : "未知";
result.append("- Session ID: ").append(session.getId())
.append(", User: ").append(nickname)
.append(", UserType: ").append(loginUser.getUserType()).append("\n");
}
}
}
return success(result.toString());
}
}

View File

@@ -44,12 +44,12 @@ public class ModelFarmingOperationInputDO extends BaseDO {
/**
* 单位
*/
private String unit;
private Integer unit;
private String unitName;
/**
* 单件
*/
private String packageUnit;
private Integer packageUnit;
private String packageUnitName;
/**
* 稀释倍液
@@ -80,5 +80,5 @@ public class ModelFarmingOperationInputDO extends BaseDO {
private String tenantId;
private String assetType;
private Integer assetType;
}

View File

@@ -340,38 +340,47 @@ public class CropFarmingCenterServiceImpl implements CropFarmingCenterService {
public ProductionPlanResp2VO.PlanOverview getPlanOverview(ProductionPlanDO planObj) {
BaseInfoDO baseObj = baseInfoMapper.selectOne("id", planObj.getBaseId());
ProductionPlanRespVO.PlanOverview source = productionPlanMapper.getPlanOverview(planObj.getBaseId());
source.setTotalArea(baseObj.getArea());
return BeanUtils.toBean(source, ProductionPlanResp2VO.PlanOverview.class);
if (baseObj != null) {
ProductionPlanRespVO.PlanOverview source = productionPlanMapper.getPlanOverview(planObj.getBaseId());
source.setTotalArea(baseObj.getArea());
return BeanUtils.toBean(source, ProductionPlanResp2VO.PlanOverview.class);
} else {
return null;
}
}
public ProductionPlanResp2VO.ModelInfo getModelManagement(ProductionPlanDO planObj, Map<String, String> employeeNameMap) {
BaseInfoDO baseObj = baseInfoMapper.selectOne("id", planObj.getBaseId());
String employeeId = baseObj.getManager();
if (baseObj != null) {
String employeeId = baseObj.getManager();
ModelManagementDO modelManagementDo = modelManagementService.getModelManagement("id", planObj.getSpeciesModelId());
ModelManagementDO modelManagementDo = modelManagementService.getModelManagement("id", planObj.getSpeciesModelId());
ProductionPlanResp2VO.ModelInfo modelInfo = new ProductionPlanResp2VO.ModelInfo();
modelInfo.setId(modelManagementDo.getId());
modelInfo.setModelName(modelManagementDo.getModelName());
modelInfo.setSpeciesImage(modelManagementDo.getSpeciesImage());
modelInfo.setPlantSpeciesName(modelManagementDo.getSpeciesName());
modelInfo.setPlanNumber(planObj.getPlanNumber());
modelInfo.setPlanStatus(planObj.getPlanStatus());
modelInfo.setPlanSubStatus(ObjectUtils.defaultIfNull(planObj.getPlanSubStatus(), planObj.getPlanStatus()));
modelInfo.setPlanStatusName(PLAN_MAP.getOrDefault(planObj.getPlanStatus(), ""));
modelInfo.setPlanSubStatusName(PLAN_MAP.getOrDefault(planObj.getPlanSubStatus(), ""));
modelInfo.setCycleName(modelManagementDo.getCycleName());
modelInfo.setPredictedArea(planObj.getPredictedArea());
modelInfo.setPredictedYields(planObj.getPredictedYields());
modelInfo.setProgressBar("1/10"); // TODO: 2025/8/30
modelInfo.setProgressRatio("10"); // TODO: 2025/8/31
modelInfo.setProgressNumerator(1);
modelInfo.setProgressDenominator(10);
modelInfo.setBaseName(planObj.getBaseName());
modelInfo.setBaseManager(employeeNameMap.get(employeeId)); // 分割,
return modelInfo;
} else {
return null;
}
ProductionPlanResp2VO.ModelInfo modelInfo = new ProductionPlanResp2VO.ModelInfo();
modelInfo.setId(modelManagementDo.getId());
modelInfo.setModelName(modelManagementDo.getModelName());
modelInfo.setSpeciesImage(modelManagementDo.getSpeciesImage());
modelInfo.setPlantSpeciesName(modelManagementDo.getSpeciesName());
modelInfo.setPlanNumber(planObj.getPlanNumber());
modelInfo.setPlanStatus(planObj.getPlanStatus());
modelInfo.setPlanSubStatus(ObjectUtils.defaultIfNull(planObj.getPlanSubStatus(), planObj.getPlanStatus()));
modelInfo.setPlanStatusName(PLAN_MAP.getOrDefault(planObj.getPlanStatus(), ""));
modelInfo.setPlanSubStatusName(PLAN_MAP.getOrDefault(planObj.getPlanSubStatus(), ""));
modelInfo.setCycleName(modelManagementDo.getCycleName());
modelInfo.setPredictedArea(planObj.getPredictedArea());
modelInfo.setPredictedYields(planObj.getPredictedYields());
modelInfo.setProgressBar("1/10"); // TODO: 2025/8/30
modelInfo.setProgressRatio("10"); // TODO: 2025/8/31
modelInfo.setProgressNumerator(1);
modelInfo.setProgressDenominator(10);
modelInfo.setBaseName(planObj.getBaseName());
modelInfo.setBaseManager(employeeNameMap.get(employeeId)); // 分割,
return modelInfo;
}
private int countIntervalDays(ProductionPlanDO aDo) {