This commit is contained in:
2026-05-15 15:10:34 +08:00
34 changed files with 1693 additions and 215 deletions

View File

@@ -40,13 +40,16 @@ import org.jeecg.modules.xslmes.service.IMesXslWarehouseAreaService;
import org.jeecg.modules.xslmes.service.IMesXslWarehouseService;
import org.jeecg.modules.xslmes.service.IMesXslWeightRecordService;
import org.jeecg.modules.xslmes.service.MesXslStompNotifyService;
import org.jeecg.modules.xslmes.vo.MesXslRawMaterialCardBriefVO;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
/**
@@ -511,19 +514,48 @@ public class MesXslDesktopAnonController {
return Result.OK("编辑成功!");
}
@Operation(summary = "原料入场记录-免密查询关联原材料卡片")
@GetMapping("/xslmes/mesXslRawMaterialEntry/anon/linkedRawMaterialCards")
public Result<List<MesXslRawMaterialCardBriefVO>> rawMaterialEntryAnonLinkedRawMaterialCards(
@RequestParam(name = "ids") String ids) {
List<String> idList = Arrays.stream(ids.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.distinct()
.toList();
return Result.OK(rawMaterialEntryService.listLinkedRawMaterialCards(idList));
}
@Operation(summary = "原料入场记录-免密删除")
@DeleteMapping("/xslmes/mesXslRawMaterialEntry/anon/delete")
public Result<String> rawMaterialEntryAnonDelete(@RequestParam(name = "id") String id) {
rawMaterialEntryService.removeById(id);
stompNotify.publishRawMaterialEntryChanged("delete", id);
public Result<?> rawMaterialEntryAnonDelete(
@RequestParam(name = "id") String id,
@RequestParam(name = "cascadeDeleteCards", defaultValue = "true") boolean cascadeDeleteCards) {
Optional<List<MesXslRawMaterialCardBriefVO>> blocked =
rawMaterialEntryService.removeEntriesRespectingLinkedCards(List.of(id), cascadeDeleteCards);
if (blocked.isPresent()) {
return Result.error(
"存在已生成的原材料卡片但未允许级联删除,请传 cascadeDeleteCards=true。", blocked.get());
}
return Result.OK("删除成功!");
}
@Operation(summary = "原料入场记录-免密批量删除")
@DeleteMapping("/xslmes/mesXslRawMaterialEntry/anon/deleteBatch")
public Result<String> rawMaterialEntryAnonDeleteBatch(@RequestParam(name = "ids") String ids) {
rawMaterialEntryService.removeByIds(Arrays.asList(ids.split(",")));
stompNotify.publishRawMaterialEntryChanged("batchDelete", ids);
public Result<?> rawMaterialEntryAnonDeleteBatch(
@RequestParam(name = "ids") String ids,
@RequestParam(name = "cascadeDeleteCards", defaultValue = "true") boolean cascadeDeleteCards) {
List<String> idList = Arrays.stream(ids.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.distinct()
.toList();
Optional<List<MesXslRawMaterialCardBriefVO>> blocked =
rawMaterialEntryService.removeEntriesRespectingLinkedCards(idList, cascadeDeleteCards);
if (blocked.isPresent()) {
return Result.error(
"存在已生成的原材料卡片但未允许级联删除,请传 cascadeDeleteCards=true。", blocked.get());
}
return Result.OK("批量删除成功!");
}
@@ -563,9 +595,11 @@ public class MesXslDesktopAnonController {
if (oConvertUtils.isEmpty(entity.getId())) {
return Result.error("主键不能为空");
}
boolean ok = rawMaterialCardService.updateById(entity);
boolean ok =
rawMaterialCardService.updateEditableInventoryFields(
entity, "桌面端免密编辑", "桌面端(免密)");
if (!ok) {
return Result.error("数据已被他人修改,请刷新后重试");
return Result.error("未找到数据或更新失败,请刷新后重试");
}
stompNotify.publishRawMaterialCardChanged("edit", entity.getId());
return Result.OK("编辑成功!");

View File

@@ -138,7 +138,12 @@ public class MesXslRawMaterialCardController extends JeecgController<MesXslRawMa
@RequiresPermissions("xslmes:mes_xsl_raw_material_card:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
public Result<String> edit(@RequestBody MesXslRawMaterialCard mesXslRawMaterialCard) {
mesXslRawMaterialCardService.updateById(mesXslRawMaterialCard);
boolean ok =
mesXslRawMaterialCardService.updateEditableInventoryFields(
mesXslRawMaterialCard, "Web端原材料卡片", null);
if (!ok) {
return Result.error("未找到数据或更新失败,请刷新后重试");
}
stompNotify.publishRawMaterialCardChanged("edit", mesXslRawMaterialCard.getId());
return Result.OK("编辑成功!");
}

View File

@@ -0,0 +1,62 @@
package org.jeecg.modules.xslmes.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCardEditLog;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialCardEditLogService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
/**
* 原材料卡片修改日志(只读查询)
*/
@Tag(name = "原材料卡片修改日志")
@RestController
@RequestMapping("/xslmes/mesXslRawMaterialCardEditLog")
@Slf4j
public class MesXslRawMaterialCardEditLogController
extends JeecgController<MesXslRawMaterialCardEditLog, IMesXslRawMaterialCardEditLogService> {
@Operation(summary = "原材料卡片修改日志-分页列表查询")
@RequiresPermissions("xslmes:mes_xsl_raw_material_card_edit_log:list")
@GetMapping(value = "/list")
public Result<IPage<MesXslRawMaterialCardEditLog>> queryPageList(
MesXslRawMaterialCardEditLog entity,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<MesXslRawMaterialCardEditLog> qw = QueryGenerator.initQueryWrapper(entity, req.getParameterMap());
qw.orderByDesc("modify_time");
Page<MesXslRawMaterialCardEditLog> page = new Page<>(pageNo, pageSize);
return Result.OK(service.page(page, qw));
}
@Operation(summary = "原材料卡片修改日志-通过id查询")
@RequiresPermissions("xslmes:mes_xsl_raw_material_card_edit_log:list")
@GetMapping(value = "/queryById")
public Result<MesXslRawMaterialCardEditLog> queryById(@RequestParam(name = "id", required = true) String id) {
MesXslRawMaterialCardEditLog row = service.getById(id);
if (row == null) {
return Result.error("未找到对应数据");
}
return Result.OK(row);
}
@RequiresPermissions("xslmes:mes_xsl_raw_material_card_edit_log:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, MesXslRawMaterialCardEditLog entity) {
return super.exportXls(request, entity, MesXslRawMaterialCardEditLog.class, "原材料卡片修改日志");
}
}

