diff --git a/backend/ruoyi-admin/src/main/resources/application-dev.yml b/backend/ruoyi-admin/src/main/resources/application-dev.yml
index 187582ba..96b88da6 100644
--- a/backend/ruoyi-admin/src/main/resources/application-dev.yml
+++ b/backend/ruoyi-admin/src/main/resources/application-dev.yml
@@ -115,8 +115,8 @@ spring:
idleTimeout: 300000
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟(减少生命周期)
maxLifetime: 900000
- # 多久检查一次连接的活性(增加间隔,减少检测频率)
- keepaliveTime: 60000
+ # 多久检查一次连接的活性(增加间隔,减少检测频率,减少日志输出)
+ keepaliveTime: 300000
# 连接测试查询(验证连接有效性)
# connectionTestQuery: SELECT 11
diff --git a/backend/ruoyi-admin/src/main/resources/application-prod-9900.yml b/backend/ruoyi-admin/src/main/resources/application-prod-9900.yml
new file mode 100644
index 00000000..a0648192
--- /dev/null
+++ b/backend/ruoyi-admin/src/main/resources/application-prod-9900.yml
@@ -0,0 +1,44 @@
+# 生产环境配置 - 9900端口实例
+server:
+ port: 9900
+ servlet:
+ context-path: /
+
+# 实例标识
+spring:
+ application:
+ name: RuoYi-Vue-Plus-9900
+
+# 日志文件配置
+logging:
+ file:
+ name: logs/ruoyi-admin-9900.log
+ pattern:
+ file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
+
+# 监控中心配置 - 端口漂移
+spring.boot.admin.client:
+ enabled: true
+ url: http://172.16.0.67:9090/admin
+ instance:
+ service-host-type: IP
+ metadata:
+ username: ${spring.boot.admin.client.username}
+ userpassword: ${spring.boot.admin.client.password}
+ instance.port: 9900
+ instance.name: ruoyi-admin-9900
+ username: @monitor.username@
+ password: @monitor.password@
+
+# SnailJob 配置 - 端口漂移
+snail-job:
+ enabled: true
+ group: "ruoyi_group_9900"
+ token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
+ server:
+ host: 172.16.0.67
+ port: 17888
+ namespace: "prod"
+ port: 29900
+ host:
+ rpc-type: grpc
diff --git a/backend/ruoyi-admin/src/main/resources/application-prod-9901.yml b/backend/ruoyi-admin/src/main/resources/application-prod-9901.yml
new file mode 100644
index 00000000..699e3d3c
--- /dev/null
+++ b/backend/ruoyi-admin/src/main/resources/application-prod-9901.yml
@@ -0,0 +1,44 @@
+# 生产环境配置 - 9901端口实例
+server:
+ port: 9901
+ servlet:
+ context-path: /
+
+# 实例标识
+spring:
+ application:
+ name: RuoYi-Vue-Plus-9901
+
+# 日志文件配置
+logging:
+ file:
+ name: logs/ruoyi-admin-9901.log
+ pattern:
+ file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
+
+# 监控中心配置 - 端口漂移
+spring.boot.admin.client:
+ enabled: true
+ url: http://172.16.0.67:9090/admin
+ instance:
+ service-host-type: IP
+ metadata:
+ username: ${spring.boot.admin.client.username}
+ userpassword: ${spring.boot.admin.client.password}
+ instance.port: 9901
+ instance.name: ruoyi-admin-9901
+ username: @monitor.username@
+ password: @monitor.password@
+
+# SnailJob 配置 - 端口漂移
+snail-job:
+ enabled: true
+ group: "ruoyi_group_9901"
+ token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
+ server:
+ host: 172.16.0.67
+ port: 17888
+ namespace: "prod"
+ port: 29901
+ host:
+ rpc-type: grpc
diff --git a/backend/ruoyi-admin/src/main/resources/application-prod-9902.yml b/backend/ruoyi-admin/src/main/resources/application-prod-9902.yml
new file mode 100644
index 00000000..c50e9f94
--- /dev/null
+++ b/backend/ruoyi-admin/src/main/resources/application-prod-9902.yml
@@ -0,0 +1,44 @@
+# 生产环境配置 - 9902端口实例
+server:
+ port: 9902
+ servlet:
+ context-path: /
+
+# 实例标识
+spring:
+ application:
+ name: RuoYi-Vue-Plus-9902
+
+# 日志文件配置
+logging:
+ file:
+ name: logs/ruoyi-admin-9902.log
+ pattern:
+ file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
+
+# 监控中心配置 - 端口漂移
+spring.boot.admin.client:
+ enabled: true
+ url: http://172.16.0.67:9090/admin
+ instance:
+ service-host-type: IP
+ metadata:
+ username: ${spring.boot.admin.client.username}
+ userpassword: ${spring.boot.admin.client.password}
+ instance.port: 9902
+ instance.name: ruoyi-admin-9902
+ username: @monitor.username@
+ password: @monitor.password@
+
+# SnailJob 配置 - 端口漂移
+snail-job:
+ enabled: true
+ group: "ruoyi_group_9902"
+ token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
+ server:
+ host: 172.16.0.67
+ port: 17888
+ namespace: "prod"
+ port: 29902
+ host:
+ rpc-type: grpc
diff --git a/backend/ruoyi-admin/src/main/resources/application-prod-9903.yml b/backend/ruoyi-admin/src/main/resources/application-prod-9903.yml
new file mode 100644
index 00000000..209ae9a6
--- /dev/null
+++ b/backend/ruoyi-admin/src/main/resources/application-prod-9903.yml
@@ -0,0 +1,44 @@
+# 生产环境配置 - 9903端口实例
+server:
+ port: 9903
+ servlet:
+ context-path: /
+
+# 实例标识
+spring:
+ application:
+ name: RuoYi-Vue-Plus-9903
+
+# 日志文件配置
+logging:
+ file:
+ name: logs/ruoyi-admin-9903.log
+ pattern:
+ file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
+
+# 监控中心配置 - 端口漂移
+spring.boot.admin.client:
+ enabled: true
+ url: http://172.16.0.67:9090/admin
+ instance:
+ service-host-type: IP
+ metadata:
+ username: ${spring.boot.admin.client.username}
+ userpassword: ${spring.boot.admin.client.password}
+ instance.port: 9903
+ instance.name: ruoyi-admin-9903
+ username: @monitor.username@
+ password: @monitor.password@
+
+# SnailJob 配置 - 端口漂移
+snail-job:
+ enabled: true
+ group: "ruoyi_group_9903"
+ token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
+ server:
+ host: 172.16.0.67
+ port: 17888
+ namespace: "prod"
+ port: 29903
+ host:
+ rpc-type: grpc
diff --git a/backend/ruoyi-admin/src/main/resources/application-prod-local.yml b/backend/ruoyi-admin/src/main/resources/application-prod-local.yml
new file mode 100644
index 00000000..7ba1abbf
--- /dev/null
+++ b/backend/ruoyi-admin/src/main/resources/application-prod-local.yml
@@ -0,0 +1,353 @@
+--- # 临时文件存储位置 避免临时文件被系统清理报错
+spring.servlet.multipart.location: /ruoyi/server/temp
+
+--- # 监控中心配置
+spring.boot.admin.client:
+ # 增加客户端开关
+ enabled: true
+ url: http://47.121.125.41:9090/admin
+ instance:
+ service-host-type: IP
+ metadata:
+ username: ${spring.boot.admin.client.username}
+ userpassword: ${spring.boot.admin.client.password}
+ username: @monitor.username@
+ password: @monitor.password@
+
+--- # snail-job 配置
+snail-job:
+ enabled: true
+ # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
+ group: "ruoyi_group"
+ # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
+ token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
+ server:
+ host: 47.121.125.41
+ port: 17888
+ # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
+ namespace: ${spring.profiles.active}
+ # 随主应用端口漂移
+ port: 2${server.port}
+ # 客户端ip指定
+ host:
+ # RPC类型: netty, grpc
+ rpc-type: grpc
+
+--- # 数据源配置
+spring:
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
+ dynamic:
+ # 性能分析插件(有性能损耗 不建议生产环境使用)
+ p6spy: false
+ # 设置默认的数据源或者数据源组,默认值即为 master
+ primary: master
+ # 严格模式 匹配不到数据源则报错
+ strict: true
+ datasource:
+ # 主库数据源
+ master:
+ type: ${spring.datasource.type}
+ driverClassName: com.mysql.cj.jdbc.Driver
+ # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
+ # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
+ url: jdbc:mysql://47.99.164.66:3306/hzdyjs_xg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+ username: root
+ password: hn02le.34lkdLKD
+# # 从库数据源
+# slave:
+# lazy: true
+# type: ${spring.datasource.type}
+# driverClassName: com.mysql.cj.jdbc.Driver
+# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+# username:
+# password:
+# oracle:
+# type: ${spring.datasource.type}
+# driverClassName: oracle.jdbc.OracleDriver
+# url: jdbc:oracle:thin:@//localhost:1521/XE
+# username: ROOT
+# password: root
+# postgres:
+# type: ${spring.datasource.type}
+# driverClassName: org.postgresql.Driver
+# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
+# username: root
+# password: root
+# sqlserver:
+# type: ${spring.datasource.type}
+# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
+# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
+# username: SA
+# password: root
+ hikari:
+ # 最大连接池数量
+ maxPoolSize: 30
+ # 最小空闲线程数量
+ minIdle: 10
+ # 配置获取连接等待超时的时间
+ connectionTimeout: 30000
+ # 校验超时时间
+ validationTimeout: 5000
+ # 空闲连接存活最大时间,默认10分钟
+ idleTimeout: 600000
+ # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
+ maxLifetime: 1800000
+ # 多久检查一次连接的活性
+ keepaliveTime: 30000
+
+--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
+spring.data:
+ redis:
+ # 地址
+ host: 47.99.164.66
+ # 端口,默认为6379
+ port: 16379
+ # 数据库索引
+ database: 3
+ # redis 密码必须配置
+ password: Hzdyjs@123456
+ # 连接超时时间
+ timeout: 10s
+ # 是否开启ssl
+ ssl.enabled: false
+
+# redisson 配置
+redisson:
+ # redis key前缀
+ keyPrefix:
+ # 线程池数量
+ threads: 16
+ # Netty线程池数量
+ nettyThreads: 32
+ # 单节点配置
+ singleServerConfig:
+ # 客户端名称 不能用中文
+ clientName: RuoYi-Vue-Plus
+ # 最小空闲连接数
+ connectionMinimumIdleSize: 32
+ # 连接池大小
+ connectionPoolSize: 64
+ # 连接空闲超时,单位:毫秒
+ idleConnectionTimeout: 10000
+ # 命令等待超时,单位:毫秒
+ timeout: 3000
+ # 发布和订阅连接池大小
+ subscriptionConnectionPoolSize: 50
+
+--- # mail 邮件发送
+mail:
+ enabled: false
+ host: smtp.163.com
+ port: 465
+ # 是否需要用户名密码验证
+ auth: true
+ # 发送方,遵循RFC-822标准
+ from: xxx@163.com
+ # 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
+ user: xxx@163.com
+ # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
+ pass: xxxxxxxxxx
+ # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
+ starttlsEnable: true
+ # 使用SSL安全连接
+ sslEnable: true
+ # SMTP超时时长,单位毫秒,缺省值不超时
+ timeout: 0
+ # Socket连接超时值,单位毫秒,缺省值不超时
+ connectionTimeout: 0
+
+--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
+# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
+sms:
+ # 配置源类型用于标定配置来源(interface,yaml)
+ config-type: yaml
+ # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
+ restricted: true
+ # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
+ minute-max: 1
+ # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
+ account-max: 30
+ # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
+ blends:
+ # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
+ # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
+ config1:
+ # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+ supplier: alibaba
+ # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
+ access-key-id: 您的accessKey
+ # 称为accessSecret有些称之为apiSecret
+ access-key-secret: 您的accessKeySecret
+ signature: 您的短信签名
+ sdk-app-id: 您的sdkAppId
+ config2:
+ # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+ supplier: tencent
+ access-key-id: 您的accessKey
+ access-key-secret: 您的accessKeySecret
+ signature: 您的短信签名
+ sdk-app-id: 您的sdkAppId
+
+--- # 三方授权
+justauth:
+ # 前端外网访问地址
+ address: http://localhost:80
+ type:
+ maxkey:
+ # maxkey 服务器地址
+ # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
+ server-url: http://sso.maxkey.top
+ client-id: 876892492581044224
+ client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
+ redirect-uri: ${justauth.address}/social-callback?source=maxkey
+ topiam:
+ # topiam 服务器地址
+ server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
+ client-id: 449c4*********937************759
+ client-secret: ac7***********1e0************28d
+ redirect-uri: ${justauth.address}/social-callback?source=topiam
+ scopes: [ openid, email, phone, profile ]
+ qq:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=qq
+ union-id: false
+ weibo:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=weibo
+ gitee:
+ client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
+ client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
+ redirect-uri: ${justauth.address}/social-callback?source=gitee
+ dingtalk:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=dingtalk
+ baidu:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=baidu
+ csdn:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=csdn
+ coding:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=coding
+ coding-group-name: xx
+ oschina:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=oschina
+ alipay_wallet:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
+ alipay-public-key: MIIB**************DAQAB
+ wechat_open:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=wechat_open
+ wechat_mp:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
+ wechat_enterprise:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
+ agent-id: 1000002
+ gitlab:
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=gitlab
+ gitea:
+ # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
+ # gitea 服务器地址
+ server-url: https://demo.gitea.com
+ client-id: 10**********6
+ client-secret: 1f7d08**********5b7**********29e
+ redirect-uri: ${justauth.address}/social-callback?source=gitea
+
+system:
+ file-cache:
+ # 是否启用文件缓存
+ enabled: true
+
+ # 最大缓存文件数量
+ max-cache-size: 100
+
+ # 最大缓存大小(字节)- 500MB
+ max-cache-size-bytes: 524288000
+
+ # 缓存过期时间(毫秒)- 30分钟
+ expire-time-ms: 1800000
+
+ # 最大缓存单个文件大小(字节)- 50MB
+ max-single-file-size: 52428800
+
+ # LRU淘汰比例(百分比)
+ evict-ratio: 20
+
+ # 是否启用缓存统计
+ enable-stats: true
+
+ # 缓存清理间隔(毫秒)- 5分钟
+ cleanup-interval-ms: 300000
+# 配置说明:
+# 1. enabled: 控制是否启用文件缓存功能
+# 2. max-cache-size: 缓存的最大文件数量
+# 3. max-cache-size-bytes: 缓存的最大总大小(字节)
+# 4. expire-time-ms: 缓存条目的过期时间(毫秒)
+# 5. max-single-file-size: 单个文件的最大缓存大小(字节)
+# 6. evict-ratio: LRU淘汰时移除的条目比例(百分比)
+# 7. enable-stats: 是否启用缓存统计功能
+# 8. cleanup-interval-ms: 自动清理过期条目的间隔(毫秒)
+
+# 性能优化建议:
+# - 对于内存充足的环境,可以适当增加 max-cache-size-bytes
+# - 对于频繁访问的小文件,可以增加 max-cache-size
+# - 对于大文件较多的环境,可以调整 max-single-file-size
+# - 对于访问频率较低的环境,可以适当减少 expire-time-ms
+
+
+--- # SQL日志配置(用于调试班级名额查询问题)
+logging:
+ level:
+ # 开启 MyBatis SQL 日志
+ org.dromara.jcbt.mapper: DEBUG
+ org.dromara.jcbt.service: DEBUG
+ # 开启 MyBatis-Plus SQL 日志
+ com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor: DEBUG
+ # 开启 HikariCP 连接池日志(可选)
+ com.zaxxer.hikari: DEBUG
+ # 开启 P6Spy SQL 日志(更详细的SQL执行日志)
+ p6spy: INFO
+ pattern:
+ # 控制台日志格式,增加线程名和方法名
+ console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"
+
+# MyBatis 配置
+mybatis-plus:
+ configuration:
+ # 开启 SQL 日志打印
+ log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+ # 开启驼峰命名转换
+ map-underscore-to-camel-case: true
+ # 开启缓存
+ cache-enabled: true
+ # 开启懒加载
+ lazy-loading-enabled: true
+ # 开启积极懒加载
+ aggressive-lazy-loading: false
+
+--- # 高德地图配置
+amap:
+ # 高德地图API密钥(请替换为您的实际密钥)
+ key: 920750e421cd6357ddf60ee1e929e0dd
+ # 高德地图API基础URL
+ url: https://restapi.amap.com
diff --git a/backend/ruoyi-admin/src/main/resources/application-prod.yml b/backend/ruoyi-admin/src/main/resources/application-prod.yml
index 7ba1abbf..6ce93392 100644
--- a/backend/ruoyi-admin/src/main/resources/application-prod.yml
+++ b/backend/ruoyi-admin/src/main/resources/application-prod.yml
@@ -5,7 +5,7 @@ spring.servlet.multipart.location: /ruoyi/server/temp
spring.boot.admin.client:
# 增加客户端开关
enabled: true
- url: http://47.121.125.41:9090/admin
+ url: http://172.16.0.67:9090/admin
instance:
service-host-type: IP
metadata:
@@ -22,7 +22,7 @@ snail-job:
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
server:
- host: 47.121.125.41
+ host: 172.16.0.67
port: 17888
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
namespace: ${spring.profiles.active}
@@ -52,9 +52,9 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
- url: jdbc:mysql://47.99.164.66:3306/hzdyjs_xg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+ url: jdbc:mysql://172.16.0.67:3306/hzdyjs_xg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
- password: hn02le.34lkdLKD
+ password: Hzdyjs@Root@123456
# # 从库数据源
# slave:
# lazy: true
@@ -83,7 +83,7 @@ spring:
# password: root
hikari:
# 最大连接池数量
- maxPoolSize: 30
+ maxPoolSize: 60
# 最小空闲线程数量
minIdle: 10
# 配置获取连接等待超时的时间
@@ -94,20 +94,20 @@ spring:
idleTimeout: 600000
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
maxLifetime: 1800000
- # 多久检查一次连接的活性
- keepaliveTime: 30000
+ # 多久检查一次连接的活性(增加间隔,减少检测频率,减少日志输出)
+ keepaliveTime: 300000
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
spring.data:
redis:
# 地址
- host: 47.99.164.66
+ host: 172.16.0.67
# 端口,默认为6379
port: 16379
# 数据库索引
database: 3
# redis 密码必须配置
- password: Hzdyjs@123456
+ password: Hzdyjs@redis@123456
# 连接超时时间
timeout: 10s
# 是否开启ssl
@@ -318,15 +318,16 @@ system:
--- # SQL日志配置(用于调试班级名额查询问题)
logging:
level:
- # 开启 MyBatis SQL 日志
- org.dromara.jcbt.mapper: DEBUG
- org.dromara.jcbt.service: DEBUG
- # 开启 MyBatis-Plus SQL 日志
- com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor: DEBUG
- # 开启 HikariCP 连接池日志(可选)
- com.zaxxer.hikari: DEBUG
- # 开启 P6Spy SQL 日志(更详细的SQL执行日志)
- p6spy: INFO
+ # 生产环境关闭 MyBatis SQL 日志,避免性能影响和敏感信息泄露
+ # org.dromara.jcbt.mapper: DEBUG
+ # org.dromara.jcbt.service: DEBUG
+ # 生产环境关闭 MyBatis-Plus SQL 日志
+ # com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor: DEBUG
+ # 关闭 HikariCP 连接池DEBUG日志,减少keepalive检测日志
+ com.zaxxer.hikari: WARN
+ com.zaxxer.hikari.pool.HikariPool: WARN
+ # 生产环境关闭 P6Spy SQL 日志,避免性能影响
+ # p6spy: INFO
pattern:
# 控制台日志格式,增加线程名和方法名
console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"
@@ -334,7 +335,7 @@ logging:
# MyBatis 配置
mybatis-plus:
configuration:
- # 开启 SQL 日志打印
+ # 生产环境关闭 SQL 日志打印,避免性能影响和敏感信息泄露
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 开启驼峰命名转换
map-underscore-to-camel-case: true
@@ -345,7 +346,7 @@ mybatis-plus:
# 开启积极懒加载
aggressive-lazy-loading: false
---- # 高德地图配置
+--- # 高德地图配置 生产环境需要修改成学校的配置
amap:
# 高德地图API密钥(请替换为您的实际密钥)
key: 920750e421cd6357ddf60ee1e929e0dd
diff --git a/backend/ruoyi-admin/src/main/resources/application.yml b/backend/ruoyi-admin/src/main/resources/application.yml
index f66f8f50..86b03355 100644
--- a/backend/ruoyi-admin/src/main/resources/application.yml
+++ b/backend/ruoyi-admin/src/main/resources/application.yml
@@ -41,6 +41,9 @@ logging:
org.springframework: warn
org.mybatis.spring.mapper: error
org.apache.fury: warn
+ # 关闭HikariCP连接池DEBUG日志,减少keepalive检测日志
+ com.zaxxer.hikari: warn
+ com.zaxxer.hikari.pool.HikariPool: warn
# 关闭SQL日志调试,减少SELECT 1心跳检测日志
# org.dromara.cw.mapper.CwApprovalRecordMapper: info
# org.dromara.cw.service.impl.CwApprovalRecordServiceImpl: info
diff --git a/backend/ruoyi-admin/src/main/resources/logback-plus.xml b/backend/ruoyi-admin/src/main/resources/logback-plus.xml
index d6322759..3ed170b9 100644
--- a/backend/ruoyi-admin/src/main/resources/logback-plus.xml
+++ b/backend/ruoyi-admin/src/main/resources/logback-plus.xml
@@ -165,4 +165,9 @@
+
+
+
+
+
diff --git a/backend/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml b/backend/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
index 86766d7a..108a203e 100644
--- a/backend/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
+++ b/backend/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
@@ -38,7 +38,7 @@ spring.boot.admin.client:
# 增加客户端开关
enabled: true
# 设置 Spring Boot Admin Server 地址
- url: http://127.0.0.1:9090/admin
+ url: http://172.16.0.67:9090/admin
instance:
service-host-type: IP
metadata:
diff --git a/backend/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod-local.yml b/backend/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod-local.yml
new file mode 100644
index 00000000..0f731ab7
--- /dev/null
+++ b/backend/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod-local.yml
@@ -0,0 +1,40 @@
+spring:
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://47.99.164.66:3306/hzdyjs_xg?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+ username: root
+ password: hn02le.34lkdLKD
+ hikari:
+ connection-timeout: 30000
+ validation-timeout: 5000
+ minimum-idle: 10
+ maximum-pool-size: 20
+ idle-timeout: 600000
+ max-lifetime: 900000
+ keepaliveTime: 30000
+
+--- # snail-job 服务端配置
+snail-job:
+ # 拉取重试数据的每批次的大小
+ retry-pull-page-size: 1000
+ # 拉取重试数据的每批次的大小
+ job-pull-page-size: 1000
+ # 服务器端口
+ server-port: 17888
+ # 日志保存时间(单位: day)
+ log-storage: 7
+ rpc-type: grpc
+
+--- # 监控中心配置
+spring.boot.admin.client:
+ # 增加客户端开关
+ enabled: true
+ url: http://47.121.125.41:9090/admin
+ instance:
+ service-host-type: IP
+ metadata:
+ username: ${spring.boot.admin.client.username}
+ userpassword: ${spring.boot.admin.client.password}
+ username: @monitor.username@
+ password: @monitor.password@
diff --git a/backend/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml b/backend/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml
index 0f731ab7..a0059e7d 100644
--- a/backend/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml
+++ b/backend/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml
@@ -2,9 +2,9 @@ spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://47.99.164.66:3306/hzdyjs_xg?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+ url: jdbc:mysql://172.16.0.67:3306/hzdyjs_xg?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
- password: hn02le.34lkdLKD
+ password: Hzdyjs@Root@123456
hikari:
connection-timeout: 30000
validation-timeout: 5000
@@ -30,7 +30,7 @@ snail-job:
spring.boot.admin.client:
# 增加客户端开关
enabled: true
- url: http://47.121.125.41:9090/admin
+ url: http://172.16.0.67:9090/admin
instance:
service-host-type: IP
metadata:
diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/VolunteerMemberController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/VolunteerMemberController.java
deleted file mode 100644
index 0519ecba..00000000
--- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/VolunteerMemberController.java
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/技术问题解决记录/SSE关闭接口406错误解决方案.md b/docs/技术问题解决记录/SSE关闭接口406错误解决方案.md
new file mode 100644
index 00000000..c779157f
--- /dev/null
+++ b/docs/技术问题解决记录/SSE关闭接口406错误解决方案.md
@@ -0,0 +1,252 @@
+# SSE关闭接口406错误解决方案
+
+## 🚨 问题描述
+
+用户在前台退出时,SSE关闭接口返回406 Not Acceptable错误:
+
+```
+Request URL: http://hzdyjsweb.camasort.com/prod-api/resource/sse/close
+Request Method: GET
+Status Code: 406 Not Acceptable
+```
+
+## 🔍 问题分析
+
+### 1. 错误原因分析
+
+**406 Not Acceptable错误**通常表示:
+- 服务器无法根据请求的内容特性完成请求
+- 请求的资源的内容特性无法满足请求头中的条件
+- 用户认证信息缺失或无效
+
+### 2. 根本原因
+
+1. **认证时机问题**:
+ - 前端在logout时先调用SSE关闭接口,再调用logout接口
+ - 此时token可能已经失效或即将失效
+ - 后端`LoginHelper.getUserId()`可能返回null
+
+2. **后端接口设计问题**:
+ - SSE关闭接口使用了`@SaIgnore`注解,但仍然需要获取用户信息
+ - 缺少对空值和异常的处理
+ - 没有优雅降级机制
+
+3. **nginx代理配置问题**:
+ - SSE关闭接口可能没有正确的代理配置
+ - 缺少专门的错误处理
+
+## 🛠️ 解决方案
+
+### 方案一:修复后端SSE关闭接口(已实施)
+
+#### 1. 优化错误处理
+
+```java
+/**
+ * 关闭 SSE 连接
+ */
+@SaIgnore
+@GetMapping(value = "${sse.path}/close")
+public R close() {
+ try {
+ // 尝试获取token和用户ID,如果获取失败则直接返回成功
+ String tokenValue = null;
+ Long userId = null;
+
+ try {
+ tokenValue = StpUtil.getTokenValue();
+ userId = LoginHelper.getUserId();
+ } catch (Exception e) {
+ // 如果获取用户信息失败,记录日志但不抛出异常
+ log.warn("Failed to get user info for SSE close: {}", e.getMessage());
+ return R.ok();
+ }
+
+ // 如果token或userId为空,直接返回成功
+ if (tokenValue == null || userId == null) {
+ log.info("Token or userId is null, skipping SSE disconnect");
+ return R.ok();
+ }
+
+ sseEmitterManager.disconnect(userId, tokenValue);
+ return R.ok();
+ } catch (Exception e) {
+ log.error("Error during SSE close: {}", e.getMessage(), e);
+ // 即使出错也返回成功,避免影响用户退出流程
+ return R.ok();
+ }
+}
+```
+
+#### 2. 关键改进点
+
+- **添加异常捕获**:捕获所有可能的异常
+- **空值检查**:检查token和userId是否为空
+- **优雅降级**:即使出错也返回成功状态
+- **日志记录**:记录详细的错误信息用于调试
+
+### 方案二:优化前端logout逻辑(已实施)
+
+#### 1. 添加错误处理
+
+```typescript
+/**
+ * 注销
+ */
+export function logout() {
+ // 先尝试关闭SSE连接,但不阻塞logout流程
+ if (import.meta.env.VITE_APP_SSE === 'true') {
+ request({
+ url: '/resource/sse/close',
+ method: 'get'
+ }).catch((error) => {
+ // SSE关闭失败不影响用户退出,只记录日志
+ console.warn('SSE close failed during logout:', error);
+ });
+ }
+
+ // 执行正常的logout流程
+ return request({
+ url: '/auth/logout',
+ method: 'post'
+ });
+}
+```
+
+#### 2. 关键改进点
+
+- **非阻塞调用**:SSE关闭失败不影响logout流程
+- **错误捕获**:捕获SSE关闭接口的错误
+- **日志记录**:记录错误信息但不影响用户体验
+
+### 方案三:更新nginx配置(已实施)
+
+#### 1. 添加SSE关闭接口专用配置
+
+```nginx
+# SSE关闭接口专用配置
+location ^~ /prod-api/resource/sse/close {
+ proxy_pass http://47.121.125.41:8080/resource/sse/close;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # 普通API配置(不需要SSE特殊配置)
+ proxy_connect_timeout 30s;
+ proxy_send_timeout 30s;
+ proxy_read_timeout 30s;
+
+ # CORS处理
+ add_header Access-Control-Allow-Origin *;
+ add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
+ add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,clientid';
+ add_header Access-Control-Allow-Credentials true;
+
+ if ($request_method = 'OPTIONS') {
+ return 204;
+ }
+}
+```
+
+#### 2. 配置说明
+
+- **独立配置**:为SSE关闭接口提供独立的代理配置
+- **合理超时**:设置30秒超时,适合普通API调用
+- **CORS支持**:确保跨域请求正常工作
+
+## 📋 部署步骤
+
+### 1. 更新后端代码
+
+```bash
+# 重新编译后端项目
+mvn clean compile
+
+# 重启后端服务
+systemctl restart your-backend-service
+```
+
+### 2. 更新前端代码
+
+```bash
+# 重新构建前端项目
+npm run build
+
+# 部署到nginx目录
+cp -r dist/* /root/hzdyjsweb9091/dist/
+```
+
+### 3. 更新nginx配置
+
+```bash
+# 测试nginx配置
+nginx -t
+
+# 重新加载配置
+nginx -s reload
+```
+
+## 🔧 验证方法
+
+### 1. 功能验证
+
+1. **正常登录**:确保用户能够正常登录
+2. **SSE连接**:检查SSE连接是否正常建立
+3. **正常退出**:测试用户退出流程
+4. **错误处理**:检查控制台是否有错误信息
+
+### 2. 错误场景测试
+
+1. **Token失效退出**:在token即将失效时退出
+2. **网络异常退出**:在网络不稳定的情况下退出
+3. **并发退出**:多个用户同时退出
+
+### 3. 日志检查
+
+```bash
+# 检查后端日志
+tail -f /path/to/backend/logs/application.log | grep -i "sse"
+
+# 检查nginx错误日志
+tail -f /var/log/nginx/hzdyjsweb.camasort.com.error.log
+```
+
+## 🎯 预期效果
+
+### 1. 问题解决
+
+- ✅ 消除406错误
+- ✅ 用户退出流程顺畅
+- ✅ 不影响用户体验
+
+### 2. 系统稳定性
+
+- ✅ SSE连接正确关闭
+- ✅ 资源正确释放
+- ✅ 错误处理完善
+
+### 3. 监控指标
+
+- SSE关闭成功率:100%
+- 用户退出成功率:100%
+- 错误日志减少:显著减少
+
+## 📚 相关文档
+
+- [SSE生产环境配置问题解决方案](../SSE生产环境配置问题解决方案.md)
+- [nginx配置文档](../../部署文档/nginx配置/)
+- [前端环境变量配置建议](../../部署文档/nginx配置/前端环境变量配置建议.md)
+
+## 🔄 后续优化
+
+1. **监控告警**:添加SSE关闭失败监控
+2. **性能优化**:优化SSE连接管理
+3. **用户体验**:添加退出进度提示
+4. **错误恢复**:实现自动重试机制
+
+---
+
+**更新时间**:2025-01-03
+**维护人员**:系统管理员
+**文档版本**:v1.0
\ No newline at end of file
diff --git a/docs/技术问题解决记录/SSE生产环境配置问题解决方案.md b/docs/技术问题解决记录/SSE生产环境配置问题解决方案.md
new file mode 100644
index 00000000..b13744af
--- /dev/null
+++ b/docs/技术问题解决记录/SSE生产环境配置问题解决方案.md
@@ -0,0 +1,256 @@
+# SSE生产环境配置问题解决方案
+
+## 🚨 问题描述
+
+在生产环境中,通过nginx配置后,前端无法正常接收SSE(Server-Sent Events)消息推送。
+
+## 🔍 问题分析
+
+### 1. 技术架构分析
+
+**后端SSE实现**:
+- 使用Spring Boot的`SseEmitter`实现
+- SSE端点:`/resource/sse`
+- 支持用户认证和Token验证
+- 使用Redis进行消息广播
+
+**前端SSE实现**:
+- 使用Vue 3的`useEventSource`组合式API
+- 自动重连机制(10次重试,3秒间隔)
+- 消息处理:显示通知和存储到状态管理
+
+**nginx代理配置**:
+- 当前配置缺少专门的SSE代理处理
+- 超时设置可能不够长
+- 缺少SSE特定的响应头配置
+
+### 2. 根本原因
+
+1. **nginx配置不完整**:缺少专门的SSE代理配置
+2. **超时设置不足**:SSE是长连接,需要更长的超时时间
+3. **响应头配置缺失**:SSE需要特定的Content-Type和缓存控制
+4. **代理优先级问题**:SSE路由可能被其他配置覆盖
+
+## 🛠️ 解决方案
+
+### 方案一:优化nginx配置(推荐)
+
+#### 1. 添加专门的SSE代理配置
+
+```nginx
+# SSE专用代理配置(重要:必须放在其他API配置之前)
+location ^~ /prod-api/resource/sse {
+ proxy_pass http://47.121.125.41:8080/resource/sse;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # SSE特殊配置
+ proxy_http_version 1.1;
+ proxy_set_header Connection "";
+ proxy_buffering off;
+ proxy_cache off;
+
+ # SSE需要更长的超时时间
+ proxy_connect_timeout 60s;
+ proxy_send_timeout 86400s; # 24小时
+ proxy_read_timeout 86400s; # 24小时
+
+ # SSE响应头设置
+ proxy_set_header Accept "text/event-stream";
+ proxy_set_header Cache-Control "no-cache";
+ proxy_set_header Connection "keep-alive";
+
+ # CORS处理
+ add_header Access-Control-Allow-Origin *;
+ add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
+ add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,clientid';
+ add_header Access-Control-Allow-Credentials true;
+
+ if ($request_method = 'OPTIONS') {
+ return 204;
+ }
+}
+```
+
+#### 2. 关键配置说明
+
+- **`proxy_buffering off`**:禁用nginx缓冲,确保SSE消息实时传输
+- **`proxy_cache off`**:禁用缓存,避免消息被缓存
+- **`proxy_read_timeout 86400s`**:设置24小时超时,支持长连接
+- **`proxy_set_header Connection ""`**:清除Connection头,避免代理问题
+- **`add_header Access-Control-Allow-Credentials true`**:支持跨域认证
+
+### 方案二:修改前端配置
+
+#### 1. 环境变量配置
+
+```bash
+# 推荐配置:通过nginx代理
+VITE_APP_BASE_API = '/prod-api'
+VITE_APP_SSE = true
+VITE_APP_WEBSOCKET = false
+```
+
+#### 2. 备选配置:直接访问后端
+
+```bash
+# 备选配置:直接访问后端
+VITE_APP_BASE_API = 'http://47.121.125.41:8080'
+VITE_APP_SSE = true
+VITE_APP_WEBSOCKET = false
+```
+
+## 📋 部署步骤
+
+### 1. 更新nginx配置
+
+```bash
+# 备份原配置
+cp /etc/nginx/conf.d/hzdyjsweb.camasort.com.conf /etc/nginx/conf.d/hzdyjsweb.camasort.com.conf.backup
+
+# 更新配置文件
+vim /etc/nginx/conf.d/hzdyjsweb.camasort.com.conf
+
+# 测试配置
+nginx -t
+
+# 重新加载配置
+nginx -s reload
+```
+
+### 2. 更新前端配置
+
+```bash
+# 进入前端项目目录
+cd /path/to/frontend
+
+# 更新环境变量
+vim .env.production
+
+# 重新构建
+npm run build
+
+# 部署到nginx目录
+cp -r dist/* /root/hzdyjsweb9091/dist/
+```
+
+### 3. 重启服务
+
+```bash
+# 重启后端服务(如果需要)
+systemctl restart your-backend-service
+
+# 检查nginx状态
+systemctl status nginx
+```
+
+## 🔧 故障排查
+
+### 1. 检查nginx配置
+
+```bash
+# 测试nginx配置
+nginx -t
+
+# 查看nginx错误日志
+tail -f /var/log/nginx/error.log
+tail -f /var/log/nginx/hzdyjsweb.camasort.com.error.log
+
+# 查看nginx访问日志
+tail -f /var/log/nginx/hzdyjsweb.camasort.com.access.log
+```
+
+### 2. 检查SSE连接
+
+#### 浏览器开发者工具
+1. 打开F12开发者工具
+2. 切换到Network面板
+3. 筛选XHR/Fetch请求
+4. 查找SSE连接(通常显示为EventStream)
+5. 检查连接状态和响应头
+
+#### 控制台错误信息
+```javascript
+// 检查SSE连接状态
+console.log('SSE连接状态:', EventSource.readyState);
+// 0: CONNECTING, 1: OPEN, 2: CLOSED
+```
+
+### 3. 测试SSE端点
+
+#### 直接测试后端
+```bash
+curl -H "Authorization: Bearer YOUR_TOKEN" \
+ -H "clientid: e5cd7e4891bf95d1d19206ce24a7b32e" \
+ http://47.121.125.41:8080/resource/sse
+```
+
+#### 通过nginx测试
+```bash
+curl -H "Authorization: Bearer YOUR_TOKEN" \
+ -H "clientid: e5cd7e4891bf95d1d19206ce24a7b32e" \
+ http://hzdyjsweb.camasort.com/prod-api/resource/sse
+```
+
+### 4. 检查后端SSE服务
+
+#### 查看后端日志
+```bash
+# 查看应用日志
+tail -f /path/to/backend/logs/application.log
+
+# 查看SSE相关日志
+grep -i "sse" /path/to/backend/logs/application.log
+```
+
+#### 检查Redis连接
+```bash
+# 连接Redis
+redis-cli
+
+# 检查SSE主题订阅
+SUBSCRIBE global:sse
+```
+
+## 🎯 验证方法
+
+### 1. 功能验证
+
+1. **登录系统**:确保用户已登录
+2. **触发SSE消息**:通过工作流或其他功能触发消息推送
+3. **检查通知**:查看是否收到实时通知
+4. **检查控制台**:查看SSE连接状态和消息
+
+### 2. 性能验证
+
+1. **连接稳定性**:长时间保持连接,检查是否断开
+2. **消息延迟**:测试消息推送的实时性
+3. **并发连接**:测试多用户同时连接的情况
+
+### 3. 监控指标
+
+- SSE连接数
+- 消息推送成功率
+- 连接断开频率
+- 响应时间
+
+## 📚 相关文档
+
+- [nginx SSE代理配置官方文档](http://nginx.org/en/docs/http/ngx_http_proxy_module.html)
+- [Spring Boot SSE官方文档](https://docs.spring.io/spring-framework/reference/web/webmvc/controller/ann-methods/sse.html)
+- [Vue 3 EventSource使用指南](https://vueuse.org/core/useEventSource/)
+
+## 🔄 后续优化
+
+1. **添加SSE连接监控**:监控连接状态和性能
+2. **实现连接池管理**:优化连接资源使用
+3. **添加消息重试机制**:提高消息推送可靠性
+4. **优化nginx配置**:根据实际使用情况调整超时和缓冲设置
+
+---
+
+**更新时间**:2025-01-03
+**维护人员**:系统管理员
+**文档版本**:v1.0
\ No newline at end of file
diff --git a/docs/部署文档/nginx配置/hzdyjsweb.camasort.com.conf b/docs/部署文档/nginx配置/hzdyjsweb.camasort.com.conf
index 62cdacc8..2993ebb8 100644
--- a/docs/部署文档/nginx配置/hzdyjsweb.camasort.com.conf
+++ b/docs/部署文档/nginx配置/hzdyjsweb.camasort.com.conf
@@ -41,36 +41,95 @@ server {
add_header Pragma "no-cache";
}
- # Warm-Flow工作流UI代理配置(重要:必须放在Vue路由配置之前,使用^~确保优先级)
- location ^~ /prod-api/warm-flow-ui/ {
- proxy_pass http://47.121.125.41:8080/warm-flow-ui/;
+ # SSE专用代理配置(重要:必须放在其他API配置之前)
+ location ^~ /prod-api/resource/sse {
+ proxy_pass http://47.121.125.41:8080/resource/sse;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
- # 支持WebSocket和SSE
+ # SSE特殊配置
proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
+ proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
- # 超时设置
+ # SSE需要更长的超时时间
proxy_connect_timeout 60s;
- proxy_send_timeout 60s;
- proxy_read_timeout 86400s;
+ proxy_send_timeout 86400s; # 24小时
+ proxy_read_timeout 86400s; # 24小时
+
+ # SSE响应头设置
+ proxy_set_header Accept "text/event-stream";
+ proxy_set_header Cache-Control "no-cache";
+ proxy_set_header Connection "keep-alive";
# CORS处理
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,clientid';
+ add_header Access-Control-Allow-Credentials true;
if ($request_method = 'OPTIONS') {
return 204;
}
}
+ # SSE关闭接口专用配置
+ location ^~ /prod-api/resource/sse/close {
+ proxy_pass http://47.121.125.41:8080/resource/sse/close;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # 普通API配置(不需要SSE特殊配置)
+ proxy_connect_timeout 30s;
+ proxy_send_timeout 30s;
+ proxy_read_timeout 30s;
+
+ # CORS处理
+ add_header Access-Control-Allow-Origin *;
+ add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
+ add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,clientid';
+ add_header Access-Control-Allow-Credentials true;
+
+ if ($request_method = 'OPTIONS') {
+ return 204;
+ }
+ }
+
+ # Warm-Flow工作流UI代理配置(重要:必须放在Vue路由配置之前)
+ location ^~ /prod-api/warm-flow-ui/ {
+ proxy_pass http://47.121.125.41:8080/warm-flow-ui/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # 支持WebSocket和SSE
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_buffering off;
+ proxy_cache off;
+
+ # 超时设置
+ proxy_connect_timeout 60s;
+ proxy_send_timeout 60s;
+ proxy_read_timeout 86400s;
+
+ # CORS处理
+ add_header Access-Control-Allow-Origin *;
+ add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
+ add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,clientid';
+
+ if ($request_method = 'OPTIONS') {
+ return 204;
+ }
+ }
+
# API代理配置(使用^~确保优先级,避免被Vue路由配置干扰)
location ^~ /prod-api/ {
proxy_pass http://47.121.125.41:8080/;
diff --git a/docs/部署文档/杭州第一技师部署文档/docker-compose-multi.yml b/docs/部署文档/杭州第一技师部署文档/docker-compose-multi.yml
new file mode 100644
index 00000000..b6b3f869
--- /dev/null
+++ b/docs/部署文档/杭州第一技师部署文档/docker-compose-multi.yml
@@ -0,0 +1,135 @@
+version: '3.8'
+
+services:
+ # 9900端口实例
+ ruoyi-admin-9900:
+ image: openjdk:17-jre-slim
+ container_name: ruoyi-admin-9900
+ ports:
+ - "9900:9900"
+ volumes:
+ - ./ruoyi-admin.jar:/app/ruoyi-admin.jar
+ - ./logs:/app/logs
+ - ./temp:/app/temp
+ working_dir: /app
+ command: >
+ java -Xms512m -Xmx1024m
+ -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
+ -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
+ -XX:+UseG1GC -XX:MaxGCPauseMillis=200
+ -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/
+ -Dspring.profiles.active=prod-9900
+ -Dserver.port=9900
+ -jar ruoyi-admin.jar
+ environment:
+ - SPRING_PROFILES_ACTIVE=prod-9900
+ - SERVER_PORT=9900
+ restart: unless-stopped
+ networks:
+ - ruoyi-network
+
+ # 9901端口实例
+ ruoyi-admin-9901:
+ image: openjdk:17-jre-slim
+ container_name: ruoyi-admin-9901
+ ports:
+ - "9901:9901"
+ volumes:
+ - ./ruoyi-admin.jar:/app/ruoyi-admin.jar
+ - ./logs:/app/logs
+ - ./temp:/app/temp
+ working_dir: /app
+ command: >
+ java -Xms512m -Xmx1024m
+ -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
+ -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
+ -XX:+UseG1GC -XX:MaxGCPauseMillis=200
+ -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/
+ -Dspring.profiles.active=prod-9901
+ -Dserver.port=9901
+ -jar ruoyi-admin.jar
+ environment:
+ - SPRING_PROFILES_ACTIVE=prod-9901
+ - SERVER_PORT=9901
+ restart: unless-stopped
+ networks:
+ - ruoyi-network
+
+ # 9902端口实例
+ ruoyi-admin-9902:
+ image: openjdk:17-jre-slim
+ container_name: ruoyi-admin-9902
+ ports:
+ - "9902:9902"
+ volumes:
+ - ./ruoyi-admin.jar:/app/ruoyi-admin.jar
+ - ./logs:/app/logs
+ - ./temp:/app/temp
+ working_dir: /app
+ command: >
+ java -Xms512m -Xmx1024m
+ -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
+ -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
+ -XX:+UseG1GC -XX:MaxGCPauseMillis=200
+ -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/
+ -Dspring.profiles.active=prod-9902
+ -Dserver.port=9902
+ -jar ruoyi-admin.jar
+ environment:
+ - SPRING_PROFILES_ACTIVE=prod-9902
+ - SERVER_PORT=9902
+ restart: unless-stopped
+ networks:
+ - ruoyi-network
+
+ # 9903端口实例
+ ruoyi-admin-9903:
+ image: openjdk:17-jre-slim
+ container_name: ruoyi-admin-9903
+ ports:
+ - "9903:9903"
+ volumes:
+ - ./ruoyi-admin.jar:/app/ruoyi-admin.jar
+ - ./logs:/app/logs
+ - ./temp:/app/temp
+ working_dir: /app
+ command: >
+ java -Xms512m -Xmx1024m
+ -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
+ -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
+ -XX:+UseG1GC -XX:MaxGCPauseMillis=200
+ -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/
+ -Dspring.profiles.active=prod-9903
+ -Dserver.port=9903
+ -jar ruoyi-admin.jar
+ environment:
+ - SPRING_PROFILES_ACTIVE=prod-9903
+ - SERVER_PORT=9903
+ restart: unless-stopped
+ networks:
+ - ruoyi-network
+
+ # Nginx负载均衡器
+ nginx:
+ image: nginx:alpine
+ container_name: ruoyi-nginx
+ ports:
+ - "80:80"
+ volumes:
+ - ./nginx.conf:/etc/nginx/nginx.conf:ro
+ depends_on:
+ - ruoyi-admin-9900
+ - ruoyi-admin-9901
+ - ruoyi-admin-9902
+ - ruoyi-admin-9903
+ restart: unless-stopped
+ networks:
+ - ruoyi-network
+
+networks:
+ ruoyi-network:
+ driver: bridge
+
+volumes:
+ logs:
+ temp:
diff --git a/docs/部署文档/杭州第一技师部署文档/nginx.conf b/docs/部署文档/杭州第一技师部署文档/nginx.conf
new file mode 100644
index 00000000..00242db3
--- /dev/null
+++ b/docs/部署文档/杭州第一技师部署文档/nginx.conf
@@ -0,0 +1,116 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ # 日志格式
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+ error_log /var/log/nginx/error.log;
+
+ # 基础配置
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+
+ # Gzip压缩
+ gzip on;
+ gzip_vary on;
+ gzip_min_length 1024;
+ gzip_proxied any;
+ gzip_comp_level 6;
+ gzip_types
+ text/plain
+ text/css
+ text/xml
+ text/javascript
+ application/json
+ application/javascript
+ application/xml+rss
+ application/atom+xml
+ image/svg+xml;
+
+ # 上游服务器配置
+ upstream ruoyi_backend {
+ # 轮询负载均衡
+ server ruoyi-admin-9900:9900 weight=1 max_fails=3 fail_timeout=30s;
+ server ruoyi-admin-9901:9901 weight=1 max_fails=3 fail_timeout=30s;
+ server ruoyi-admin-9902:9902 weight=1 max_fails=3 fail_timeout=30s;
+ server ruoyi-admin-9903:9903 weight=1 max_fails=3 fail_timeout=30s;
+
+ # 健康检查
+ keepalive 32;
+ }
+
+ # 主服务器配置
+ server {
+ listen 80;
+ server_name localhost;
+
+ # 静态资源缓存
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ proxy_pass http://ruoyi_backend;
+ }
+
+ # API请求
+ location / {
+ proxy_pass http://ruoyi_backend;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # 超时设置
+ proxy_connect_timeout 30s;
+ proxy_send_timeout 30s;
+ proxy_read_timeout 30s;
+
+ # 缓冲设置
+ proxy_buffering on;
+ proxy_buffer_size 4k;
+ proxy_buffers 8 4k;
+ proxy_busy_buffers_size 8k;
+ }
+
+ # 健康检查端点
+ location /health {
+ access_log off;
+ return 200 "healthy\n";
+ add_header Content-Type text/plain;
+ }
+
+ # 状态监控页面
+ location /nginx_status {
+ stub_status on;
+ access_log off;
+ allow 127.0.0.1;
+ deny all;
+ }
+ }
+
+ # 监控和日志配置
+ server {
+ listen 8080;
+ server_name localhost;
+
+ location / {
+ return 200 "Nginx Load Balancer Status\n";
+ add_header Content-Type text/plain;
+ }
+
+ location /status {
+ stub_status on;
+ access_log off;
+ }
+ }
+}
diff --git a/docs/部署文档/杭州第一技师部署文档/start-extend-services.sh b/docs/部署文档/杭州第一技师部署文档/start-extend-services.sh
new file mode 100644
index 00000000..f15f39a9
--- /dev/null
+++ b/docs/部署文档/杭州第一技师部署文档/start-extend-services.sh
@@ -0,0 +1,368 @@
+#!/bin/bash
+
+# 扩展服务启动停止一体化脚本
+# 使用方法: ./start-extend-services.sh [start|stop|restart|status] [service_name]
+# 服务名称: monitor, snailjob, all
+
+# 配置变量
+MONITOR_JAR="ruoyi-monitor-admin.jar"
+SNAILJOB_JAR="ruoyi-snailjob-server.jar"
+JAVA_OPTS="-Xms2048m -Xmx2048m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m"
+JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai"
+JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
+JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/"
+
+# 服务配置
+declare -A SERVICES
+SERVICES[monitor]="9090:ruoyi-monitor-admin:prod"
+SERVICES[snailjob]="8800:ruoyi-snailjob-server:prod"
+
+# 日志目录
+LOG_DIR="logs"
+mkdir -p $LOG_DIR
+
+# 颜色输出
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# 打印带颜色的消息
+print_message() {
+ local color=$1
+ local message=$2
+ echo -e "${color}${message}${NC}"
+}
+
+# 检查JAR文件是否存在
+check_jar() {
+ local service=$1
+ local jar_file=""
+
+ case $service in
+ monitor)
+ jar_file="./$MONITOR_JAR"
+ ;;
+ snailjob)
+ jar_file="./$SNAILJOB_JAR"
+ ;;
+ esac
+
+ if [ ! -f "$jar_file" ]; then
+ print_message $RED "错误: $jar_file 文件不存在!"
+ print_message $YELLOW "请先编译项目: mvn clean package -Pprod"
+ return 1
+ fi
+ return 0
+}
+
+# 启动单个服务
+start_service() {
+ local service=$1
+ local config=${SERVICES[$service]}
+ local port=$(echo $config | cut -d: -f1)
+ local app_name=$(echo $config | cut -d: -f2)
+ local profile=$(echo $config | cut -d: -f3)
+
+ local pid_file="logs/${app_name}.pid"
+ local log_file="logs/${app_name}.log"
+
+ # 检查是否已经运行
+ if [ -f "$pid_file" ]; then
+ local pid=$(cat "$pid_file")
+ if ps -p $pid > /dev/null 2>&1; then
+ print_message $YELLOW "服务 $service 已经在运行中 (PID: $pid)"
+ return
+ else
+ # PID文件存在但进程不存在,清理PID文件
+ rm -f "$pid_file"
+ fi
+ fi
+
+ # 检查JAR文件
+ if ! check_jar $service; then
+ return 1
+ fi
+
+ print_message $GREEN "正在启动服务 $service (端口: $port, 配置文件: $profile)..."
+
+ # 启动应用
+ case $service in
+ monitor)
+ local jar_path="../ruoyi-extend/ruoyi-monitor-admin/target/$MONITOR_JAR"
+ nohup java $JAVA_OPTS \
+ -Dspring.profiles.active=$profile \
+ -Dserver.port=$port \
+ -jar "$jar_path" \
+ > "$log_file" 2>&1 &
+ ;;
+ snailjob)
+ local jar_path="../ruoyi-extend/ruoyi-snailjob-server/target/$SNAILJOB_JAR"
+ nohup java $JAVA_OPTS \
+ -Dspring.profiles.active=$profile \
+ -Dserver.port=$port \
+ -jar "$jar_path" \
+ > "$log_file" 2>&1 &
+ ;;
+ esac
+
+ local new_pid=$!
+ echo $new_pid > "$pid_file"
+
+ # 等待启动
+ sleep 5
+
+ # 检查启动状态
+ if ps -p $new_pid > /dev/null 2>&1; then
+ print_message $GREEN "服务 $service 启动成功 (PID: $new_pid)"
+ print_message $GREEN "访问地址: http://localhost:$port"
+ print_message $GREEN "日志文件: $log_file"
+
+ # 显示服务特定信息
+ case $service in
+ monitor)
+ print_message $BLUE "监控中心: http://localhost:$port/admin"
+ ;;
+ snailjob)
+ print_message $BLUE "任务调度: http://localhost:$port/snail-job"
+ ;;
+ esac
+ else
+ print_message $RED "服务 $service 启动失败,请检查日志文件: $log_file"
+ rm -f "$pid_file"
+ fi
+}
+
+# 停止单个服务
+stop_service() {
+ local service=$1
+ local config=${SERVICES[$service]}
+ local app_name=$(echo $config | cut -d: -f2)
+ local pid_file="logs/${app_name}.pid"
+
+ if [ -f "$pid_file" ]; then
+ local pid=$(cat "$pid_file")
+ if ps -p $pid > /dev/null 2>&1; then
+ print_message $YELLOW "正在停止服务 $service (PID: $pid)..."
+ kill $pid
+
+ # 等待进程结束
+ local count=0
+ while ps -p $pid > /dev/null 2>&1 && [ $count -lt 30 ]; do
+ sleep 1
+ count=$((count + 1))
+ done
+
+ # 强制杀死进程
+ if ps -p $pid > /dev/null 2>&1; then
+ print_message $YELLOW "强制停止服务 $service..."
+ kill -9 $pid
+ fi
+
+ rm -f "$pid_file"
+ print_message $GREEN "服务 $service 已停止"
+ else
+ print_message $YELLOW "服务 $service 未运行"
+ rm -f "$pid_file"
+ fi
+ else
+ print_message $YELLOW "服务 $service 未运行"
+ fi
+}
+
+# 重启单个服务
+restart_service() {
+ local service=$1
+ print_message $YELLOW "重启服务 $service..."
+ stop_service $service
+ sleep 3
+ start_service $service
+}
+
+# 查看服务状态
+status_service() {
+ local service=$1
+ local config=${SERVICES[$service]}
+ local port=$(echo $config | cut -d: -f1)
+ local app_name=$(echo $config | cut -d: -f2)
+ local pid_file="logs/${app_name}.pid"
+
+ if [ -f "$pid_file" ]; then
+ local pid=$(cat "$pid_file")
+ if ps -p $pid > /dev/null 2>&1; then
+ local memory=$(ps -o rss= -p $pid | awk '{print $1/1024 " MB"}')
+ local uptime=$(ps -o etime= -p $pid)
+ print_message $GREEN "服务 $service: 运行中 (PID: $pid, 端口: $port, 内存: $memory, 运行时间: $uptime)"
+ else
+ print_message $RED "服务 $service: 异常 (PID文件存在但进程不存在)"
+ rm -f "$pid_file"
+ fi
+ else
+ print_message $YELLOW "服务 $service: 未运行 (端口: $port)"
+ fi
+}
+
+# 启动所有服务
+start_all() {
+ print_message $GREEN "启动所有扩展服务..."
+ for service in "${!SERVICES[@]}"; do
+ start_service $service
+ sleep 3
+ done
+}
+
+# 停止所有服务
+stop_all() {
+ print_message $YELLOW "停止所有扩展服务..."
+ for service in "${!SERVICES[@]}"; do
+ stop_service $service
+ done
+}
+
+# 重启所有服务
+restart_all() {
+ print_message $YELLOW "重启所有扩展服务..."
+ stop_all
+ sleep 5
+ start_all
+}
+
+# 查看所有服务状态
+status_all() {
+ print_message $GREEN "所有扩展服务状态:"
+ for service in "${!SERVICES[@]}"; do
+ status_service $service
+ done
+}
+
+# 编译项目
+build_project() {
+ print_message $BLUE "开始编译扩展服务项目..."
+
+ # 编译monitor-admin
+ print_message $YELLOW "编译 ruoyi-monitor-admin..."
+ cd ../ruoyi-extend/ruoyi-monitor-admin
+ mvn clean package -Pprod
+ if [ $? -eq 0 ]; then
+ print_message $GREEN "ruoyi-monitor-admin 编译成功"
+ else
+ print_message $RED "ruoyi-monitor-admin 编译失败"
+ return 1
+ fi
+
+ # 编译snailjob-server
+ print_message $YELLOW "编译 ruoyi-snailjob-server..."
+ cd ../ruoyi-snailjob-server
+ mvn clean package -Pprod
+ if [ $? -eq 0 ]; then
+ print_message $GREEN "ruoyi-snailjob-server 编译成功"
+ else
+ print_message $RED "ruoyi-snailjob-server 编译失败"
+ return 1
+ fi
+
+ # 返回原目录
+ cd ../../部署文档/杭州第一技师部署文档
+ print_message $GREEN "所有扩展服务编译完成"
+}
+
+# 显示帮助信息
+show_help() {
+ echo "扩展服务启动停止一体化脚本使用方法:"
+ echo " $0 [命令] [服务名称]"
+ echo ""
+ echo "命令:"
+ echo " start 启动服务"
+ echo " stop 停止服务"
+ echo " restart 重启服务"
+ echo " status 查看服务状态"
+ echo " build 编译项目"
+ echo ""
+ echo "服务名称:"
+ echo " monitor 启动监控中心服务 (端口: 9090)"
+ echo " snailjob 启动任务调度服务 (端口: 8800)"
+ echo " all 操作所有服务"
+ echo ""
+ echo "示例:"
+ echo " $0 start monitor # 启动监控中心服务"
+ echo " $0 start snailjob # 启动任务调度服务"
+ echo " $0 start all # 启动所有服务"
+ echo " $0 stop all # 停止所有服务"
+ echo " $0 status all # 查看所有服务状态"
+ echo " $0 build # 编译所有扩展服务"
+ echo ""
+ echo "服务访问地址:"
+ echo " 监控中心: http://localhost:9090/admin"
+ echo " 任务调度: http://localhost:8800/snail-job"
+}
+
+# 主函数
+main() {
+ local action=$1
+ local service=$2
+
+ # 检查参数
+ if [ -z "$action" ]; then
+ show_help
+ exit 1
+ fi
+
+ case $action in
+ start)
+ if [ "$service" = "all" ]; then
+ start_all
+ elif [ -n "${SERVICES[$service]}" ]; then
+ start_service $service
+ else
+ print_message $RED "无效的服务名称: $service"
+ show_help
+ exit 1
+ fi
+ ;;
+ stop)
+ if [ "$service" = "all" ]; then
+ stop_all
+ elif [ -n "${SERVICES[$service]}" ]; then
+ stop_service $service
+ else
+ print_message $RED "无效的服务名称: $service"
+ show_help
+ exit 1
+ fi
+ ;;
+ restart)
+ if [ "$service" = "all" ]; then
+ restart_all
+ elif [ -n "${SERVICES[$service]}" ]; then
+ restart_service $service
+ else
+ print_message $RED "无效的服务名称: $service"
+ show_help
+ exit 1
+ fi
+ ;;
+ status)
+ if [ "$service" = "all" ]; then
+ status_all
+ elif [ -n "${SERVICES[$service]}" ]; then
+ status_service $service
+ else
+ print_message $RED "无效的服务名称: $service"
+ show_help
+ exit 1
+ fi
+ ;;
+ build)
+ build_project
+ ;;
+ *)
+ print_message $RED "无效的命令: $action"
+ show_help
+ exit 1
+ ;;
+ esac
+}
+
+# 执行主函数
+main "$@"
\ No newline at end of file
diff --git a/docs/部署文档/杭州第一技师部署文档/start-multi-instance.bat b/docs/部署文档/杭州第一技师部署文档/start-multi-instance.bat
new file mode 100644
index 00000000..a3ad321f
--- /dev/null
+++ b/docs/部署文档/杭州第一技师部署文档/start-multi-instance.bat
@@ -0,0 +1,296 @@
+@echo off
+chcp 65001 >nul
+setlocal enabledelayedexpansion
+
+REM 多实例启动脚本 (Windows版本)
+REM 使用方法: start-multi-instance.bat [start|stop|restart|status] [instance_name]
+REM 实例名称: 9900, 9901, 9902, 9903, all
+
+REM 配置变量
+set JAR_NAME=ruoyi-admin.jar
+set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
+set JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
+set JAVA_OPTS=%JAVA_OPTS% -XX:+UseG1GC -XX:MaxGCPauseMillis=200
+set JAVA_OPTS=%JAVA_OPTS% -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs\
+
+REM 日志目录
+if not exist logs mkdir logs
+
+REM 颜色输出函数
+:print_message
+set color=%1
+set message=%2
+if "%color%"=="RED" (
+ echo [31m%message%[0m
+) else if "%color%"=="GREEN" (
+ echo [32m%message%[0m
+) else if "%color%"=="YELLOW" (
+ echo [33m%message%[0m
+) else (
+ echo %message%
+)
+goto :eof
+
+REM 检查JAR文件是否存在
+:check_jar
+if not exist "%JAR_NAME%" (
+ call :print_message RED "错误: %JAR_NAME% 文件不存在!"
+ call :print_message YELLOW "请确保在正确的目录下运行此脚本,或先执行 mvn clean package -Pprod 编译项目"
+ pause
+ exit /b 1
+)
+goto :eof
+
+REM 启动单个实例
+:start_instance
+set port=%1
+set profile=%2
+set pid_file=logs\ruoyi-admin-%port%.pid
+set log_file=logs\ruoyi-admin-%port%.log
+
+REM 检查是否已经运行
+if exist "%pid_file%" (
+ for /f "tokens=*" %%i in (%pid_file%) do set pid=%%i
+ tasklist /FI "PID eq !pid!" 2>nul | find /I "!pid!" >nul
+ if !errorlevel! equ 0 (
+ call :print_message YELLOW "实例 %port% 已经在运行中 (PID: !pid!)"
+ goto :eof
+ ) else (
+ REM PID文件存在但进程不存在,清理PID文件
+ del "%pid_file%" 2>nul
+ )
+)
+
+call :print_message GREEN "正在启动实例 %port% (配置文件: %profile%)..."
+
+REM 启动应用
+start /b java %JAVA_OPTS% -Dspring.profiles.active=%profile% -Dserver.port=%port% -jar %JAR_NAME% > "%log_file%" 2>&1
+
+REM 等待启动
+timeout /t 3 /nobreak >nul
+
+REM 查找新启动的进程
+for /f "tokens=2" %%i in ('tasklist /FI "IMAGENAME eq java.exe" /FO CSV ^| findstr /I "java.exe"') do (
+ set new_pid=%%i
+ set new_pid=!new_pid:"=!
+ echo !new_pid! > "%pid_file%"
+ call :print_message GREEN "实例 %port% 启动成功 (PID: !new_pid!)"
+ call :print_message GREEN "访问地址: http://localhost:%port%"
+ call :print_message GREEN "日志文件: %log_file%"
+ goto :eof
+)
+
+call :print_message RED "实例 %port% 启动失败,请检查日志文件: %log_file%"
+if exist "%pid_file%" del "%pid_file%"
+goto :eof
+
+REM 停止单个实例
+:stop_instance
+set port=%1
+set pid_file=logs\ruoyi-admin-%port%.pid
+
+if exist "%pid_file%" (
+ for /f "tokens=*" %%i in (%pid_file%) do set pid=%%i
+ tasklist /FI "PID eq !pid!" 2>nul | find /I "!pid!" >nul
+ if !errorlevel! equ 0 (
+ call :print_message YELLOW "正在停止实例 %port% (PID: !pid!)..."
+ taskkill /PID !pid! /F >nul 2>&1
+ del "%pid_file%" 2>nul
+ call :print_message GREEN "实例 %port% 已停止"
+ ) else (
+ call :print_message YELLOW "实例 %port% 未运行"
+ del "%pid_file%" 2>nul
+ )
+) else (
+ call :print_message YELLOW "实例 %port% 未运行"
+)
+goto :eof
+
+REM 重启单个实例
+:restart_instance
+set port=%1
+call :print_message YELLOW "重启实例 %port%..."
+call :stop_instance %port%
+timeout /t 2 /nobreak >nul
+if "%port%"=="9900" call :start_instance %port% prod-9900
+if "%port%"=="9901" call :start_instance %port% prod-9901
+if "%port%"=="9902" call :start_instance %port% prod-9902
+if "%port%"=="9903" call :start_instance %port% prod-9903
+goto :eof
+
+REM 查看实例状态
+:status_instance
+set port=%1
+set pid_file=logs\ruoyi-admin-%port%.pid
+
+if exist "%pid_file%" (
+ for /f "tokens=*" %%i in (%pid_file%) do set pid=%%i
+ tasklist /FI "PID eq !pid!" 2>nul | find /I "!pid!" >nul
+ if !errorlevel! equ 0 (
+ call :print_message GREEN "实例 %port%: 运行中 (PID: !pid!)"
+ ) else (
+ call :print_message RED "实例 %port%: 异常 (PID文件存在但进程不存在)"
+ del "%pid_file%" 2>nul
+ )
+) else (
+ call :print_message YELLOW "实例 %port%: 未运行"
+)
+goto :eof
+
+REM 启动所有实例
+:start_all
+call :print_message GREEN "启动所有实例..."
+call :start_instance 9900 prod-9900
+timeout /t 2 /nobreak >nul
+call :start_instance 9901 prod-9901
+timeout /t 2 /nobreak >nul
+call :start_instance 9902 prod-9902
+timeout /t 2 /nobreak >nul
+call :start_instance 9903 prod-9903
+goto :eof
+
+REM 停止所有实例
+:stop_all
+call :print_message YELLOW "停止所有实例..."
+call :stop_instance 9900
+call :stop_instance 9901
+call :stop_instance 9902
+call :stop_instance 9903
+goto :eof
+
+REM 重启所有实例
+:restart_all
+call :print_message YELLOW "重启所有实例..."
+call :stop_all
+timeout /t 3 /nobreak >nul
+call :start_all
+goto :eof
+
+REM 查看所有实例状态
+:status_all
+call :print_message GREEN "所有实例状态:"
+call :status_instance 9900
+call :status_instance 9901
+call :status_instance 9902
+call :status_instance 9903
+goto :eof
+
+REM 显示帮助信息
+:show_help
+echo 多实例启动脚本使用方法:
+echo %0 [命令] [实例名称]
+echo.
+echo 命令:
+echo start 启动实例
+echo stop 停止实例
+echo restart 重启实例
+echo status 查看实例状态
+echo.
+echo 实例名称:
+echo 9900 启动9900端口实例
+echo 9901 启动9901端口实例
+echo 9902 启动9902端口实例
+echo 9903 启动9903端口实例
+echo all 操作所有实例
+echo.
+echo 示例:
+echo %0 start 9900 # 启动9900端口实例
+echo %0 start all # 启动所有实例
+echo %0 stop 9900 # 停止9900端口实例
+echo %0 status all # 查看所有实例状态
+echo %0 restart all # 重启所有实例
+goto :eof
+
+REM 主函数
+:main
+set action=%1
+set instance=%2
+
+REM 检查参数
+if "%action%"=="" (
+ call :show_help
+ pause
+ exit /b 1
+)
+
+REM 检查JAR文件
+call :check_jar
+if !errorlevel! neq 0 goto :eof
+
+if "%action%"=="start" (
+ if "%instance%"=="all" (
+ call :start_all
+ ) else if "%instance%"=="9900" (
+ call :start_instance 9900 prod-9900
+ ) else if "%instance%"=="9901" (
+ call :start_instance 9901 prod-9901
+ ) else if "%instance%"=="9902" (
+ call :start_instance 9902 prod-9902
+ ) else if "%instance%"=="9903" (
+ call :start_instance 9903 prod-9903
+ ) else (
+ call :print_message RED "无效的实例名称: %instance%"
+ call :show_help
+ pause
+ exit /b 1
+ )
+) else if "%action%"=="stop" (
+ if "%instance%"=="all" (
+ call :stop_all
+ ) else if "%instance%"=="9900" (
+ call :stop_instance 9900
+ ) else if "%instance%"=="9901" (
+ call :stop_instance 9901
+ ) else if "%instance%"=="9902" (
+ call :stop_instance 9902
+ ) else if "%instance%"=="9903" (
+ call :stop_instance 9903
+ ) else (
+ call :print_message RED "无效的实例名称: %instance%"
+ call :show_help
+ pause
+ exit /b 1
+ )
+) else if "%action%"=="restart" (
+ if "%instance%"=="all" (
+ call :restart_all
+ ) else if "%instance%"=="9900" (
+ call :restart_instance 9900
+ ) else if "%instance%"=="9901" (
+ call :restart_instance 9901
+ ) else if "%instance%"=="9902" (
+ call :restart_instance 9902
+ ) else if "%instance%"=="9903" (
+ call :restart_instance 9903
+ ) else (
+ call :print_message RED "无效的实例名称: %instance%"
+ call :show_help
+ pause
+ exit /b 1
+ )
+) else if "%action%"=="status" (
+ if "%instance%"=="all" (
+ call :status_all
+ ) else if "%instance%"=="9900" (
+ call :status_instance 9900
+ ) else if "%instance%"=="9901" (
+ call :status_instance 9901
+ ) else if "%instance%"=="9902" (
+ call :status_instance 9902
+ ) else if "%instance%"=="9903" (
+ call :status_instance 9903
+ ) else (
+ call :print_message RED "无效的实例名称: %instance%"
+ call :show_help
+ pause
+ exit /b 1
+ )
+) else (
+ call :print_message RED "无效的命令: %action%"
+ call :show_help
+ pause
+ exit /b 1
+)
+
+pause
+goto :eof
diff --git a/docs/部署文档/杭州第一技师部署文档/start-multi-instance.sh b/docs/部署文档/杭州第一技师部署文档/start-multi-instance.sh
new file mode 100644
index 00000000..73e11c8d
--- /dev/null
+++ b/docs/部署文档/杭州第一技师部署文档/start-multi-instance.sh
@@ -0,0 +1,284 @@
+#!/bin/bash
+
+# 多实例启动脚本
+# 使用方法: ./start-multi-instance.sh [start|stop|restart|status] [instance_name]
+# 实例名称: 9900, 9901, 9902, 9903, all
+
+# 配置变量
+JAR_NAME="ruoyi-admin.jar"
+JAVA_OPTS="-Xms8192m -Xmx8192m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m"
+JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai"
+JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
+JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/"
+
+# 实例配置
+declare -A INSTANCES
+INSTANCES[9900]="prod-9900"
+INSTANCES[9901]="prod-9901"
+INSTANCES[9902]="prod-9902"
+INSTANCES[9903]="prod-9903"
+
+# 日志目录
+LOG_DIR="logs"
+mkdir -p $LOG_DIR
+
+# 颜色输出
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+# 打印带颜色的消息
+print_message() {
+ local color=$1
+ local message=$2
+ echo -e "${color}${message}${NC}"
+}
+
+# 检查JAR文件是否存在
+check_jar() {
+ if [ ! -f "$JAR_NAME" ]; then
+ print_message $RED "错误: $JAR_NAME 文件不存在!"
+ print_message $YELLOW "请确保在正确的目录下运行此脚本,或先执行 mvn clean package -Pprod 编译项目"
+ exit 1
+ fi
+}
+
+# 启动单个实例
+start_instance() {
+ local port=$1
+ local profile=$2
+
+ local pid_file="logs/ruoyi-admin-${port}.pid"
+ local log_file="logs/ruoyi-admin-${port}.log"
+
+ # 检查是否已经运行
+ if [ -f "$pid_file" ]; then
+ local pid=$(cat "$pid_file")
+ if ps -p $pid > /dev/null 2>&1; then
+ print_message $YELLOW "实例 $port 已经在运行中 (PID: $pid)"
+ return
+ else
+ # PID文件存在但进程不存在,清理PID文件
+ rm -f "$pid_file"
+ fi
+ fi
+
+ print_message $GREEN "正在启动实例 $port (配置文件: $profile)..."
+
+ # 启动应用
+ nohup java $JAVA_OPTS \
+ -Dspring.profiles.active=prod,$profile \
+ -Dserver.port=$port \
+ -jar $JAR_NAME \
+ > "$log_file" 2>&1 &
+
+ local new_pid=$!
+ echo $new_pid > "$pid_file"
+
+ # 等待启动
+ sleep 3
+
+ # 检查启动状态
+ if ps -p $new_pid > /dev/null 2>&1; then
+ print_message $GREEN "实例 $port 启动成功 (PID: $new_pid)"
+ print_message $GREEN "访问地址: http://localhost:$port"
+ print_message $GREEN "日志文件: $log_file"
+ else
+ print_message $RED "实例 $port 启动失败,请检查日志文件: $log_file"
+ rm -f "$pid_file"
+ fi
+}
+
+# 停止单个实例
+stop_instance() {
+ local port=$1
+ local pid_file="logs/ruoyi-admin-${port}.pid"
+
+ if [ -f "$pid_file" ]; then
+ local pid=$(cat "$pid_file")
+ if ps -p $pid > /dev/null 2>&1; then
+ print_message $YELLOW "正在停止实例 $port (PID: $pid)..."
+ kill $pid
+
+ # 等待进程结束
+ local count=0
+ while ps -p $pid > /dev/null 2>&1 && [ $count -lt 30 ]; do
+ sleep 1
+ count=$((count + 1))
+ done
+
+ # 强制杀死进程
+ if ps -p $pid > /dev/null 2>&1; then
+ print_message $YELLOW "强制停止实例 $port..."
+ kill -9 $pid
+ fi
+
+ rm -f "$pid_file"
+ print_message $GREEN "实例 $port 已停止"
+ else
+ print_message $YELLOW "实例 $port 未运行"
+ rm -f "$pid_file"
+ fi
+ else
+ print_message $YELLOW "实例 $port 未运行"
+ fi
+}
+
+# 重启单个实例
+restart_instance() {
+ local port=$1
+ print_message $YELLOW "重启实例 $port..."
+ stop_instance $port
+ sleep 2
+ start_instance $port ${INSTANCES[$port]}
+}
+
+# 查看实例状态
+status_instance() {
+ local port=$1
+ local pid_file="logs/ruoyi-admin-${port}.pid"
+
+ if [ -f "$pid_file" ]; then
+ local pid=$(cat "$pid_file")
+ if ps -p $pid > /dev/null 2>&1; then
+ local memory=$(ps -o rss= -p $pid | awk '{print $1/1024 " MB"}')
+ local uptime=$(ps -o etime= -p $pid)
+ print_message $GREEN "实例 $port: 运行中 (PID: $pid, 内存: $memory, 运行时间: $uptime)"
+ else
+ print_message $RED "实例 $port: 异常 (PID文件存在但进程不存在)"
+ rm -f "$pid_file"
+ fi
+ else
+ print_message $YELLOW "实例 $port: 未运行"
+ fi
+}
+
+# 启动所有实例
+start_all() {
+ print_message $GREEN "启动所有实例..."
+ for port in "${!INSTANCES[@]}"; do
+ start_instance $port ${INSTANCES[$port]}
+ sleep 2
+ done
+}
+
+# 停止所有实例
+stop_all() {
+ print_message $YELLOW "停止所有实例..."
+ for port in "${!INSTANCES[@]}"; do
+ stop_instance $port
+ done
+}
+
+# 重启所有实例
+restart_all() {
+ print_message $YELLOW "重启所有实例..."
+ stop_all
+ sleep 3
+ start_all
+}
+
+# 查看所有实例状态
+status_all() {
+ print_message $GREEN "所有实例状态:"
+ for port in "${!INSTANCES[@]}"; do
+ status_instance $port
+ done
+}
+
+# 显示帮助信息
+show_help() {
+ echo "多实例启动脚本使用方法:"
+ echo " $0 [命令] [实例名称]"
+ echo ""
+ echo "命令:"
+ echo " start 启动实例"
+ echo " stop 停止实例"
+ echo " restart 重启实例"
+ echo " status 查看实例状态"
+ echo ""
+ echo "实例名称:"
+ echo " 9900 启动9900端口实例"
+ echo " 9901 启动9901端口实例"
+ echo " 9902 启动9902端口实例"
+ echo " 9903 启动9903端口实例"
+ echo " all 操作所有实例"
+ echo ""
+ echo "示例:"
+ echo " $0 start 9900 # 启动9900端口实例"
+ echo " $0 start all # 启动所有实例"
+ echo " $0 stop 9900 # 停止9900端口实例"
+ echo " $0 status all # 查看所有实例状态"
+ echo " $0 restart all # 重启所有实例"
+}
+
+# 主函数
+main() {
+ local action=$1
+ local instance=$2
+
+ # 检查参数
+ if [ -z "$action" ]; then
+ show_help
+ exit 1
+ fi
+
+ # 检查JAR文件
+ check_jar
+
+ case $action in
+ start)
+ if [ "$instance" = "all" ]; then
+ start_all
+ elif [ -n "${INSTANCES[$instance]}" ]; then
+ start_instance $instance ${INSTANCES[$instance]}
+ else
+ print_message $RED "无效的实例名称: $instance"
+ show_help
+ exit 1
+ fi
+ ;;
+ stop)
+ if [ "$instance" = "all" ]; then
+ stop_all
+ elif [ -n "${INSTANCES[$instance]}" ]; then
+ stop_instance $instance
+ else
+ print_message $RED "无效的实例名称: $instance"
+ show_help
+ exit 1
+ fi
+ ;;
+ restart)
+ if [ "$instance" = "all" ]; then
+ restart_all
+ elif [ -n "${INSTANCES[$instance]}" ]; then
+ restart_instance $instance
+ else
+ print_message $RED "无效的实例名称: $instance"
+ show_help
+ exit 1
+ fi
+ ;;
+ status)
+ if [ "$instance" = "all" ]; then
+ status_all
+ elif [ -n "${INSTANCES[$instance]}" ]; then
+ status_instance $instance
+ else
+ print_message $RED "无效的实例名称: $instance"
+ show_help
+ exit 1
+ fi
+ ;;
+ *)
+ print_message $RED "无效的命令: $action"
+ show_help
+ exit 1
+ ;;
+ esac
+}
+
+# 执行主函数
+main "$@"
diff --git a/docs/部署文档/杭州第一技师部署文档/test-config-fix.sh b/docs/部署文档/杭州第一技师部署文档/test-config-fix.sh
new file mode 100644
index 00000000..8939d900
--- /dev/null
+++ b/docs/部署文档/杭州第一技师部署文档/test-config-fix.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+# 配置修复验证脚本
+# 使用方法: ./test-config-fix.sh
+
+# 颜色输出
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+print_message() {
+ local color=$1
+ local message=$2
+ echo -e "${color}${message}${NC}"
+}
+
+print_message $GREEN "=== 多实例配置修复验证脚本 ==="
+
+# 检查JAR文件
+JAR_NAME="ruoyi-admin.jar"
+if [ ! -f "$JAR_NAME" ]; then
+ print_message $RED "错误: $JAR_NAME 文件不存在!"
+ print_message $YELLOW "请先执行 mvn clean package -Pprod 编译项目"
+ exit 1
+fi
+
+print_message $GREEN "✓ JAR文件存在"
+
+# 检查配置文件
+CONFIG_FILES=(
+ "backend/ruoyi-admin/src/main/resources/application-prod-9900.yml"
+ "backend/ruoyi-admin/src/main/resources/application-prod-9901.yml"
+ "backend/ruoyi-admin/src/main/resources/application-prod-9902.yml"
+ "backend/ruoyi-admin/src/main/resources/application-prod-9903.yml"
+)
+
+for config_file in "${CONFIG_FILES[@]}"; do
+ if [ -f "$config_file" ]; then
+ # 检查是否包含spring.profiles.include
+ if grep -q "spring.profiles.include" "$config_file"; then
+ print_message $RED "✗ $config_file 仍包含 spring.profiles.include"
+ else
+ print_message $GREEN "✓ $config_file 配置正确"
+ fi
+ else
+ print_message $RED "✗ $config_file 文件不存在"
+ fi
+done
+
+# 检查启动脚本
+START_SCRIPT="docs/部署文档/杭州第一技师部署文档/start-multi-instance.sh"
+if [ -f "$START_SCRIPT" ]; then
+ if grep -q "spring.profiles.active=prod,\$profile" "$START_SCRIPT"; then
+ print_message $GREEN "✓ 启动脚本配置正确"
+ else
+ print_message $RED "✗ 启动脚本配置错误"
+ fi
+else
+ print_message $RED "✗ 启动脚本不存在"
+fi
+
+print_message $GREEN ""
+print_message $GREEN "=== 验证完成 ==="
+print_message $YELLOW "如果所有检查都通过,可以尝试启动实例:"
+print_message $YELLOW " ./start-multi-instance.sh start 9900"
\ No newline at end of file
diff --git a/docs/部署文档/杭州第一技师部署文档/多实例启动配置修复说明.md b/docs/部署文档/杭州第一技师部署文档/多实例启动配置修复说明.md
new file mode 100644
index 00000000..9b1a73a7
--- /dev/null
+++ b/docs/部署文档/杭州第一技师部署文档/多实例启动配置修复说明.md
@@ -0,0 +1,96 @@
+# 多实例启动配置修复说明
+
+## 问题描述
+
+在使用 `start-multi-instance.sh` 启动实例时,出现以下错误:
+
+```
+org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property 'spring.profiles.include' imported from location 'class path resource [application-prod-9900.yml]' is invalid in a profile specific resource
+```
+
+## 问题原因
+
+在Spring Boot 2.4+版本中,`spring.profiles.include` 属性不能在profile-specific的配置文件中使用(如 `application-prod-9900.yml`)。这个属性只能在主配置文件(`application.yml`)中使用。
+
+## 修复方案
+
+### 1. 修改配置文件结构
+
+移除了所有实例配置文件中的 `spring.profiles.include: prod` 配置:
+
+**修复前:**
+```yaml
+# application-prod-9900.yml
+spring:
+ profiles:
+ include: prod # ❌ 这在profile-specific文件中不被允许
+```
+
+**修复后:**
+```yaml
+# application-prod-9900.yml
+spring:
+ application:
+ name: RuoYi-Vue-Plus-9900 # ✅ 直接配置应用名称
+```
+
+### 2. 修改启动脚本
+
+修改启动脚本,在启动时同时激活 `prod` 和具体的实例profile:
+
+**修复前:**
+```bash
+-Dspring.profiles.active=$profile # 只激活 prod-9900
+```
+
+**修复后:**
+```bash
+-Dspring.profiles.active=prod,$profile # 同时激活 prod 和 prod-9900
+```
+
+## 修复的文件
+
+1. `backend/ruoyi-admin/src/main/resources/application-prod-9900.yml`
+2. `backend/ruoyi-admin/src/main/resources/application-prod-9901.yml`
+3. `backend/ruoyi-admin/src/main/resources/application-prod-9902.yml`
+4. `backend/ruoyi-admin/src/main/resources/application-prod-9903.yml`
+5. `docs/部署文档/杭州第一技师部署文档/start-multi-instance.sh`
+
+## 配置加载顺序
+
+修复后的配置加载顺序:
+
+1. `application.yml` (主配置文件)
+2. `application-prod.yml` (生产环境基础配置)
+3. `application-prod-9900.yml` (9900端口实例特定配置)
+
+## 验证方法
+
+1. 重新编译项目:
+ ```bash
+ mvn clean package -Pprod
+ ```
+
+2. 启动单个实例测试:
+ ```bash
+ ./start-multi-instance.sh start 9900
+ ```
+
+3. 检查启动日志,确认没有配置错误
+
+4. 访问应用验证功能正常:
+ ```bash
+ curl http://localhost:9900
+ ```
+
+## 注意事项
+
+1. 确保在修改配置文件后重新编译项目
+2. 如果之前有实例在运行,需要先停止再重新启动
+3. 检查日志文件确认配置加载正确
+4. 验证监控中心和SnailJob配置是否正常工作
+
+## 相关文档
+
+- [Spring Boot Profile配置最佳实践](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.files.profile-specific)
+- [多实例部署指南](../README.md)
\ No newline at end of file
diff --git a/docs/部署文档/杭州第一技师部署文档/多实例部署说明.md b/docs/部署文档/杭州第一技师部署文档/多实例部署说明.md
new file mode 100644
index 00000000..09af58de
--- /dev/null
+++ b/docs/部署文档/杭州第一技师部署文档/多实例部署说明.md
@@ -0,0 +1,293 @@
+# 🚀 RuoYi-Admin 多实例部署说明
+
+## 📋 概述
+
+本文档说明如何在生产环境中部署多个 `ruoyi-admin.jar` 实例到不同端口,实现负载均衡和高可用。
+
+## 🏗️ 部署架构
+
+```
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ 前端应用 │ │ Nginx │ │ 后端实例 │
+│ │ │ 负载均衡器 │ │ │
+│ http://80 │───▶│ http://80 │───▶│ 9900, 9901 │
+│ │ │ │ │ 9902, 9903 │
+└─────────────────┘ └─────────────────┘ └─────────────────┘
+```
+
+## 📁 文件结构
+
+```
+backend/
+├── ruoyi-admin.jar # 编译后的JAR包
+├── application-prod-9900.yml # 9900端口配置文件
+├── application-prod-9901.yml # 9901端口配置文件
+├── application-prod-9902.yml # 9902端口配置文件
+├── application-prod-9903.yml # 9903端口配置文件
+├── start-multi-instance.sh # Linux启动脚本
+├── start-multi-instance.bat # Windows启动脚本
+├── docker-compose-multi.yml # Docker部署配置
+├── nginx.conf # Nginx负载均衡配置
+└── logs/ # 日志目录
+ ├── ruoyi-admin-9900.log
+ ├── ruoyi-admin-9901.log
+ ├── ruoyi-admin-9902.log
+ └── ruoyi-admin-9903.log
+```
+
+## 🔧 部署步骤
+
+### 1. 编译项目
+
+```bash
+# 进入backend目录
+cd backend
+
+# 编译生产环境JAR包
+mvn clean package -Pprod
+
+# 检查生成的JAR文件
+ls -la ruoyi-admin/target/ruoyi-admin.jar
+```
+
+### 2. 准备部署文件
+
+```bash
+# 复制JAR文件到部署目录
+cp ruoyi-admin/target/ruoyi-admin.jar ./
+
+# 创建日志目录
+mkdir -p logs
+
+# 设置脚本执行权限 (Linux)
+chmod +x start-multi-instance.sh
+```
+
+### 3. 启动多实例
+
+#### Linux环境
+
+```bash
+# 启动所有实例
+./start-multi-instance.sh start all
+
+# 启动单个实例
+./start-multi-instance.sh start 9900
+./start-multi-instance.sh start 9901
+./start-multi-instance.sh start 9902
+./start-multi-instance.sh start 9903
+
+# 查看实例状态
+./start-multi-instance.sh status all
+
+# 停止实例
+./start-multi-instance.sh stop all
+
+# 重启实例
+./start-multi-instance.sh restart all
+```
+
+#### Windows环境
+
+```cmd
+# 启动所有实例
+start-multi-instance.bat start all
+
+# 启动单个实例
+start-multi-instance.bat start 9900
+start-multi-instance.bat start 9901
+start-multi-instance.bat start 9902
+start-multi-instance.bat start 9903
+
+# 查看实例状态
+start-multi-instance.bat status all
+
+# 停止实例
+start-multi-instance.bat stop all
+
+# 重启实例
+start-multi-instance.bat restart all
+```
+
+### 4. Docker部署 (可选)
+
+```bash
+# 启动所有服务
+docker-compose -f docker-compose-multi.yml up -d
+
+# 查看服务状态
+docker-compose -f docker-compose-multi.yml ps
+
+# 查看日志
+docker-compose -f docker-compose-multi.yml logs -f
+
+# 停止服务
+docker-compose -f docker-compose-multi.yml down
+```
+
+## 🌐 访问地址
+
+### 直接访问各实例
+- **实例1**: http://localhost:9900
+- **实例2**: http://localhost:9901
+- **实例3**: http://localhost:9902
+- **实例4**: http://localhost:9903
+
+### 通过负载均衡器访问
+- **主入口**: http://localhost (Nginx负载均衡)
+- **监控页面**: http://localhost:8080/status
+
+## 📊 监控和管理
+
+### 1. 实例状态监控
+
+```bash
+# 查看所有实例状态
+./start-multi-instance.sh status all
+
+# 查看单个实例状态
+./start-multi-instance.sh status 9900
+```
+
+### 2. 日志监控
+
+```bash
+# 查看实时日志
+tail -f logs/ruoyi-admin-9900.log
+tail -f logs/ruoyi-admin-9901.log
+tail -f logs/ruoyi-admin-9902.log
+tail -f logs/ruoyi-admin-9903.log
+
+# 查看错误日志
+grep "ERROR" logs/ruoyi-admin-*.log
+```
+
+### 3. 进程监控
+
+```bash
+# 查看Java进程
+ps aux | grep java
+
+# 查看端口占用
+netstat -tlnp | grep :99
+```
+
+## ⚠️ 注意事项
+
+### 1. 端口冲突
+- 确保9900-9903端口未被其他服务占用
+- 检查防火墙设置,确保端口可访问
+
+### 2. 内存配置
+- 每个实例默认配置:最小512MB,最大1024MB
+- 根据服务器配置调整 `JAVA_OPTS` 参数
+
+### 3. 数据库连接
+- 所有实例共享同一个数据库
+- 确保数据库连接池配置合理,避免连接数过多
+
+### 4. 文件上传
+- 临时文件目录:`/ruoyi/server/temp`
+- 确保目录存在且有写权限
+
+## 🔄 故障排查
+
+### 1. 实例启动失败
+
+```bash
+# 检查JAR文件是否存在
+ls -la ruoyi-admin.jar
+
+# 检查端口是否被占用
+netstat -tlnp | grep :99
+
+# 查看启动日志
+tail -f logs/ruoyi-admin-9900.log
+```
+
+### 2. 实例无法访问
+
+```bash
+# 检查防火墙设置
+firewall-cmd --list-ports
+
+# 检查SELinux状态 (CentOS/RHEL)
+getenforce
+
+# 测试端口连通性
+telnet localhost 9900
+```
+
+### 3. 负载均衡不工作
+
+```bash
+# 检查Nginx状态
+docker-compose -f docker-compose-multi.yml ps nginx
+
+# 查看Nginx日志
+docker-compose -f docker-compose-multi.yml logs nginx
+
+# 测试后端服务
+curl http://localhost:9900/health
+```
+
+## 📈 性能优化建议
+
+### 1. JVM调优
+```bash
+# 根据服务器内存调整JVM参数
+JAVA_OPTS="-Xms2g -Xmx4g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
+```
+
+### 2. 数据库连接池优化
+```yaml
+# 在application-prod-*.yml中调整
+spring:
+ datasource:
+ hikari:
+ maxPoolSize: 100 # 根据实例数调整
+ minIdle: 20
+ connectionTimeout: 30000
+```
+
+### 3. 缓存配置
+```yaml
+# Redis连接池优化
+redisson:
+ threads: 32
+ nettyThreads: 64
+ singleServerConfig:
+ connectionPoolSize: 128
+```
+
+## 🚀 扩展部署
+
+### 1. 添加更多实例
+- 复制配置文件模板
+- 修改端口号和应用名称
+- 更新启动脚本和Docker配置
+
+### 2. 集群部署
+- 使用Redis集群
+- 配置数据库主从复制
+- 实现会话共享
+
+### 3. 容器化部署
+- 使用Kubernetes管理
+- 配置自动扩缩容
+- 实现滚动更新
+
+## 📞 技术支持
+
+如遇到部署问题,请检查:
+1. 系统日志和应用日志
+2. 端口占用情况
+3. 防火墙和网络配置
+4. 数据库连接状态
+5. 内存和磁盘空间
+
+---
+
+**📝 文档版本**: v1.0
+**🔄 最后更新**: 2025-01-03
+**👨💻 维护人员**: 开发团队
\ No newline at end of file
diff --git a/docs/问题解决方案/SnailJob命名空间配置修复方案.md b/docs/问题解决方案/SnailJob命名空间配置修复方案.md
new file mode 100644
index 00000000..95cf0a36
--- /dev/null
+++ b/docs/问题解决方案/SnailJob命名空间配置修复方案.md
@@ -0,0 +1,96 @@
+# SnailJob命名空间配置修复方案
+
+## 问题描述
+
+四个ruoyi-admin实例启动后都提示SnailJob Token认证失败:
+
+```
+2025-08-31 01:44:09 [snail-job-grpc-client-executor-172.16.0.67-1216] ERROR c.a.s.c.c.handler.ClientRegister
+ - heartbeat check requestId:[1216] message:[Token authentication failed. [namespace:prod,prod-9900 groupName:ruoyi_group_9900 token:SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT]]
+```
+
+## 问题分析
+
+### 根本原因
+1. **命名空间配置不匹配**:客户端尝试连接的命名空间是 `prod,prod-9900`,但数据库中的命名空间只有 `prod`
+2. **动态配置问题**:配置文件使用 `${spring.profiles.active}` 作为命名空间,导致9900-9903实例的命名空间变成了 `prod-9900`, `prod-9901` 等
+3. **数据库数据缺失**:数据库中缺少对应的命名空间记录
+
+### 配置对比
+
+**数据库中的命名空间:**
+```sql
+1 Development dev
+2 Production prod
+```
+
+**客户端尝试连接的命名空间:**
+- prod,prod-9900
+- prod,prod-9901
+- prod,prod-9902
+- prod,prod-9903
+
+## 解决方案
+
+### 方案一:修改配置文件(已实施)
+
+将所有实例的命名空间统一为 `prod`,而不是使用 `${spring.profiles.active}`:
+
+**修改的文件:**
+- `application-prod-9900.yml`
+- `application-prod-9901.yml`
+- `application-prod-9902.yml`
+- `application-prod-9903.yml`
+
+**修改内容:**
+```yaml
+snail-job:
+ namespace: "prod" # 从 ${spring.profiles.active} 改为固定值
+```
+
+### 方案二:数据库方案(备选)
+
+如果希望保持动态命名空间配置,可以在数据库中创建对应的命名空间:
+
+```sql
+-- 为每个实例创建对应的命名空间
+INSERT INTO `sj_namespace` VALUES (3, 'Production-9900', 'prod-9900', '', 0, now(), now());
+INSERT INTO `sj_namespace` VALUES (4, 'Production-9901', 'prod-9901', '', 0, now(), now());
+INSERT INTO `sj_namespace` VALUES (5, 'Production-9902', 'prod-9902', '', 0, now(), now());
+INSERT INTO `sj_namespace` VALUES (6, 'Production-9903', 'prod-9903', '', 0, now(), now());
+
+-- 为每个命名空间创建对应的组配置
+INSERT INTO `sj_group_config` VALUES (13, 'prod-9900', 'ruoyi_group_9900', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, now(), now());
+INSERT INTO `sj_group_config` VALUES (14, 'prod-9901', 'ruoyi_group_9901', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, now(), now());
+INSERT INTO `sj_group_config` VALUES (15, 'prod-9902', 'ruoyi_group_9902', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, now(), now());
+INSERT INTO `sj_group_config` VALUES (16, 'prod-9903', 'ruoyi_group_9903', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, now(), now());
+```
+
+## 验证步骤
+
+1. **重启应用实例**
+ ```bash
+ # 重启所有实例
+ ./hot-start.sh
+ ```
+
+2. **检查日志**
+ - 确认不再出现Token认证失败错误
+ - 确认SnailJob客户端成功注册
+
+3. **验证连接**
+ - 访问SnailJob管理界面
+ - 确认所有实例都显示为在线状态
+
+## 最佳实践建议
+
+1. **命名空间管理**:建议使用统一的命名空间(如 `prod`),避免动态配置导致的复杂性
+2. **组名管理**:使用有意义的组名,便于识别和管理
+3. **配置验证**:在部署前验证配置文件与数据库配置的一致性
+4. **监控告警**:配置SnailJob连接状态的监控告警
+
+## 相关文档
+
+- [SnailJob官方文档](https://github.com/aizuda/snail-job)
+- [项目部署文档](../部署文档/)
+- [数据库设计文档](../设计文档/database/)
\ No newline at end of file
diff --git a/web/src/views/xg/xsjcxx/studentMsg/studentMsg/index.vue b/web/src/views/xg/xsjcxx/studentMsg/studentMsg/index.vue
index beb6612b..545330e1 100644
--- a/web/src/views/xg/xsjcxx/studentMsg/studentMsg/index.vue
+++ b/web/src/views/xg/xsjcxx/studentMsg/studentMsg/index.vue
@@ -2652,7 +2652,7 @@ const handleAdd = async () => {
form.majorName = classData.major_name || classData.majorName;
// 确保学历层次为数字类型,避免类型不匹配导致的显示问题
const classType = classData.class_type || classData.classType;
- form.academicLevel = classType ? Number(classType) : undefined;
+ form.academicLevel = classType ? classType : undefined;
form.grade = classData.grade;
form.enrollmentYear = classData.enrollment_year || classData.enrollmentYear;
form.classTeacherId = classData.class_teacher_id || classData.classTeacherId;