diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/capacity/impl/RawMaterialCardRemainQtyCapacityContribution.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/capacity/impl/RawMaterialCardRemainQtyCapacityContribution.java index 7e113f3..d7b07e5 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/capacity/impl/RawMaterialCardRemainQtyCapacityContribution.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/capacity/impl/RawMaterialCardRemainQtyCapacityContribution.java @@ -4,17 +4,15 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jeecg.modules.xslmes.capacity.WarehouseAreaActualCapacityContribution; -import org.jeecg.modules.xslmes.config.XslMesWarehouseAreaCapacityProperties; import org.jeecg.modules.xslmes.dto.MesXslAreaRemainQtySumDTO; -import org.jeecg.modules.system.service.ISysCategoryService; import org.jeecg.modules.xslmes.entity.MesXslWarehouseArea; import org.jeecg.modules.xslmes.mapper.MesXslRawMaterialCardMapper; +import org.jeecg.modules.xslmes.service.IMesXslWarehouseAreaCapacityCfgService; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -23,6 +21,7 @@ import java.util.stream.Collectors; /** * 用「原材料卡片」按库区(trim)汇总 remaining_quantity,回填展示字段 actual_capacity(不写库)。 + * 匹配哪些仓库分类由 {@link IMesXslWarehouseAreaCapacityCfgService}(页面配置 / YAML 兜底)决定。 */ @Slf4j @Component @@ -30,21 +29,20 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class RawMaterialCardRemainQtyCapacityContribution implements WarehouseAreaActualCapacityContribution { - private final XslMesWarehouseAreaCapacityProperties properties; + private final IMesXslWarehouseAreaCapacityCfgService capacityCfgService; private final MesXslRawMaterialCardMapper mesXslRawMaterialCardMapper; - private final ISysCategoryService sysCategoryService; @Override public void contribute(List areas) { if (areas == null || areas.isEmpty()) { return; } - if (!properties.isEnabled()) { + if (!capacityCfgService.isActualCapacityBackfillEnabled()) { return; } - Set allowedCategories = resolveAllowedWarehouseCategoryIds(); + Set allowedCategories = capacityCfgService.resolveRawMaterialWarehouseCategoryIds(); if (allowedCategories.isEmpty()) { - log.debug("[WarehouseAreaActualCapacity] 原材料库分类编码/ID 均未解析到有效 warehouse_category,跳过 RawMaterialCard 策略"); + log.debug("[WarehouseAreaActualCapacity] 原材料库分类未配置或解析为空,跳过 RawMaterialCard 策略"); return; } @@ -102,33 +100,4 @@ public class RawMaterialCardRemainQtyCapacityContribution implements WarehouseAr private static String normId(String id) { return StringUtils.trimToEmpty(id); } - - private static Set normalizeIdSet(Collection raw) { - if (raw == null) { - return Collections.emptySet(); - } - return raw.stream().filter(StringUtils::isNotBlank).map(String::trim).collect(Collectors.toSet()); - } - - /** 配置的 id + 配置的 code 解析出的 id 合并 */ - private Set resolveAllowedWarehouseCategoryIds() { - Set set = new HashSet<>(normalizeIdSet(properties.getRawMaterialWarehouseCategoryIds())); - Collection codes = properties.getRawMaterialWarehouseCategoryCodes(); - if (codes != null) { - for (String c : codes) { - if (StringUtils.isBlank(c)) { - continue; - } - try { - String id = sysCategoryService.queryIdByCode(c.trim()); - if (StringUtils.isNotBlank(id)) { - set.add(id.trim()); - } - } catch (Exception ex) { - log.warn("[WarehouseAreaActualCapacity] 解析原材料库分类编码失败 code={}: {}", c, ex.getMessage()); - } - } - } - return set; - } } diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/config/XslMesWarehouseAreaCapacityProperties.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/config/XslMesWarehouseAreaCapacityProperties.java index d5c3715..6ef3f6b 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/config/XslMesWarehouseAreaCapacityProperties.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/config/XslMesWarehouseAreaCapacityProperties.java @@ -1,43 +1,45 @@ -package org.jeecg.modules.xslmes.config; - -import lombok.Data; -import org.jeecg.modules.xslmes.constant.MesXslWarehouseCategory; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; - -/** - * 库区列表「实际存放量」展示口径配置(不写库) - *

- * 推荐使用 {@link #rawMaterialWarehouseCategoryCodes},启动时解析为 sys_category.id, - * 避免在配置里维护易变的雪花 id;也可用 {@link #rawMaterialWarehouseCategoryIds} 直接指定 id。 - */ -@Data -@Component -@ConfigurationProperties(prefix = "xslmes.warehouse-area.display-actual-capacity") -public class XslMesWarehouseAreaCapacityProperties { - - /** - * 是否启用展示层回填「实际存放量」 - */ - private boolean enabled = true; - - /** - * 原材料库分类编码列表(resolve 成 id 后与 warehouse_category 匹配)。 - * 默认兼容 {@link MesXslWarehouseCategory#RAW_MATERIAL_CATEGORY_CODES}。 - */ - private List rawMaterialWarehouseCategoryCodes = defaultRawCodes(); - - /** - * 直接指定 warehouse_category=sys_category.id(与 codes 解析结果合并)。 - * 可为空。 - */ - private List rawMaterialWarehouseCategoryIds = new ArrayList<>(); - - private static List defaultRawCodes() { - return new ArrayList<>(MesXslWarehouseCategory.RAW_MATERIAL_CATEGORY_CODES); - } -} +package org.jeecg.modules.xslmes.config; + +import lombok.Data; +import org.jeecg.modules.xslmes.constant.MesXslWarehouseCategory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 库区列表「实际存放量」展示兜底配置(不写库) + *

+ * 当某租户在 {@code mes_xsl_warehouse_area_capacity_cfg} 中尚无已保存记录时, + * 启用状态与分类 codes/ids 仍从本配置读取;一旦有库内记录,则仅以页面「匹配仓库」配置为准。 + *

+ * 推荐使用 codes 配置,由运行时解析为 sys_category.id;亦可直接配置 ids。 + */ +@Data +@Component +@ConfigurationProperties(prefix = "xslmes.warehouse-area.display-actual-capacity") +public class XslMesWarehouseAreaCapacityProperties { + + /** + * 是否启用展示层回填「实际存放量」 + */ + private boolean enabled = true; + + /** + * 原材料库分类编码列表(resolve 成 id 后与 warehouse_category 匹配)。 + * 默认兼容 {@link MesXslWarehouseCategory#RAW_MATERIAL_CATEGORY_CODES}。 + */ + private List rawMaterialWarehouseCategoryCodes = defaultRawCodes(); + + /** + * 直接指定 warehouse_category=sys_category.id(与 codes 解析结果合并)。 + * 可为空。 + */ + private List rawMaterialWarehouseCategoryIds = new ArrayList<>(); + + private static List defaultRawCodes() { + return new ArrayList<>(MesXslWarehouseCategory.RAW_MATERIAL_CATEGORY_CODES); + } +} \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslWarehouseAreaController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslWarehouseAreaController.java index 933337f..2ff8f94 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslWarehouseAreaController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslWarehouseAreaController.java @@ -19,7 +19,9 @@ import org.jeecg.common.util.oConvertUtils; import org.jeecg.config.JeecgBaseConfig; import org.jeecg.modules.xslmes.entity.MesXslWarehouseArea; import org.jeecg.modules.xslmes.service.IMesXslWarehouseAreaService; +import org.jeecg.modules.xslmes.service.IMesXslWarehouseAreaCapacityCfgService; import org.jeecg.modules.xslmes.service.MesXslStompNotifyService; +import org.jeecg.modules.xslmes.vo.MesXslWarehouseAreaCapacityMatchVO; import org.jeecgframework.poi.excel.def.NormalExcelConstants; import org.jeecgframework.poi.excel.entity.ExportParams; import org.jeecgframework.poi.excel.entity.enmus.ExcelType; @@ -50,6 +52,9 @@ public class MesXslWarehouseAreaController extends JeecgController> queryPageList( @@ -182,6 +187,22 @@ public class MesXslWarehouseAreaController extends JeecgController capacityMatchConfig() { + return Result.OK(mesXslWarehouseAreaCapacityCfgService.getCapacityMatchForEdit()); + } + + @AutoLog(value = "MES库区管理-保存匹配仓库配置") + @Operation(summary = "MES库区管理-保存匹配仓库配置") + @RequiresPermissions("xslmes:mes_xsl_warehouse_area:capacityMatch") + @PostMapping(value = "/capacityMatchConfig") + public Result saveCapacityMatchConfig(@RequestBody MesXslWarehouseAreaCapacityMatchVO vo) { + mesXslWarehouseAreaCapacityCfgService.saveCapacityMatch(vo); + return Result.OK("保存成功"); + } + @RequiresPermissions("xslmes:mes_xsl_warehouse_area:exportXls") @RequestMapping(value = "/exportXls") public ModelAndView exportXls(HttpServletRequest request, MesXslWarehouseArea mesXslWarehouseArea) { diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslWarehouseAreaCapacityCfg.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslWarehouseAreaCapacityCfg.java new file mode 100644 index 0000000..46daf56 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslWarehouseAreaCapacityCfg.java @@ -0,0 +1,38 @@ +package org.jeecg.modules.xslmes.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.io.Serializable; + +/** + * 库区「实际存放量」展示回填规则(按租户;匹配哪些仓库分类走原材料卡片汇总) + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("mes_xsl_warehouse_area_capacity_cfg") +@Schema(description = "MES库区实际存放量回填配置") +public class MesXslWarehouseAreaCapacityCfg extends JeecgEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "是否启用回填 0否 1是") + private Integer enabled; + + @Schema(description = "仓库分类 sys_category.id,逗号分隔") + private String warehouseCategoryIds; + + @Schema(description = "仓库分类编码,逗号分隔") + private String warehouseCategoryCodes; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "租户ID") + private Integer tenantId; +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslWarehouseAreaCapacityCfgMapper.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslWarehouseAreaCapacityCfgMapper.java new file mode 100644 index 0000000..38819c0 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslWarehouseAreaCapacityCfgMapper.java @@ -0,0 +1,9 @@ +package org.jeecg.modules.xslmes.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.modules.xslmes.entity.MesXslWarehouseAreaCapacityCfg; + +/** + * MES 库区实际存放量回填规则 + */ +public interface MesXslWarehouseAreaCapacityCfgMapper extends BaseMapper {} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslWarehouseAreaCapacityCfgService.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslWarehouseAreaCapacityCfgService.java new file mode 100644 index 0000000..767a492 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslWarehouseAreaCapacityCfgService.java @@ -0,0 +1,28 @@ +package org.jeecg.modules.xslmes.service; + +import org.jeecg.modules.xslmes.vo.MesXslWarehouseAreaCapacityMatchVO; + +import java.util.Set; + +/** + * 库区「实际存放量」回填:页面配置存储 + 运行时解析 + */ +public interface IMesXslWarehouseAreaCapacityCfgService { + + /** + * 弹窗回显:库内有配置读库;否则回显 YAML / 默认编码(configSource 区分) + */ + MesXslWarehouseAreaCapacityMatchVO getCapacityMatchForEdit(); + + /** 保存或更新当前租户配置 */ + void saveCapacityMatch(MesXslWarehouseAreaCapacityMatchVO vo); + + /** 是否启用回填(库内 saved=0 则关闭,不回落 YAML) */ + boolean isActualCapacityBackfillEnabled(); + + /** + * 参与「原材料卡片 remaining 汇总」的仓库分类 sys_category.id 集合。 + * 无库内记录时等同原 YAML + 编码解析逻辑。 + */ + Set resolveRawMaterialWarehouseCategoryIds(); +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslWarehouseAreaCapacityCfgServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslWarehouseAreaCapacityCfgServiceImpl.java new file mode 100644 index 0000000..fd69d31 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslWarehouseAreaCapacityCfgServiceImpl.java @@ -0,0 +1,225 @@ +package org.jeecg.modules.xslmes.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.config.TenantContext; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.common.util.TokenUtils; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.modules.system.service.ISysCategoryService; +import org.jeecg.modules.xslmes.config.XslMesWarehouseAreaCapacityProperties; +import org.jeecg.modules.xslmes.entity.MesXslWarehouseAreaCapacityCfg; +import org.jeecg.modules.xslmes.mapper.MesXslWarehouseAreaCapacityCfgMapper; +import org.jeecg.modules.xslmes.service.IMesXslWarehouseAreaCapacityCfgService; +import org.jeecg.modules.xslmes.vo.MesXslWarehouseAreaCapacityMatchVO; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 库区实际存放量回填规则(租户级页面配置) + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class MesXslWarehouseAreaCapacityCfgServiceImpl extends ServiceImpl + implements IMesXslWarehouseAreaCapacityCfgService { + + private final XslMesWarehouseAreaCapacityProperties yamlProperties; + private final ISysCategoryService sysCategoryService; + + /** 当前租户下已保存的配置(无记录则返回 null,走 YAML 兜底) */ + private MesXslWarehouseAreaCapacityCfg findTenantRowStrict() { + int tid = resolveTenantIdBestEffort(); + if (tid <= 0) { + return null; + } + return this.lambdaQuery().eq(MesXslWarehouseAreaCapacityCfg::getTenantId, tid).last("LIMIT 1").one(); + } + + private int resolveTenantIdBestEffort() { + String t = TenantContext.getTenant(); + if (oConvertUtils.isEmpty(t)) { + try { + t = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest()); + } catch (Exception ignored) { + } + } + return oConvertUtils.getInt(t, 0); + } + + /** 页面保存必须用有效租户 */ + private int requireTenantIdForSave() { + int tid = resolveTenantIdBestEffort(); + if (tid <= 0) { + throw new JeecgBootException("未获取到租户信息,无法保存匹配仓库配置"); + } + return tid; + } + + @Override + public MesXslWarehouseAreaCapacityMatchVO getCapacityMatchForEdit() { + MesXslWarehouseAreaCapacityCfg row = findTenantRowStrict(); + MesXslWarehouseAreaCapacityMatchVO vo = new MesXslWarehouseAreaCapacityMatchVO(); + if (row != null) { + vo.setEnabled(Integer.valueOf(1).equals(row.getEnabled())); + vo.setWarehouseCategoryIds(StringUtils.trimToEmpty(row.getWarehouseCategoryIds())); + vo.setWarehouseCategoryCodes(StringUtils.trimToEmpty(row.getWarehouseCategoryCodes())); + vo.setRemark(row.getRemark()); + vo.setConfigSource("db"); + vo.setBootstrapHint(null); + return vo; + } + vo.setEnabled(yamlProperties.isEnabled()); + vo.setWarehouseCategoryIds(""); + List codes = yamlProperties.getRawMaterialWarehouseCategoryCodes(); + if (codes != null && !codes.isEmpty()) { + vo.setWarehouseCategoryCodes(codes.stream().filter(StringUtils::isNotBlank).map(String::trim).collect(Collectors.joining(","))); + } else { + vo.setWarehouseCategoryCodes(""); + } + vo.setRemark(null); + vo.setBootstrapHint("尚未保存页面配置:当前展示为服务端 YAML 兜底,保存后以库内配置为准。"); + vo.setConfigSource("yaml"); + return vo; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveCapacityMatch(MesXslWarehouseAreaCapacityMatchVO vo) { + if (vo == null) { + return; + } + boolean en = Boolean.TRUE.equals(vo.getEnabled()); + String ids = normalizeCsv(vo.getWarehouseCategoryIds()); + String codes = normalizeCsv(vo.getWarehouseCategoryCodes()); + String remark = StringUtils.trimToNull(vo.getRemark()); + + int tenantId = requireTenantIdForSave(); + MesXslWarehouseAreaCapacityCfg row = findTenantRowStrict(); + LoginUser user = SecurityUtils.getSubject() != null && SecurityUtils.getSubject().getPrincipal() instanceof LoginUser + ? (LoginUser) SecurityUtils.getSubject().getPrincipal() + : null; + String username = user != null ? user.getUsername() : null; + java.util.Date now = new java.util.Date(); + + if (row == null) { + row = new MesXslWarehouseAreaCapacityCfg(); + row.setTenantId(tenantId); + row.setEnabled(en ? 1 : 0); + row.setWarehouseCategoryIds(StringUtils.isBlank(ids) ? null : ids); + row.setWarehouseCategoryCodes(StringUtils.isBlank(codes) ? null : codes); + row.setRemark(remark); + row.setCreateBy(username); + row.setCreateTime(now); + row.setUpdateBy(username); + row.setUpdateTime(now); + this.save(row); + } else { + row.setEnabled(en ? 1 : 0); + row.setWarehouseCategoryIds(StringUtils.isBlank(ids) ? null : ids); + row.setWarehouseCategoryCodes(StringUtils.isBlank(codes) ? null : codes); + row.setRemark(remark); + row.setUpdateBy(username); + row.setUpdateTime(now); + this.updateById(row); + } + } + + @Override + public boolean isActualCapacityBackfillEnabled() { + MesXslWarehouseAreaCapacityCfg row = findTenantRowStrict(); + if (row != null) { + return Integer.valueOf(1).equals(row.getEnabled()); + } + return yamlProperties.isEnabled(); + } + + @Override + public Set resolveRawMaterialWarehouseCategoryIds() { + MesXslWarehouseAreaCapacityCfg row = findTenantRowStrict(); + if (row != null) { + if (!Integer.valueOf(1).equals(row.getEnabled())) { + return Collections.emptySet(); + } + return mergeCategoryIds(row.getWarehouseCategoryIds(), row.getWarehouseCategoryCodes()); + } + if (!yamlProperties.isEnabled()) { + return Collections.emptySet(); + } + return mergeYamlCategoryIds(); + } + + /** 逗号拆分并 trim */ + private static List splitComma(String csv) { + if (StringUtils.isBlank(csv)) { + return Collections.emptyList(); + } + return Arrays.stream(csv.split(",")).map(String::trim).filter(StringUtils::isNotBlank).collect(Collectors.toList()); + } + + private static String normalizeCsv(String raw) { + if (StringUtils.isBlank(raw)) { + return ""; + } + return splitComma(raw).stream().distinct().collect(Collectors.joining(",")); + } + + private Set mergeCategoryIds(String idsCsv, String codesCsv) { + Set set = new HashSet<>(); + for (String id : splitComma(idsCsv)) { + set.add(id); + } + for (String code : splitComma(codesCsv)) { + resolveCode(code).ifPresent(set::add); + } + return set; + } + + private Optional resolveCode(String code) { + try { + String id = sysCategoryService.queryIdByCode(code); + if (StringUtils.isNotBlank(id)) { + return Optional.of(id.trim()); + } + } catch (Exception ex) { + log.warn("[WarehouseAreaCapacityCfg] 分类编码解析失败 code={}: {}", code, ex.getMessage()); + } + return Optional.empty(); + } + + private Set mergeYamlCategoryIds() { + Set set = new HashSet<>(); + Collection yamlIds = yamlProperties.getRawMaterialWarehouseCategoryIds(); + if (yamlIds != null) { + for (String id : yamlIds) { + if (StringUtils.isNotBlank(id)) { + set.add(id.trim()); + } + } + } + Collection yamlCodes = yamlProperties.getRawMaterialWarehouseCategoryCodes(); + if (yamlCodes != null) { + for (String c : yamlCodes) { + if (StringUtils.isBlank(c)) { + continue; + } + resolveCode(c.trim()).ifPresent(set::add); + } + } + return set; + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslWarehouseAreaCapacityMatchVO.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslWarehouseAreaCapacityMatchVO.java new file mode 100644 index 0000000..36a9211 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslWarehouseAreaCapacityMatchVO.java @@ -0,0 +1,35 @@ +package org.jeecg.modules.xslmes.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** + * 库区管理「匹配仓库」配置(读写) + */ +@Data +@Schema(description = "库区实际存放量回填-匹配仓库配置") +public class MesXslWarehouseAreaCapacityMatchVO implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "是否启用回填") + private Boolean enabled; + + /** 与前端 JCategorySelect 多选一致:sys_category.id 逗号分隔 */ + @Schema(description = "MES仓库分类ID,逗号分隔") + private String warehouseCategoryIds; + + @Schema(description = "MES仓库分类编码,逗号分隔(可选补充,与 YAML 兜底逻辑一致)") + private String warehouseCategoryCodes; + + @Schema(description = "配置来源 db=库内已保存 yaml=暂无库内配置使用文件兜底 preview") + private String configSource; + + @Schema(description = "首次打开且无库内配置时给出的提示(不落库)") + private String bootstrapHint; + + @Schema(description = "备注(落库)") + private String remark; +} diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/config/application-xslmes-warehouse-area.yml b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/config/application-xslmes-warehouse-area.yml index cfc9656..4c74f4e 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/config/application-xslmes-warehouse-area.yml +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/config/application-xslmes-warehouse-area.yml @@ -1,11 +1,11 @@ -# MES XSL — 库区「实际存放量」展示回填(不写库,可覆盖) +# MES XSL — 库区「实际存放量」兜底配置(不写库的可选 YAML) +# 当前租户若在表 mes_xsl_warehouse_area_capacity_cfg 中已保存「匹配仓库」配置,则仅以库内配置为准, +# 本文件的 codes/ids/enabled 仅在该租户尚无库内记录时作为兜底生效。 xslmes: warehouse-area: display-actual-capacity: enabled: true - # 按「原材料库」分类编码解析 sys_category.id(推荐;业务树已无楼层语义,编码中 F1/F2 后缀若变化请改此处) raw-material-warehouse-category-codes: - XSLMES_WH_F1_YCL - XSLMES_WH_F2_YCL - # 可选:直接写 warehouse_category=id,与上面的编码解析结果合并 raw-material-warehouse-category-ids: [] diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_57__mes_xsl_warehouse_area_capacity_cfg.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_57__mes_xsl_warehouse_area_capacity_cfg.sql new file mode 100644 index 0000000..3fcf1a3 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_57__mes_xsl_warehouse_area_capacity_cfg.sql @@ -0,0 +1,26 @@ +-- 库区「实际存放量」回填规则:按租户页面配置(原材料卡片汇总所匹配的仓库分类) + +CREATE TABLE IF NOT EXISTS `mes_xsl_warehouse_area_capacity_cfg` ( + `id` varchar(32) NOT NULL COMMENT '主键', + `enabled` tinyint NOT NULL DEFAULT 1 COMMENT '是否启用回填 0否 1是', + `warehouse_category_ids` varchar(2000) DEFAULT NULL COMMENT 'MES仓库分类(sys_category.id),逗号分隔', + `warehouse_category_codes` varchar(2000) DEFAULT NULL COMMENT 'MES仓库分类编码,逗号分隔,与 IDs 合并解析', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `create_by` varchar(50) DEFAULT NULL COMMENT '创建人', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(50) DEFAULT NULL COMMENT '更新人', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `tenant_id` int DEFAULT NULL COMMENT '租户ID', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_xsl_wacc_tenant` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES库区实际存放量回填规则(租户)'; + +-- 按钮权限:匹配仓库 +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) +SELECT '1900000000000000558', '1900000000000000550', '匹配仓库', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mes_xsl_warehouse_area:capacityMatch', '1', 8.00, 0, NULL, 1, 0, 0, 0, '配置实际存放量按原材料卡片汇总的仓库分类', 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000558'); + +INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`) +SELECT REPLACE(UUID(), '-', ''), r.id, '1900000000000000558', NULL, NOW(), '127.0.0.1' +FROM `sys_role` r WHERE r.`role_code` = 'admin' + AND NOT EXISTS (SELECT 1 FROM `sys_role_permission` rp WHERE rp.`role_id` = r.id AND rp.`permission_id` = '1900000000000000558'); diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/MesXslWarehouseArea.api.ts b/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/MesXslWarehouseArea.api.ts index bf94ffa..70a3ab3 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/MesXslWarehouseArea.api.ts +++ b/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/MesXslWarehouseArea.api.ts @@ -15,6 +15,7 @@ enum Api { importExcel = '/xslmes/mesXslWarehouseArea/importExcel', exportXls = '/xslmes/mesXslWarehouseArea/exportXls', batchAdd = '/xslmes/mesXslWarehouseArea/batchAdd', + capacityMatchConfig = '/xslmes/mesXslWarehouseArea/capacityMatchConfig', } export const getExportUrl = Api.exportXls; @@ -60,6 +61,12 @@ export const saveOrUpdate = (params, isUpdate) => { return defHttp.post({ url, params }); }; +/** 实际存放量:匹配仓库配置 */ +export const getCapacityMatchConfig = () => defHttp.get({ url: Api.capacityMatchConfig }); + +export const saveCapacityMatchConfig = (params: Record) => + defHttp.post({ url: Api.capacityMatchConfig, params }); + /** 批量添加库区(同一仓库下一次性创建多条) */ export const batchAddAreas = (params: any[]) => defHttp.post({ url: Api.batchAdd, params }); diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/MesXslWarehouseAreaList.vue b/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/MesXslWarehouseAreaList.vue index 4712a8a..3518f65 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/MesXslWarehouseAreaList.vue +++ b/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/MesXslWarehouseAreaList.vue @@ -2,6 +2,9 @@

+
@@ -35,12 +39,14 @@ import { useModal } from '/@/components/Modal'; import { useListPage } from '/@/hooks/system/useListPage'; import MesXslWarehouseAreaModal from './components/MesXslWarehouseAreaModal.vue'; + import MesXslWarehouseAreaCapacityMatchModal from './components/MesXslWarehouseAreaCapacityMatchModal.vue'; import { columns, searchFormSchema, superQuerySchema } from './MesXslWarehouseArea.data'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, updateStatus } from './MesXslWarehouseArea.api'; import Icon from '/@/components/Icon'; import type { Recordable } from '/@/types/global'; const [registerModal, { openModal }] = useModal(); + const [registerCapacityMatchModal, { openModal: openCapacityMatchModal }] = useModal(); const { tableContext, onExportXls, onImportXls } = useListPage({ tableProps: { @@ -71,6 +77,10 @@ const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext; const superQueryConfig = reactive(superQuerySchema); + function openCapacityMatch() { + openCapacityMatchModal(true, {}); + } + function handleAdd() { openModal(true, { isUpdate: false, showFooter: true, record: {} }); } diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/components/MesXslWarehouseAreaCapacityMatchModal.vue b/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/components/MesXslWarehouseAreaCapacityMatchModal.vue new file mode 100644 index 0000000..a65f1b2 --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmes/mesXslWarehouseArea/components/MesXslWarehouseAreaCapacityMatchModal.vue @@ -0,0 +1,109 @@ + + +