View File

@@ -15,6 +15,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.Logical;
@@ -34,6 +35,7 @@ import org.jeecg.modules.xslmes.constant.MesXslPrintConstants;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialEntryService;
import org.jeecg.modules.xslmes.service.MesXslStompNotifyService;
import org.jeecg.modules.xslmes.vo.MesXslRawMaterialCardBriefVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@@ -105,13 +107,36 @@ public class MesXslRawMaterialEntryController extends JeecgController<MesXslRawM
return Result.OK("编辑成功!");
}
@Operation(summary = "原料入场记录-查询关联的已生成原材料卡片(删除前校验)")
@GetMapping("/linkedRawMaterialCards")
@RequiresPermissions(
value = {"xslmes:mes_xsl_raw_material_entry:delete", "xslmes:mes_xsl_raw_material_entry:deleteBatch"},
logical = Logical.OR)
public Result<List<MesXslRawMaterialCardBriefVO>> linkedRawMaterialCards(
@RequestParam(name = "ids", required = true) String ids) {
List<String> idList = Arrays.stream(ids.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.distinct()
.toList();
return Result.OK(mesXslRawMaterialEntryService.listLinkedRawMaterialCards(idList));
}
@AutoLog(value = "原料入场记录-通过id删除")
@Operation(summary = "原料入场记录-通过id删除")
@Operation(summary = "原料入场记录-通过id删除cascadeDeleteCards=true 时同时删除关联原材料卡片)")
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
mesXslRawMaterialEntryService.removeById(id);
stompNotify.publishRawMaterialEntryChanged("delete", id);
public Result<?> delete(
@RequestParam(name = "id", required = true) String id,
@RequestParam(name = "cascadeDeleteCards", defaultValue = "false") boolean cascadeDeleteCards) {
Optional<List<MesXslRawMaterialCardBriefVO>> blocked =
mesXslRawMaterialEntryService.removeEntriesRespectingLinkedCards(
List.of(id), cascadeDeleteCards);
if (blocked.isPresent()) {
return Result.error(
"该原料入场记录已生成原材料卡片,确认删除时将一并删除下列卡片。请传 cascadeDeleteCards=true 重试。",
blocked.get());
}
return Result.OK("删除成功!");
}
@@ -130,12 +155,24 @@ public class MesXslRawMaterialEntryController extends JeecgController<MesXslRawM
}
@AutoLog(value = "原料入场记录-批量删除")
@Operation(summary = "原料入场记录-批量删除")
@Operation(summary = "原料入场记录-批量删除cascadeDeleteCards=true 时同时删除关联原材料卡片)")
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.mesXslRawMaterialEntryService.removeByIds(Arrays.asList(ids.split(",")));
stompNotify.publishRawMaterialEntryChanged("batchDelete", ids);
public Result<?> deleteBatch(
@RequestParam(name = "ids", required = true) String ids,
@RequestParam(name = "cascadeDeleteCards", defaultValue = "false") boolean cascadeDeleteCards) {
List<String> idList = Arrays.stream(ids.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.distinct()
.toList();
Optional<List<MesXslRawMaterialCardBriefVO>> blocked =
mesXslRawMaterialEntryService.removeEntriesRespectingLinkedCards(idList, cascadeDeleteCards);
if (blocked.isPresent()) {
return Result.error(
"选中记录关联的原材料卡片仍存在,确认删除时将一并删除下列卡片。请传 cascadeDeleteCards=true 重试。",
blocked.get());
}
return Result.OK("批量删除成功!");
}

View File

@@ -0,0 +1,49 @@
package org.jeecg.modules.xslmes.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialEntryService;
import org.jeecg.modules.xslmes.vo.MesXslRawMaterialEntryDeleteLogVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 原料入场删除日志:查询已逻辑删除的入场记录(不落单独日志表)
*/
@Tag(name = "原料入场删除日志")
@RestController
@RequestMapping("/xslmes/mesXslRawMaterialEntryDeleteLog")
public class MesXslRawMaterialEntryDeleteLogController {
@Autowired
private IMesXslRawMaterialEntryService mesXslRawMaterialEntryService;
@Operation(summary = "原料入场删除日志-分页查询")
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry_delete_log:list")
@GetMapping(value = "/list")
public Result<IPage<MesXslRawMaterialEntryDeleteLogVO>> queryPageList(
MesXslRawMaterialEntry query,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
MesXslRawMaterialEntry q = query == null ? new MesXslRawMaterialEntry() : query;
q.setDelFlag(null);
QueryWrapper<MesXslRawMaterialEntry> qw = QueryGenerator.initQueryWrapper(q, req.getParameterMap());
qw.eq("del_flag", CommonConstant.DEL_FLAG_1);
qw.orderByDesc("update_time").orderByDesc("create_time");
Page<MesXslRawMaterialEntryDeleteLogVO> page = new Page<>(pageNo, pageSize);
return Result.OK(mesXslRawMaterialEntryService.pageDeletedLog(page, qw));
}
}

View File

@@ -0,0 +1,94 @@
package org.jeecg.modules.xslmes.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
/**
* 原材料卡片修改日志(编辑剩余量/库区时落库)
*/
@Data
@TableName("mes_xsl_raw_material_card_edit_log")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description = "原材料卡片修改日志")
public class MesXslRawMaterialCardEditLog implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private String id;
@Excel(name = "卡片ID", width = 28)
@Schema(description = "原材料卡片ID")
private String cardId;
@Excel(name = "条码", width = 22)
@Schema(description = "条码")
private String barcode;
@Excel(name = "批次号", width = 18)
@Schema(description = "批次号")
private String batchNo;
@Excel(name = "物料名称", width = 22)
@Schema(description = "物料名称")
private String materialName;
@Excel(name = "物料ID", width = 22)
@Schema(description = "物料ID")
private String materialId;
@Excel(name = "修改前重量", width = 14)
@Schema(description = "修改前剩余重量")
private BigDecimal beforeRemainingWeight;
@Excel(name = "修改前数量", width = 12)
@Schema(description = "修改前剩余数量")
private Integer beforeRemainingQty;
@Excel(name = "修改后重量", width = 14)
@Schema(description = "修改后剩余重量")
private BigDecimal afterRemainingWeight;
@Excel(name = "修改前库区", width = 16)
@Schema(description = "修改前库区")
private String beforeWarehouseArea;
@Excel(name = "修改后数量", width = 12)
@Schema(description = "修改后剩余数量")
private Integer afterRemainingQty;
@Excel(name = "修改后库区", width = 16)
@Schema(description = "修改后库区")
private String afterWarehouseArea;
@Excel(name = "修改时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "修改时间")
private Date modifyTime;
@Excel(name = "修改人姓名", width = 16)
@Schema(description = "修改人姓名")
private String modifyByName;
@Excel(name = "租户ID", width = 10)
@Schema(description = "租户ID")
private Integer tenantId;
@Excel(name = "数值来源", width = 22)
@Schema(description = "数值来源")
private String dataSource;
}

