|
|
@@ -0,0 +1,310 @@
|
|
|
+package cn.iocoder.yudao.module.order.service.impl;
|
|
|
+
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
|
|
+import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
|
|
|
+import cn.iocoder.yudao.module.order.controller.admin.workorder.vo.*;
|
|
|
+import cn.iocoder.yudao.module.order.dal.dataobject.WorkOrdMasterDO;
|
|
|
+import cn.iocoder.yudao.module.order.dal.mysql.WorkOrdMasterMapper;
|
|
|
+import cn.iocoder.yudao.module.order.service.WorkOrderService;
|
|
|
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
|
|
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.mzt.logapi.context.LogRecordContext;
|
|
|
+import com.mzt.logapi.starter.annotation.LogRecord;
|
|
|
+import jakarta.annotation.Resource;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.http.HttpEntity;
|
|
|
+import org.springframework.http.HttpHeaders;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.validation.annotation.Validated;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
|
|
+import static cn.iocoder.yudao.module.order.enums.ErrorCodeConstants.WORK_ORDER_NOT_EXISTS;
|
|
|
+import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 工单 Service 实现类
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@Validated
|
|
|
+@Slf4j
|
|
|
+public class WorkOrderServiceImpl implements WorkOrderService {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private WorkOrdMasterMapper workOrdMasterMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ConfigApi configApi;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private AdminUserApi adminUserApi;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private RestTemplate restTemplate;
|
|
|
+
|
|
|
+ // 外部接口地址
|
|
|
+ private static final String MATERIAL_CHECK_URL = "http://123.60.180.165:9898/api/business/resource-examine/producedayplankittingcheck";
|
|
|
+ private static final String MATERIAL_REQUIREMENT_URL = "http://123.60.180.165:9898/api/business/resource-examine/AutomaticPrAdjustDate";
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @TenantIgnore
|
|
|
+ public PageResult<WorkOrderPoolRespVO> getWorkOrderPoolPage(WorkOrderPoolPageReqVO pageReqVO) {
|
|
|
+ try {
|
|
|
+ String domain = getDefaultDomain();
|
|
|
+ // 创建分页对象
|
|
|
+ Page<WorkOrderPoolRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
|
|
|
+ // 执行分页查询
|
|
|
+ List<WorkOrderPoolRespVO> list = workOrdMasterMapper.selectWorkOrderPoolPage(page, pageReqVO, domain);
|
|
|
+ // 返回分页结果
|
|
|
+ return new PageResult<>(list, page.getTotal());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("查询工单池列表失败", e);
|
|
|
+ throw new RuntimeException("查询工单池列表失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @TenantIgnore
|
|
|
+ public WorkOrderPoolRespVO getWorkOrder(Long id) {
|
|
|
+ WorkOrdMasterDO workOrder = workOrdMasterMapper.selectByRecId(id);
|
|
|
+ if (workOrder == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 转换为响应VO
|
|
|
+ WorkOrderPoolRespVO resp = new WorkOrderPoolRespVO();
|
|
|
+ resp.setId(workOrder.getRecId());
|
|
|
+ resp.setDomain(workOrder.getDomain());
|
|
|
+ resp.setWorkOrd(workOrder.getWorkOrd());
|
|
|
+ resp.setItemNum(workOrder.getItemNum());
|
|
|
+ resp.setDrawing(workOrder.getDrawing());
|
|
|
+ resp.setBatch(workOrder.getBatch());
|
|
|
+ resp.setLotSerial(workOrder.getLotSerial());
|
|
|
+ resp.setStatus(workOrder.getStatus());
|
|
|
+ resp.setTyped(workOrder.getTyped());
|
|
|
+ resp.setWoTyped(workOrder.getWoTyped());
|
|
|
+ resp.setPriority(workOrder.getPriority());
|
|
|
+ resp.setOrdDate(workOrder.getOrdDate());
|
|
|
+ resp.setDueDate(workOrder.getDueDate());
|
|
|
+ resp.setQtyOrded(workOrder.getQtyOrded());
|
|
|
+ resp.setQtyCompleted(workOrder.getQtyCompleted());
|
|
|
+ resp.setProject(workOrder.getProject());
|
|
|
+ resp.setRemark(workOrder.getRemark());
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @TenantIgnore
|
|
|
+ @LogRecord(type = WORK_ORDER_TYPE, subType = WORK_ORDER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
|
|
|
+ success = WORK_ORDER_UPDATE_SUCCESS)
|
|
|
+ public void updateWorkOrder(WorkOrderUpdateReqVO updateReqVO) {
|
|
|
+ // 校验存在
|
|
|
+ WorkOrdMasterDO exists = validateWorkOrderExists(updateReqVO.getId());
|
|
|
+
|
|
|
+ // 更新
|
|
|
+ WorkOrdMasterDO update = new WorkOrdMasterDO();
|
|
|
+ update.setRecId(updateReqVO.getId());
|
|
|
+ if (StrUtil.isNotBlank(updateReqVO.getLotSerial())) {
|
|
|
+ update.setLotSerial(updateReqVO.getLotSerial());
|
|
|
+ }
|
|
|
+ if (StrUtil.isNotBlank(updateReqVO.getOrdDate())) {
|
|
|
+ update.setOrdDate(LocalDateTime.parse(updateReqVO.getOrdDate() + " 00:00:00",
|
|
|
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
|
|
+ }
|
|
|
+ if (StrUtil.isNotBlank(updateReqVO.getStatus())) {
|
|
|
+ update.setStatus(updateReqVO.getStatus());
|
|
|
+ }
|
|
|
+ if (updateReqVO.getRemark() != null) {
|
|
|
+ update.setRemark(updateReqVO.getRemark());
|
|
|
+ }
|
|
|
+ update.setUpdateTime(LocalDateTime.now());
|
|
|
+ update.setUpdateUser(getCurrentUserName());
|
|
|
+
|
|
|
+ workOrdMasterMapper.updateById(update);
|
|
|
+
|
|
|
+ // 记录日志上下文
|
|
|
+ LogRecordContext.putVariable("workOrder", exists);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @TenantIgnore
|
|
|
+ @LogRecord(type = WORK_ORDER_TYPE, subType = WORK_ORDER_RELEASE_SUB_TYPE, bizNo = "{{#releaseReqVO.workOrderNo}}",
|
|
|
+ success = WORK_ORDER_RELEASE_SUCCESS)
|
|
|
+ public void releaseWorkOrder(WorkOrderReleaseReqVO releaseReqVO) {
|
|
|
+ String domain = getDefaultDomain();
|
|
|
+
|
|
|
+ // 查询工单
|
|
|
+ WorkOrdMasterDO workOrder = workOrdMasterMapper.selectByWorkOrd(releaseReqVO.getWorkOrderNo(), domain);
|
|
|
+ if (workOrder == null) {
|
|
|
+ throw exception(WORK_ORDER_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 1. 解析新开工日期
|
|
|
+ LocalDateTime newOrdDate = LocalDateTime.parse(releaseReqVO.getOrdDate() + " 00:00:00",
|
|
|
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
|
|
+
|
|
|
+ // 2. 判断开工日期是否小于当前日期
|
|
|
+ LocalDateTime now = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
|
|
|
+ if (newOrdDate.isBefore(now)) {
|
|
|
+ throw new RuntimeException("工单开工日期需调整至今日之后");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 计算旧开工日期与新开工日期的天数差
|
|
|
+ LocalDateTime oldOrdDate = workOrder.getOrdDate();
|
|
|
+ long daysDiff = 0;
|
|
|
+ if (oldOrdDate != null) {
|
|
|
+ daysDiff = java.time.Duration.between(
|
|
|
+ oldOrdDate.withHour(0).withMinute(0).withSecond(0).withNano(0),
|
|
|
+ newOrdDate
|
|
|
+ ).toDays();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 计算新的完工日期
|
|
|
+ LocalDateTime oldDueDate = workOrder.getDueDate();
|
|
|
+ LocalDateTime newDueDate = oldDueDate != null ? oldDueDate.plusDays(daysDiff) : newOrdDate.plusDays(7);
|
|
|
+
|
|
|
+ // 5. 计算新开工日期是第几周
|
|
|
+ int weekNum = newOrdDate.get(java.time.temporal.WeekFields.ISO.weekOfWeekBasedYear());
|
|
|
+ String week = "WK" + String.format("%02d", weekNum);
|
|
|
+
|
|
|
+ // 6. 更新 WorkOrdMaster 表
|
|
|
+ workOrdMasterMapper.updateWorkOrderDates(
|
|
|
+ releaseReqVO.getWorkOrderNo(),
|
|
|
+ releaseReqVO.getLotSerial(),
|
|
|
+ newOrdDate,
|
|
|
+ newDueDate,
|
|
|
+ getCurrentUserName()
|
|
|
+ );
|
|
|
+
|
|
|
+ // 7. 更新 ReplenishmentWeekPlan 表
|
|
|
+ workOrdMasterMapper.updateReplenishmentWeekPlan(
|
|
|
+ releaseReqVO.getWorkOrderNo(),
|
|
|
+ releaseReqVO.getLotSerial(),
|
|
|
+ newOrdDate,
|
|
|
+ newDueDate,
|
|
|
+ week
|
|
|
+ );
|
|
|
+
|
|
|
+ // 8. 调用外部接口进行物料齐套检查
|
|
|
+ String userAccount = getCurrentUserName();
|
|
|
+ String url = "http://123.60.180.165:8087/api/business/resource-examine/ProduceWorkOrdKittingCheck" +
|
|
|
+ "?workord=" + releaseReqVO.getWorkOrderNo() +
|
|
|
+ "&domain=" + domain +
|
|
|
+ "&userAccount=" + userAccount;
|
|
|
+
|
|
|
+ String response = restTemplate.getForObject(url, String.class);
|
|
|
+
|
|
|
+ // 检查返回结果
|
|
|
+ if (!"ok".equalsIgnoreCase(response)) {
|
|
|
+ throw new RuntimeException("工单下达失败:" + response);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("工单下达成功,工单号:{},生产批号:{},开工日期:{},完工日期:{},周次:{}",
|
|
|
+ releaseReqVO.getWorkOrderNo(), releaseReqVO.getLotSerial(),
|
|
|
+ newOrdDate, newDueDate, week);
|
|
|
+
|
|
|
+ // 记录日志上下文
|
|
|
+ LogRecordContext.putVariable("workOrder", workOrder);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("工单下达失败", e);
|
|
|
+ throw new RuntimeException(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @TenantIgnore
|
|
|
+ public void checkMaterialComplete(MaterialCheckReqVO checkReqVO) {
|
|
|
+ try {
|
|
|
+ String domain = getDefaultDomain();
|
|
|
+
|
|
|
+ // 获取当前用户账号
|
|
|
+ String userAccount = getCurrentUserName();
|
|
|
+
|
|
|
+ // 构建请求参数 - 注意参数名是 startime 和 endtime (没有 d)
|
|
|
+ String url = MATERIAL_CHECK_URL + "?domain=" + domain
|
|
|
+ + "&startime=" + checkReqVO.getStartDate()
|
|
|
+ + "&endtime=" + checkReqVO.getEndDate()
|
|
|
+ + "&userAccount=" + userAccount;
|
|
|
+
|
|
|
+ // 调用外部接口 - 使用 GET 方法
|
|
|
+ String response = restTemplate.getForObject(url, String.class);
|
|
|
+
|
|
|
+ // 检查返回结果
|
|
|
+ if (!"ok".equalsIgnoreCase(response)) {
|
|
|
+ throw new RuntimeException("物料齐套检查失败:" + response);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("物料齐套检查完成,参数:domain={}, startDate={}, endDate={}, userAccount={}",
|
|
|
+ domain, checkReqVO.getStartDate(), checkReqVO.getEndDate(), userAccount);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("物料齐套检查失败", e);
|
|
|
+ throw new RuntimeException("物料齐套检查失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @TenantIgnore
|
|
|
+ public void generateMaterialRequirement(MaterialRequirementReqVO reqVO) {
|
|
|
+ try {
|
|
|
+ String domain = StrUtil.isNotBlank(reqVO.getDomain()) ? reqVO.getDomain() : getDefaultDomain();
|
|
|
+
|
|
|
+ // 构建请求URL
|
|
|
+ String url = MATERIAL_REQUIREMENT_URL + "?domain=" + domain;
|
|
|
+ if (StrUtil.isNotBlank(reqVO.getCompanyId())) {
|
|
|
+ url += "&companyId=" + reqVO.getCompanyId();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调用外部接口
|
|
|
+ HttpHeaders headers = new HttpHeaders();
|
|
|
+ headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
+ HttpEntity<String> entity = new HttpEntity<>(headers);
|
|
|
+
|
|
|
+ restTemplate.getForEntity(url, String.class);
|
|
|
+ log.info("生成物料需求计划完成,参数:{}", reqVO);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("生成物料需求计划失败", e);
|
|
|
+ throw new RuntimeException("生成物料需求计划失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private WorkOrdMasterDO validateWorkOrderExists(Long id) {
|
|
|
+ WorkOrdMasterDO workOrder = workOrdMasterMapper.selectByRecId(id);
|
|
|
+ if (workOrder == null) {
|
|
|
+ throw exception(WORK_ORDER_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ return workOrder;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getDefaultDomain() {
|
|
|
+ try {
|
|
|
+ String domain = configApi.getConfigValueByKey("order.default.domain");
|
|
|
+ return StrUtil.isBlank(domain) ? "8010" : domain;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("获取默认域配置失败,使用默认值 8010", e);
|
|
|
+ return "8010";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getCurrentUserName() {
|
|
|
+ Long loginUserId = getLoginUserId();
|
|
|
+ if (loginUserId != null) {
|
|
|
+ AdminUserRespDTO user = adminUserApi.getUser(loginUserId);
|
|
|
+ if (user != null) {
|
|
|
+ return user.getNickname();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "system";
|
|
|
+ }
|
|
|
+}
|