16 小时以前 b2da163832b4e665645d39a5ec3ece7209d75143
Merge branch 'dev_New_pro' into dev_克拉玛依市新聚工贸_pro
已添加1个文件
已修改3个文件
412 ■■■■■ 文件已修改
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java 80 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-lt.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -31,6 +31,7 @@
import com.ruoyi.technology.pojo.TechnologyOperationParam;
import com.ruoyi.technology.pojo.TechnologyParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -49,6 +50,7 @@
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-04-21 03:55:52
 */
@Slf4j
@Service
@RequiredArgsConstructor()
public class ProductionBomStructureServiceImpl extends ServiceImpl<ProductionBomStructureMapper, ProductionBomStructure> implements ProductionBomStructureService {
@@ -137,6 +139,9 @@
            entity.setProductionOrderBomId(orderBomId);
            if (item.getId() == null) {
                entity.setParentId(null);
                // æ–°å¢žèŠ‚ç‚¹çš„éœ€æ±‚é‡ç”±åŽç«¯æŒ‰BOM树形结构重新计算,不清空可能导致前端传的值溢出
                entity.setDemandedQuantity(null);
                entity.setId(null);
                insertList.add(entity);
                tempEntityMap.put(item.getTempId(), entity);
            } else {
@@ -146,6 +151,11 @@
        // æ‰¹é‡æ–°å¢žï¼Œæ‹¿åˆ°æ•°æ®åº“生成的真实ID
        if (!insertList.isEmpty()) {
            for (ProductionBomStructure entity : insertList) {
                log.info("准备新增BOM结构节点: productModelId={}, technologyOperationId={}, unitQuantity={}, demandedQuantity={}, unit={}",
                        entity.getProductModelId(), entity.getTechnologyOperationId(),
                        entity.getUnitQuantity(), entity.getDemandedQuantity(), entity.getUnit());
            }
            this.saveBatch(insertList);
        }
@@ -216,24 +226,76 @@
        if (structureList == null || structureList.isEmpty()) {
            return;
        }
        List<ProductionBomStructure> updateList = new ArrayList<>();
        BigDecimal lastProcessDemandedQuantity = orderQuantity;
        log.info("开始按BOM树形结构计算需求量,订单数量={}, BOM节点总数={}", orderQuantity, structureList.size());
        // æž„建 id â†’ structure æ˜ å°„å’Œ parentId â†’ children æ˜ å°„
        Map<Long, ProductionBomStructure> structureById = new HashMap<>();
        Map<Long, List<ProductionBomStructure>> childrenMap = new LinkedHashMap<>();
        for (ProductionBomStructure structure : structureList) {
            if (structure == null || structure.getId() == null) {
                continue;
            }
            structureById.put(structure.getId(), structure);
            Long parentId = structure.getParentId();
            childrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(structure);
        }
            BigDecimal demandedQuantity = lastProcessDemandedQuantity.multiply(defaultDecimal(structure.getUnitQuantity()));
//            if (compareDecimal(structure.getDemandedQuantity(), demandedQuantity) == 0) {
//                continue;
//            }
        List<ProductionBomStructure> updateList = new ArrayList<>();
        // ä»Žæ ¹èŠ‚ç‚¹å¼€å§‹ï¼ŒæŒ‰æ ‘å½¢å±‚çº§é€å±‚è®¡ç®—éœ€æ±‚é‡
        // æ ¹èŠ‚ç‚¹ parentId ä¸º null æˆ– 0
        Deque<Long> queue = new ArrayDeque<>();
        List<ProductionBomStructure> rootNodes = childrenMap.get(null);
        if (rootNodes != null) {
            for (ProductionBomStructure root : rootNodes) {
                queue.add(root.getId());
            }
        }
        // parentId=0 çš„也视为根节点
        List<ProductionBomStructure> zeroParentNodes = childrenMap.get(0L);
        if (zeroParentNodes != null) {
            for (ProductionBomStructure root : zeroParentNodes) {
                if (!queue.contains(root.getId())) {
                    queue.add(root.getId());
                }
            }
        }
        while (!queue.isEmpty()) {
            Long nodeId = queue.poll();
            ProductionBomStructure node = structureById.get(nodeId);
            if (node == null) {
                continue;
            }
            // è®¡ç®—当前节点的需求数量 = çˆ¶èŠ‚ç‚¹éœ€æ±‚é‡ Ã— è‡ªèº« unitQuantity
            // æ ¹èŠ‚ç‚¹ï¼šéœ€æ±‚é‡ = è®¢å•数量 Ã— unitQuantity
            BigDecimal parentDemandedQuantity = orderQuantity;
            Long parentId = node.getParentId();
            if (parentId != null && parentId != 0L) {
                ProductionBomStructure parent = structureById.get(parentId);
                if (parent != null && parent.getDemandedQuantity() != null) {
                    parentDemandedQuantity = parent.getDemandedQuantity();
                }
            }
            BigDecimal demandedQuantity = parentDemandedQuantity.multiply(defaultDecimal(node.getUnitQuantity()));
            log.info("BOM需求量计算: nodeId={}, parentId={}, parentDemandedQuantity={}, unitQuantity={}, demandedQuantity={}",
                    node.getId(), node.getParentId(), parentDemandedQuantity,
                    defaultDecimal(node.getUnitQuantity()), demandedQuantity);
            ProductionBomStructure update = new ProductionBomStructure();
            update.setId(structure.getId());
            update.setId(node.getId());
            update.setDemandedQuantity(demandedQuantity);
            updateList.add(update);
            structure.setDemandedQuantity(demandedQuantity);
            lastProcessDemandedQuantity = demandedQuantity;
            node.setDemandedQuantity(demandedQuantity);
            // å°†å­èŠ‚ç‚¹åŠ å…¥é˜Ÿåˆ—
            List<ProductionBomStructure> children = childrenMap.get(nodeId);
            if (children != null) {
                for (ProductionBomStructure child : children) {
                    queue.add(child.getId());
                }
            }
        }
        if (!updateList.isEmpty()) {
            this.updateBatchById(updateList);
        }
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -439,9 +439,54 @@
        productionOrderBomMapper.insert(orderBom);
        Map<Long, Long> idMap = new HashMap<>();
        BigDecimal lastProcessDemandedQuantity = orderQuantity;
        // æž„建 sourceId â†’ sourceNode æ˜ å°„ å’Œ parentId â†’ children æ˜ å°„
        Map<Long, TechnologyBomStructure> sourceById = new HashMap<>();
        Map<Long, List<TechnologyBomStructure>> childrenMap = new LinkedHashMap<>();
        for (TechnologyBomStructure source : structureList) {
            // å­èŠ‚ç‚¹ parentId éœ€è¦æ˜ å°„成新快照节点 id,才能保留原始 BOM å±‚级。
            if (source == null || source.getId() == null) {
                continue;
            }
            sourceById.put(source.getId(), source);
            Long parentId = source.getParentId();
            childrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(source);
        }
        // BFS ä»Žæ ¹èŠ‚ç‚¹å¼€å§‹ï¼Œçˆ¶èŠ‚ç‚¹å…ˆäºŽå­èŠ‚ç‚¹å¤„ç†
        Deque<TechnologyBomStructure> queue = new ArrayDeque<>();
        List<TechnologyBomStructure> rootNodes = childrenMap.get(null);
        if (rootNodes != null) {
            queue.addAll(rootNodes);
        }
        // parentId=0 çš„也视为根节点
        List<TechnologyBomStructure> zeroParentNodes = childrenMap.get(0L);
        if (zeroParentNodes != null) {
            for (TechnologyBomStructure node : zeroParentNodes) {
                if (!queue.contains(node)) {
                    queue.add(node);
                }
            }
        }
        while (!queue.isEmpty()) {
            TechnologyBomStructure source = queue.poll();
            if (source == null || source.getId() == null) {
                continue;
            }
            // è®¡ç®—需求量 = çˆ¶èŠ‚ç‚¹éœ€æ±‚é‡ Ã— è‡ªèº« unitQuantity
            BigDecimal parentDemandedQuantity = orderQuantity;
            Long sourceParentId = source.getParentId();
            if (sourceParentId != null && sourceParentId != 0L) {
                Long parentTargetId = idMap.get(sourceParentId);
                if (parentTargetId != null) {
                    // çˆ¶èŠ‚ç‚¹å·²ç»æ’å…¥ï¼Œç”¨çˆ¶èŠ‚ç‚¹çš„ targetId ä»Žå·²æ’入列表找
                    // è¿™é‡Œæˆ‘们直接用存下来的父节点需求量
                    TechnologyBomStructure parentSource = sourceById.get(sourceParentId);
                    if (parentSource != null && parentSource.getDemandedQuantity() != null) {
                        parentDemandedQuantity = parentSource.getDemandedQuantity();
                    }
                }
            }
            ProductionBomStructure target = new ProductionBomStructure();
            target.setProductionOrderId(productionOrder.getId());
            target.setProductionOrderBomId(orderBom.getId());
@@ -449,11 +494,20 @@
            target.setProductModelId(source.getProductModelId());
            target.setTechnologyOperationId(source.getOperationId());
            target.setUnitQuantity(source.getUnitQuantity());
            target.setDemandedQuantity(lastProcessDemandedQuantity.multiply(source.getUnitQuantity()));
            target.setDemandedQuantity(parentDemandedQuantity.multiply(defaultDecimal(source.getUnitQuantity())));
            target.setUnit(source.getUnit());
            productionBomStructureMapper.insert(target);
            idMap.put(source.getId(), target.getId());
            lastProcessDemandedQuantity = target.getDemandedQuantity();
            // å°†å½“前节点的 demandedQuantity å›žå†™åˆ° source å¯¹è±¡ä¸Šï¼Œä¾›å­èŠ‚ç‚¹è®¡ç®—ä½¿ç”¨
            source.setDemandedQuantity(target.getDemandedQuantity());
            // å°†å­èŠ‚ç‚¹åŠ å…¥é˜Ÿåˆ—
            List<TechnologyBomStructure> children = childrenMap.get(source.getId());
            if (children != null) {
                for (TechnologyBomStructure child : children) {
                    queue.add(child);
                }
            }
        }
        return orderBom;
    }
src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java
@@ -51,7 +51,7 @@
     * @return ç”¨æˆ·ä¿¡æ¯é›†åˆä¿¡æ¯
     */
    @Override
    @DataScope(deptAlias = "d", userAlias = "u")
    @DataScope(userAlias = "u")
    public List<SysUser> selectUserList(SysUser user)
    {
        return userMapper.selectUserList(user);
src/main/resources/application-lt.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,268 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /data/javaWork/product-inventory-management/file
  # èŽ·å–ip地址开关
  addressEnabled: false
  # éªŒè¯ç ç±»åž‹ math æ•°å­—计算 char å­—符验证
  captchaType: math
  # ååŒå®¡æ‰¹ç¼–号前缀(配置文件后缀命名)
  approvalNumberPrefix: NEW
  # ä¸ªæŽ¨ Unipush é…ç½®
  getui:
    appId: PfjyAAE0FK64FaO1w2CMb1
    appKey: zTMb831OEL6J4GK1uE3Ob4
    masterSecret: K1GFtsv42v61tXGnF7SGE5
    domain: https://restapi.getui.cn/v2/
    # ç¦»çº¿æŽ¨é€ä½¿ç”¨çš„包名/组件名
    intentComponent: uni.app.UNI099A590/io.dcloud.PandoraEntry
# å¼€å‘环境配置
server:
  # æœåŠ¡å™¨çš„HTTP端口,默认为8080
  port: 9003
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # è¿žæŽ¥æ•°æ»¡åŽçš„æŽ’队数,默认为100
    accept-count: 1000
    threads:
      # tomcat最大线程数,默认为200
      max: 800
      # Tomcat启动初始化的线程数,默认值10
      min-spare: 100
# æ—¥å¿—配置
logging:
  level:
    com.ruoyi: warn
    org.springframework: warn
minio:
  endpoint: http://114.132.189.42/
  port: 7019
  secure: false
  accessKey: admin
  secretKey: 12345678
  preview-expiry: 24 # é¢„览地址默认24小时
  default-bucket: jxc
# ç”¨æˆ·é…ç½®
user:
  password:
    # å¯†ç æœ€å¤§é”™è¯¯æ¬¡æ•°
    maxRetryCount: 5
    # å¯†ç é”å®šæ—¶é—´ï¼ˆé»˜è®¤10分钟)
    lockTime: 10
# Spring配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # ä¸»åº“数据源
      master:
        url: jdbc:mysql://172.17.0.1:11101/product-inventory-management-lt?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
      slave:
        # ä»Žæ•°æ®æºå¼€å…³/默认关闭
        enabled: false
        url:
        username:
        password:
      # åˆå§‹è¿žæŽ¥æ•°
      initialSize: 5
      # æœ€å°è¿žæŽ¥æ± æ•°é‡
      minIdle: 10
      # æœ€å¤§è¿žæŽ¥æ± æ•°é‡
      maxActive: 20
      # é…ç½®èŽ·å–è¿žæŽ¥ç­‰å¾…è¶…æ—¶çš„æ—¶é—´
      maxWait: 60000
      # é…ç½®è¿žæŽ¥è¶…æ—¶æ—¶é—´
      connectTimeout: 30000
      # é…ç½®ç½‘络超时时间
      socketTimeout: 60000
      # é…ç½®é—´éš”多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # é…ç½®ä¸€ä¸ªè¿žæŽ¥åœ¨æ± ä¸­æœ€å°ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•位是毫秒
      minEvictableIdleTimeMillis: 300000
      # é…ç½®ä¸€ä¸ªè¿žæŽ¥åœ¨æ± ä¸­æœ€å¤§ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # é…ç½®æ£€æµ‹è¿žæŽ¥æ˜¯å¦æœ‰æ•ˆ
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      webStatFilter:
        enabled: true
      statViewServlet:
        enabled: true
        # è®¾ç½®ç™½åå•,不填则允许所有访问
        allow:
        url-pattern: /druid/*
        # æŽ§åˆ¶å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç 
        login-username: ruoyi
        login-password: 123456
      filter:
        stat:
          enabled: true
          # æ…¢SQL记录
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true
  # èµ„源信息
  messages:
    # å›½é™…化资源文件路径
    basename: i18n/messages
  # æ–‡ä»¶ä¸Šä¼ 
  servlet:
    multipart:
      # å•个文件大小
      max-file-size: 1GB
      # è®¾ç½®æ€»ä¸Šä¼ çš„æ–‡ä»¶å¤§å°
      max-request-size: 2GB
  # æœåŠ¡æ¨¡å—
  devtools:
    restart:
      # çƒ­éƒ¨ç½²å¼€å…³
      enabled: false
  # redis é…ç½®
  data:
    mongodb:
      uri: mongodb://114.132.189.42:9028/chat_memory_db_lt
    # redis é…ç½®
    redis:
      # åœ°å€
#      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 11102
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      timeout: 10s
      lettuce:
        pool:
          # è¿žæŽ¥æ± ä¸­çš„æœ€å°ç©ºé—²è¿žæŽ¥
          min-idle: 0
          # è¿žæŽ¥æ± ä¸­çš„æœ€å¤§ç©ºé—²è¿žæŽ¥
          max-idle: 8
          # è¿žæŽ¥æ± çš„æœ€å¤§æ•°æ®åº“连接数
          max-active: 8
          # #连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
  # Quartz定时任务配置(新增部分)
  quartz:
    job-store-type: jdbc  # ä½¿ç”¨æ•°æ®åº“存储
    jdbc:
      initialize-schema: never  # é¦–次运行时自动创建表结构,成功后改为never
      schema: classpath:org/quartz/impl/jdbcjobstore/tables_mysql_innodb.sql  # MySQL表结构脚本
    properties:
      org:
        quartz:
          scheduler:
            instanceName: RuoYiScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate  # MySQL适配
            tablePrefix: qrtz_  # è¡¨åå‰ç¼€ï¼Œä¸Žè„šæœ¬ä¸€è‡´
            isClustered: false  # å•节点模式(集群需改为true)
            clusterCheckinInterval: 10000
            txIsolationLevelSerializable: true
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10  # çº¿ç¨‹æ± å¤§å°
            threadPriority: 5
            makeThreadsDaemons: true
          updateCheck: false  # å…³é—­ç‰ˆæœ¬æ£€æŸ¥
# token配置
token:
  # ä»¤ç‰Œè‡ªå®šä¹‰æ ‡è¯†
  header: Authorization
  # ä»¤ç‰Œå¯†é’¥
  secret: xpAVjhCjQDaDB7mjPAzMDSbQWXNu2zYkTdDNUsPMS5Xx8QMmQVYN7n74eZrYJxDJ
  # ä»¤ç‰Œæœ‰æ•ˆæœŸï¼ˆé»˜è®¤30分钟)
  expireTime: 450
# MyBatis Plus配置
mybatis-plus:
  # æœç´¢æŒ‡å®šåŒ…别名   æ ¹æ®è‡ªå·±çš„项目来
  typeAliasesPackage: com.ruoyi.**.pojo
  # é…ç½®mapper的扫描,找到所有的mapper.xml映射文件
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # åŠ è½½å…¨å±€çš„é…ç½®æ–‡ä»¶
  configLocation: classpath:mybatis/mybatis-config.xml
  global-config:
    enable-sql-runner: true
    db-config:
      id-type: auto
# PageHelper分页插件
pagehelper:
  helperDialect: mysql
  supportMethodsArguments: true
  params: count=countSql
# Swagger配置
swagger:
  # æ˜¯å¦å¼€å¯swagger
  enabled: true
  # è¯·æ±‚前缀
  pathMapping: /dev-api
# é˜²æ­¢XSS攻击
xss:
  # è¿‡æ»¤å¼€å…³
  enabled: true
  # æŽ’除链接(多个用逗号分隔)
  excludes: /system/notice
  # åŒ¹é…é“¾æŽ¥
  urlPatterns: /system/*,/monitor/*,/tool/*
# ä»£ç ç”Ÿæˆ
gen:
  # ä½œè€…
  author: ruoyi
  # é»˜è®¤ç”ŸæˆåŒ…路径 system éœ€æ”¹æˆè‡ªå·±çš„æ¨¡å—名称 å¦‚ system monitor tool
  packageName: com.ruoyi.project.system
  # è‡ªåŠ¨åŽ»é™¤è¡¨å‰ç¼€ï¼Œé»˜è®¤æ˜¯true
  autoRemovePre: false
  # è¡¨å‰ç¼€ï¼ˆç”Ÿæˆç±»åä¸ä¼šåŒ…含表前缀,多个用逗号分隔)
  tablePrefix: sys_
  # æ˜¯å¦å…è®¸ç”Ÿæˆæ–‡ä»¶è¦†ç›–到本地(自定义路径),默认不允许
  allowOverwrite: false
# æ–‡ä»¶ä¸Šä¼ é…ç½®
file:
  temp-dir: /data/javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /data/javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /data/javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.213.180.217:9200 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)