View File

@@ -5,6 +5,7 @@ import java.math.BigDecimal;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
@@ -204,6 +205,11 @@ public class MesXslRawMaterialEntry implements Serializable {
@Schema(description = "更新日期")
private Date updateTime;
/**逻辑删除0正常 1已删除删除接口改为打标便于「删除日志」查询。*/
@Schema(description = "逻辑删除0正常 1已删除")
@TableLogic
private Integer delFlag;
@Schema(description = "租户ID")
private Integer tenantId;
}

View File

@@ -0,0 +1,9 @@
package org.jeecg.modules.xslmes.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCardEditLog;
/**
* 原材料卡片修改日志
*/
public interface MesXslRawMaterialCardEditLogMapper extends BaseMapper<MesXslRawMaterialCardEditLog> {}

View File

@@ -1,7 +1,13 @@
package org.jeecg.modules.xslmes.mapper;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
import org.jeecg.modules.xslmes.vo.MesXslRawMaterialEntryDeleteLogVO;
/**
* @Description: 原料入场记录
@@ -10,4 +16,11 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
* @Version: V1.0
*/
public interface MesXslRawMaterialEntryMapper extends BaseMapper<MesXslRawMaterialEntry> {
/**
* 分页查询已逻辑删除的入场记录(自定义 SQL不按未删除过滤
*/
IPage<MesXslRawMaterialEntryDeleteLogVO> selectDeletedPage(
Page<MesXslRawMaterialEntryDeleteLogVO> page,
@Param(Constants.WRAPPER) QueryWrapper<MesXslRawMaterialEntry> queryWrapper);
}

