1c518e10a50050d383e714b581c94dea58ec4d67..10828930ddb83578e56f96c6a489d35d740075e1
2026-05-25 liyong
fix(notice): 修复企业新闻通知路径匹配逻辑
108289 对比 | 目录
2026-05-25 liyong
更新配置文件端口9000
fe473b 对比 | 目录
2026-05-25 liyong
添加oa配置文件
447428 对比 | 目录
已添加1个文件
已修改1个文件
522 ■■■■ 文件已修改
src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java 254 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-oa.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
@@ -1,14 +1,14 @@
package com.ruoyi.project.system.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper;
import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysNotice;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper;
import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysNotice;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.domain.SysUserDept;
import com.ruoyi.project.system.mapper.SysDeptMapper;
@@ -17,15 +17,15 @@
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.project.system.service.ISysNoticeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * å…¬å‘Š æœåŠ¡å±‚å®žçŽ°
@@ -34,16 +34,16 @@
 */
@Service
@RequiredArgsConstructor
public class SysNoticeServiceImpl  extends ServiceImpl<SysNoticeMapper, SysNotice> implements ISysNoticeService {
    private static final Pattern ENTERPRISE_NEWS_ID_PATTERN = Pattern.compile("[?&]id=(\\d+)");
    private final SysNoticeMapper noticeMapper;
    private final SysUserMapper userMapper;
    private final SysDeptMapper deptMapper;
    private final SysUserDeptMapper userDeptMapper;
    private final UnipushService unipushService;
    private final EnterpriseNewsMapper enterpriseNewsMapper;
public class SysNoticeServiceImpl  extends ServiceImpl<SysNoticeMapper, SysNotice> implements ISysNoticeService {
    private static final Pattern ENTERPRISE_NEWS_ID_PATTERN = Pattern.compile("[?&]id=(\\d+)");
    private final SysNoticeMapper noticeMapper;
    private final SysUserMapper userMapper;
    private final SysDeptMapper deptMapper;
    private final SysUserDeptMapper userDeptMapper;
    private final UnipushService unipushService;
    private final EnterpriseNewsMapper enterpriseNewsMapper;
    /**
     * æŸ¥è¯¢å…¬å‘Šä¿¡æ¯
@@ -86,26 +86,26 @@
     * @param notice å…¬å‘Šä¿¡æ¯
     * @return ç»“æžœ
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateNotice(SysNotice notice)
    {
        if (notice == null || notice.getNoticeId() == null) {
            return 0;
        }
        SysNotice dbNotice = noticeMapper.selectNoticeById(notice.getNoticeId());
        if (dbNotice == null) {
            return 0;
        }
        boolean needSyncNewsReadCount = isEnterpriseNewsNotice(dbNotice)
                && notice.getStatus() != null
                && !notice.getStatus().equals(dbNotice.getStatus());
        int rows = noticeMapper.updateNotice(notice);
        if (rows > 0 && needSyncNewsReadCount) {
            syncEnterpriseNewsReadCount(dbNotice.getJumpPath());
        }
        return rows;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateNotice(SysNotice notice)
    {
        if (notice == null || notice.getNoticeId() == null) {
            return 0;
        }
        SysNotice dbNotice = noticeMapper.selectNoticeById(notice.getNoticeId());
        if (dbNotice == null) {
            return 0;
        }
        boolean needSyncNewsReadCount = isEnterpriseNewsNotice(dbNotice)
                && notice.getStatus() != null
                && !notice.getStatus().equals(dbNotice.getStatus());
        int rows = noticeMapper.updateNotice(notice);
        if (rows > 0 && needSyncNewsReadCount) {
            syncEnterpriseNewsReadCount(dbNotice.getJumpPath());
        }
        return rows;
    }
    /**
     * åˆ é™¤å…¬å‘Šå¯¹è±¡
@@ -139,20 +139,20 @@
    }
    @Override
    public int readAll() {
        Long userId = SecurityUtils.getUserId();
        List<SysNotice> unreadNotices = noticeMapper.selectList(Wrappers.<SysNotice>lambdaQuery()
                .eq(SysNotice::getConsigneeId, userId)
                .eq(SysNotice::getStatus, "0"));
        int rows = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
                .eq(SysNotice::getConsigneeId, userId)
                .eq(SysNotice::getStatus, "0")
                .set(SysNotice::getStatus, "1"));
        if (rows > 0) {
            syncEnterpriseNewsReadCount(unreadNotices);
        }
        return rows;
    }
    public int readAll() {
        Long userId = SecurityUtils.getUserId();
        List<SysNotice> unreadNotices = noticeMapper.selectList(Wrappers.<SysNotice>lambdaQuery()
                .eq(SysNotice::getConsigneeId, userId)
                .eq(SysNotice::getStatus, "0"));
        int rows = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
                .eq(SysNotice::getConsigneeId, userId)
                .eq(SysNotice::getStatus, "0")
                .set(SysNotice::getStatus, "1"));
        if (rows > 0) {
            syncEnterpriseNewsReadCount(unreadNotices);
        }
        return rows;
    }
    @Override
    public void simpleNoticeByUser(String title, String message, List<Long> consigneeId, String jumpPath) {
@@ -242,72 +242,72 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean appReadNotice(Long noticeId) {
        if (noticeId == null) {
            return false;
    public boolean appReadNotice(Long noticeId) {
        if (noticeId == null) {
            return false;
        }
        SysNotice sysNotice = noticeMapper.selectNoticeById(noticeId);
        if (sysNotice == null) {
            return false;
        }
        sysNotice.setStatus("1");
        boolean updated = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
                .eq(SysNotice::getNoticeId, noticeId)
                .eq(SysNotice::getStatus, "0")
                .set(SysNotice::getStatus, "1")) > 0;
        if (updated) {
            syncEnterpriseNewsReadCount(sysNotice.getJumpPath());
        }
        return updated;
    }
    private boolean isEnterpriseNewsNotice(SysNotice sysNotice) {
        return sysNotice != null
                && sysNotice.getJumpPath() != null
                && sysNotice.getJumpPath().startsWith("/enterpriseNews?id=");
    }
    private void syncEnterpriseNewsReadCount(List<SysNotice> notices) {
        if (notices == null || notices.isEmpty()) {
            return;
        }
        Set<String> jumpPaths = new HashSet<>();
        for (SysNotice notice : notices) {
            if (isEnterpriseNewsNotice(notice)) {
                jumpPaths.add(notice.getJumpPath());
            }
        }
        for (String jumpPath : jumpPaths) {
            syncEnterpriseNewsReadCount(jumpPath);
        }
    }
    private void syncEnterpriseNewsReadCount(String jumpPath) {
        Long newsId = parseEnterpriseNewsId(jumpPath);
        if (newsId == null) {
            return;
        }
        long readCount = noticeMapper.selectCount(Wrappers.<SysNotice>lambdaQuery()
                .eq(SysNotice::getStatus, "1")
                .eq(SysNotice::getJumpPath, jumpPath));
        EnterpriseNews enterpriseNews = new EnterpriseNews();
        enterpriseNews.setId(newsId);
        enterpriseNews.setReadCount((int) readCount);
        enterpriseNewsMapper.updateById(enterpriseNews);
    }
    private Long parseEnterpriseNewsId(String jumpPath) {
        if (jumpPath == null || !jumpPath.startsWith("/enterpriseNews")) {
            return null;
        }
        Matcher matcher = ENTERPRISE_NEWS_ID_PATTERN.matcher(jumpPath);
        if (!matcher.find()) {
            return null;
        }
        try {
            return Long.parseLong(matcher.group(1));
        } catch (NumberFormatException e) {
            return null;
        }
    }
}
        }
        sysNotice.setStatus("1");
        boolean updated = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
                .eq(SysNotice::getNoticeId, noticeId)
                .eq(SysNotice::getStatus, "0")
                .set(SysNotice::getStatus, "1")) > 0;
        if (updated) {
            syncEnterpriseNewsReadCount(sysNotice.getJumpPath());
        }
        return updated;
    }
    private boolean isEnterpriseNewsNotice(SysNotice sysNotice) {
        return sysNotice != null
                && sysNotice.getJumpPath() != null
                && sysNotice.getJumpPath().contains("/enterpriseNews?id=");
    }
    private void syncEnterpriseNewsReadCount(List<SysNotice> notices) {
        if (notices == null || notices.isEmpty()) {
            return;
        }
        Set<String> jumpPaths = new HashSet<>();
        for (SysNotice notice : notices) {
            if (isEnterpriseNewsNotice(notice)) {
                jumpPaths.add(notice.getJumpPath());
            }
        }
        for (String jumpPath : jumpPaths) {
            syncEnterpriseNewsReadCount(jumpPath);
        }
    }
    private void syncEnterpriseNewsReadCount(String jumpPath) {
        Long newsId = parseEnterpriseNewsId(jumpPath);
        if (newsId == null) {
            return;
        }
        long readCount = noticeMapper.selectCount(Wrappers.<SysNotice>lambdaQuery()
                .eq(SysNotice::getStatus, "1")
                .eq(SysNotice::getJumpPath, jumpPath));
        EnterpriseNews enterpriseNews = new EnterpriseNews();
        enterpriseNews.setId(newsId);
        enterpriseNews.setReadCount((int) readCount);
        enterpriseNewsMapper.updateById(enterpriseNews);
    }
    private Long parseEnterpriseNewsId(String jumpPath) {
        if (jumpPath == null || !jumpPath.startsWith("/enterpriseNews")) {
            return null;
        }
        Matcher matcher = ENTERPRISE_NEWS_ID_PATTERN.matcher(jumpPath);
        if (!matcher.find()) {
            return null;
        }
        try {
            return Long.parseLong(matcher.group(1));
        } catch (NumberFormatException e) {
            return null;
        }
    }
}
src/main/resources/application-oa.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,268 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /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: 9000
  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:3306/product-inventory-management-oa?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: oa@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_oa_110.167.133.1
    # redis é…ç½®
    redis:
      # åœ°å€
#      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://110.167.133.1:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)