View File

@@ -1,4 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.xslmes.mapper.MesXslRawMaterialEntryMapper">
<select id="selectDeletedPage" resultType="org.jeecg.modules.xslmes.vo.MesXslRawMaterialEntryDeleteLogVO">
SELECT
t.barcode,
t.batch_no,
t.create_by,
t.create_time,
t.material_name
FROM mes_xsl_raw_material_entry t
${ew.customSqlSegment}
</select>
</mapper>

View File

@@ -0,0 +1,9 @@
package org.jeecg.modules.xslmes.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCardEditLog;
/**
* 原材料卡片修改日志
*/
public interface IMesXslRawMaterialCardEditLogService extends IService<MesXslRawMaterialCardEditLog> {}

View File

@@ -10,4 +10,15 @@ import com.baomidou.mybatisplus.extension.service.IService;
* @Version: V1.0
*/
public interface IMesXslRawMaterialCardService extends IService<MesXslRawMaterialCard> {
/**
* 列表/桌面端「编辑」仅允许调整剩余数量、剩余重量与库区,其它字段忽略且不写库。
*
* @param incoming 至少包含 id仅读取 remainingQuantity、remainingWeight、warehouseArea
* @param dataSource 数值来源说明(如 Web端原材料卡片、桌面端免密编辑空则默认 Web端
* @param modifierRealnameOverride 修改人姓名;空则取当前登录用户真实姓名(免密接口可传入固定文案)
* @return 是否存在主键且更新成功(受影响行大于 0
*/
boolean updateEditableInventoryFields(
MesXslRawMaterialCard incoming, String dataSource, String modifierRealnameOverride);
}

View File

@@ -1,11 +1,18 @@
package org.jeecg.modules.xslmes.service;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
import org.jeecg.modules.xslmes.vo.MesXslRawMaterialCardBriefVO;
import org.jeecg.modules.xslmes.vo.MesXslRawMaterialEntryDeleteLogVO;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* @Description: 原料入场记录
@@ -15,6 +22,26 @@ import java.util.Map;
*/
public interface IMesXslRawMaterialEntryService extends IService<MesXslRawMaterialEntry> {
/**
* 原料入场删除日志:分页查询已逻辑删除的记录(仅条码/批次/创建人/创建时间/物料名称)。
*/
IPage<MesXslRawMaterialEntryDeleteLogVO> pageDeletedLog(
Page<MesXslRawMaterialEntryDeleteLogVO> page,
QueryWrapper<MesXslRawMaterialEntry> queryWrapper);
/**
* 根据入场记录主键查询已生成的原材料卡片(按拆码明细 ID 优先,否则按条码+批次等降级关联)。
*/
List<MesXslRawMaterialCardBriefVO> listLinkedRawMaterialCards(Collection<String> entryIds);
/**
* 删除入场记录;若存在关联卡片且 cascadeDeleteCards=false不删除任何数据并返回关联列表。
*
* @return empty 表示已删除入场记录(并在 cascade 为 true 时删除关联卡片);非空则为被拦截时的关联卡片
*/
Optional<List<MesXslRawMaterialCardBriefVO>> removeEntriesRespectingLinkedCards(
Collection<String> entryIds, boolean cascadeDeleteCards);
/**
* 生成条码/批次号
* 格式QH + 物料编码 + 日期(yyMMdd) + 当日序号(001起)

View File

@@ -0,0 +1,15 @@
package org.jeecg.modules.xslmes.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCardEditLog;
import org.jeecg.modules.xslmes.mapper.MesXslRawMaterialCardEditLogMapper;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialCardEditLogService;
import org.springframework.stereotype.Service;
/**
* 原材料卡片修改日志
*/
@Service
public class MesXslRawMaterialCardEditLogServiceImpl
extends ServiceImpl<MesXslRawMaterialCardEditLogMapper, MesXslRawMaterialCardEditLog>
implements IMesXslRawMaterialCardEditLogService {}

View File

@@ -1,10 +1,20 @@
package org.jeecg.modules.xslmes.service.impl;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCard;
import org.jeecg.modules.xslmes.mapper.MesXslRawMaterialCardMapper;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialCardService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCard;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCardEditLog;
import org.jeecg.modules.xslmes.mapper.MesXslRawMaterialCardMapper;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialCardEditLogService;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialCardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @Description: 原材料卡片
@@ -13,5 +23,94 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
* @Version: V1.0
*/
@Service
public class MesXslRawMaterialCardServiceImpl extends ServiceImpl<MesXslRawMaterialCardMapper, MesXslRawMaterialCard> implements IMesXslRawMaterialCardService {
public class MesXslRawMaterialCardServiceImpl extends ServiceImpl<MesXslRawMaterialCardMapper, MesXslRawMaterialCard>
implements IMesXslRawMaterialCardService {
@Autowired
private IMesXslRawMaterialCardEditLogService editLogService;
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateEditableInventoryFields(
MesXslRawMaterialCard incoming, String dataSource, String modifierRealnameOverride) {
if (incoming == null || StringUtils.isBlank(incoming.getId())) {
return false;
}
MesXslRawMaterialCard existing = getById(incoming.getId());
if (existing == null) {
return false;
}
String area = incoming.getWarehouseArea();
String areaTrimmed = area == null ? null : StringUtils.trimToNull(area);
boolean weightChanged = !bigDecimalEquals(existing.getRemainingWeight(), incoming.getRemainingWeight());
boolean qtyChanged = !Objects.equals(existing.getRemainingQuantity(), incoming.getRemainingQuantity());
boolean areaChanged = !Objects.equals(normArea(existing.getWarehouseArea()), areaTrimmed);
boolean anyChanged = weightChanged || qtyChanged || areaChanged;
boolean ok = lambdaUpdate()
.eq(MesXslRawMaterialCard::getId, incoming.getId())
.set(MesXslRawMaterialCard::getRemainingWeight, incoming.getRemainingWeight())
.set(MesXslRawMaterialCard::getRemainingQuantity, incoming.getRemainingQuantity())
.set(MesXslRawMaterialCard::getWarehouseArea, areaTrimmed)
.update();
if (!ok) {
return false;
}
if (!anyChanged) {
return true;
}
String ds = StringUtils.isNotBlank(dataSource) ? dataSource : "Web端原材料卡片";
String modifier = resolveModifierName(modifierRealnameOverride);
MesXslRawMaterialCardEditLog logRow = new MesXslRawMaterialCardEditLog();
logRow.setCardId(existing.getId());
logRow.setBarcode(existing.getBarcode());
logRow.setBatchNo(existing.getBatchNo());
logRow.setMaterialName(existing.getMaterialName());
logRow.setMaterialId(existing.getMaterialId());
logRow.setBeforeRemainingWeight(existing.getRemainingWeight());
logRow.setBeforeRemainingQty(existing.getRemainingQuantity());
logRow.setAfterRemainingWeight(incoming.getRemainingWeight());
logRow.setBeforeWarehouseArea(existing.getWarehouseArea());
logRow.setAfterRemainingQty(incoming.getRemainingQuantity());
logRow.setAfterWarehouseArea(areaTrimmed);
logRow.setModifyTime(new Date());
logRow.setModifyByName(modifier);
logRow.setTenantId(existing.getTenantId());
logRow.setDataSource(ds);
editLogService.save(logRow);
return true;
}
private static String normArea(String s) {
return s == null ? null : StringUtils.trimToNull(s);
}
private static boolean bigDecimalEquals(BigDecimal a, BigDecimal b) {
if (a == null && b == null) {
return true;
}
if (a == null || b == null) {
return false;
}
return a.compareTo(b) == 0;
}
private static String resolveModifierName(String override) {
if (StringUtils.isNotBlank(override)) {
return override;
}
try {
Object p = SecurityUtils.getSubject().getPrincipal();
if (p instanceof LoginUser) {
String name = ((LoginUser) p).getRealname();
if (StringUtils.isNotBlank(name)) {
return name;
}
}
} catch (Exception ignored) {
// 免密或未登录场景
}
return "未知";
}
}

View File

@@ -1,7 +1,10 @@
package org.jeecg.modules.xslmes.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCard;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
@@ -12,6 +15,9 @@ import org.jeecg.modules.xslmes.service.IMesXslRawMaterialCardService;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialInventoryService;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialEntryService;
import org.jeecg.modules.xslmes.service.IMesXslWarehouseAreaService;
import org.jeecg.modules.xslmes.service.MesXslStompNotifyService;
import org.jeecg.modules.xslmes.vo.MesXslRawMaterialCardBriefVO;
import org.jeecg.modules.xslmes.vo.MesXslRawMaterialEntryDeleteLogVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@@ -20,13 +26,17 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -49,6 +59,117 @@ public class MesXslRawMaterialEntryServiceImpl
private IMesXslWarehouseAreaService mesXslWarehouseAreaService;
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private MesXslStompNotifyService stompNotify;
@Override
public IPage<MesXslRawMaterialEntryDeleteLogVO> pageDeletedLog(
Page<MesXslRawMaterialEntryDeleteLogVO> page, QueryWrapper<MesXslRawMaterialEntry> queryWrapper) {
return baseMapper.selectDeletedPage(page, queryWrapper);
}
@Override
public List<MesXslRawMaterialCardBriefVO> listLinkedRawMaterialCards(Collection<String> entryIds) {
if (entryIds == null || entryIds.isEmpty()) {
return Collections.emptyList();
}
List<String> distinctIds = entryIds.stream()
.filter(Objects::nonNull)
.map(String::trim)
.filter(s -> !s.isEmpty())
.distinct()
.toList();
if (distinctIds.isEmpty()) {
return Collections.emptyList();
}
List<MesXslRawMaterialEntry> entries = this.listByIds(distinctIds);
Map<String, MesXslRawMaterialCard> byId = new LinkedHashMap<>();
for (MesXslRawMaterialEntry entry : entries) {
for (MesXslRawMaterialCard card : findLinkedCardsForEntry(entry)) {
if (card.getId() != null) {
byId.putIfAbsent(card.getId(), card);
}
}
}
return byId.values().stream().map(MesXslRawMaterialEntryServiceImpl::toCardBrief).toList();
}
@Override
@Transactional(rollbackFor = Exception.class)
public Optional<List<MesXslRawMaterialCardBriefVO>> removeEntriesRespectingLinkedCards(
Collection<String> entryIds, boolean cascadeDeleteCards) {
if (entryIds == null || entryIds.isEmpty()) {
return Optional.empty();
}
List<String> distinctIds = entryIds.stream()
.filter(Objects::nonNull)
.map(String::trim)
.filter(s -> !s.isEmpty())
.distinct()
.toList();
if (distinctIds.isEmpty()) {
return Optional.empty();
}
List<MesXslRawMaterialCardBriefVO> linked = listLinkedRawMaterialCards(distinctIds);
if (!linked.isEmpty() && !cascadeDeleteCards) {
return Optional.of(linked);
}
if (!linked.isEmpty()) {
List<String> cardIds = linked.stream()
.map(MesXslRawMaterialCardBriefVO::getId)
.filter(Objects::nonNull)
.distinct()
.toList();
if (!cardIds.isEmpty()) {
mesXslRawMaterialCardService.removeByIds(cardIds);
stompNotify.publishRawMaterialCardChanged("batchDelete", String.join(",", cardIds));
}
}
this.removeByIds(distinctIds);
String action = distinctIds.size() > 1 ? "batchDelete" : "delete";
stompNotify.publishRawMaterialEntryChanged(action, String.join(",", distinctIds));
return Optional.empty();
}
/** 单条入场记录关联的原材料卡片:优先拆码明细 GUID其次条码+批次等 */
private List<MesXslRawMaterialCard> findLinkedCardsForEntry(MesXslRawMaterialEntry entry) {
if (entry == null) {
return Collections.emptyList();
}
String[] splitIds = splitJoined(entry.getPortionDetailIds());
if (splitIds.length > 0) {
return mesXslRawMaterialCardService
.lambdaQuery()
.in(MesXslRawMaterialCard::getSplitDetailId, Arrays.asList(splitIds))
.list();
}
String bc = normalizeText(entry.getBarcode());
String bn = normalizeText(entry.getBatchNo());
if (!bc.isEmpty() && !bn.isEmpty()) {
return mesXslRawMaterialCardService
.lambdaQuery()
.eq(MesXslRawMaterialCard::getBarcode, bc)
.eq(MesXslRawMaterialCard::getBatchNo, bn)
.list();
}
if (!bc.isEmpty()) {
return mesXslRawMaterialCardService.lambdaQuery().eq(MesXslRawMaterialCard::getBarcode, bc).list();
}
if (!bn.isEmpty()) {
return mesXslRawMaterialCardService.lambdaQuery().eq(MesXslRawMaterialCard::getBatchNo, bn).list();
}
return Collections.emptyList();
}
private static MesXslRawMaterialCardBriefVO toCardBrief(MesXslRawMaterialCard c) {
MesXslRawMaterialCardBriefVO v = new MesXslRawMaterialCardBriefVO();
v.setId(c.getId());
v.setBarcode(c.getBarcode());
v.setBatchNo(c.getBatchNo());
v.setMaterialName(c.getMaterialName());
v.setSplitDetailId(c.getSplitDetailId());
return v;
}
@Override
public String generateBarcode(String materialCode) {

View File

@@ -0,0 +1,30 @@
package org.jeecg.modules.xslmes.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import lombok.Data;
/**
* 原材料卡片简要信息(入场删除确认弹窗用)
*/
@Data
@Schema(description = "原材料卡片简要信息")
public class MesXslRawMaterialCardBriefVO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "卡片主键")
private String id;
@Schema(description = "条码")
private String barcode;
@Schema(description = "批次号")
private String batchNo;
@Schema(description = "物料名称")
private String materialName;
@Schema(description = "关联拆码明细行ID可空")
private String splitDetailId;
}

View File

@@ -0,0 +1,35 @@
package org.jeecg.modules.xslmes.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
/**
* 原料入场「删除日志」列表专用 VO仅展示字段不落单独日志表
*/
@Data
@Schema(description = "原料入场删除日志展示")
public class MesXslRawMaterialEntryDeleteLogVO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "条码")
private String barcode;
@Schema(description = "批次号")
private String batchNo;
@Schema(description = "创建人")
private String createBy;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "物料名称")
private String materialName;
}