Files
senmesh-zc/backend/docs/生产部署说明.md

221 lines
8.5 KiB
Markdown
Raw Permalink Normal View History

# s-file 生产部署 / 上线说明
> 适用版本:2026-09-14(含用户启停/注销、机构账号查询、手机号查记录、机构上传配置、禁用踢下线黑名单)
> 构建产物:`target/s-file.jar`(Spring Boot 可执行 fat jar,需 JDK 8)
---
## 0. 上线前必须处理(阻断项)
### 0.1 替换 spire.pdf 占位 jar ⚠️
本机构开发机的 `~/.m2/repository/e-iceblue/spire.pdf/3.7.4/` 中目前安装的是**编译级 stub(空实现)jar**——它能保证编译打包通过,但 **PDF 盖章功能(`PdfUtils.pdfStampPerPage`)运行时必然失败**。
上线前必须:
1. 从公司私服获取真实的 `spire.pdf-3.7.4.jar`(阿里云与 e-iceblue 官方仓库均无此版本);
2. 覆盖 `~/.m2/repository/e-iceblue/spire.pdf/3.7.4/spire.pdf-3.7.4.jar`(pom 同步覆盖);
3. 重新 `mvn clean package -DskipTests`;
4. 验证:解包 jar 中 `com/spire/pdf/PdfDocument.class` 应为真实实现(stub 版仅 300~500 字节)。
> 若该业务暂时不用 PDF 盖章,可带 stub 上线,但需明确告知团队。
### 0.2 提供生产配置文件
**仓库内不含任何运行配置**(application.yml 是外置的)。启动时通过 `spring.config.additional-location` 挂载。生产模板如下(按实际环境修改):
```yaml
server:
port: 8080
spring:
application:
name: s-file
datasource:
# 生产为达梦 DM8;驱动 com.dameng.jdbc.driver(pom 已含依赖)
url: jdbc:dm://<DB_HOST>:5236/S_FILE
driver-class-name: com.dameng.jdbc.driver
username: <DB_USER>
password: <DB_PASSWORD>
druid:
initial-size: 5
min-idle: 5
max-active: 20
validation-query: SELECT 1
redis: # 必须可用:登录缓存 + 禁用/注销踢下线黑名单依赖 Redis
host: <REDIS_HOST>
port: 6379
timeout: 5000
rabbitmq:
host: <MQ_HOST>
port: 5672
username: <MQ_USER>
password: <MQ_PASSWORD>
servlet:
multipart:
max-file-size: 500MB # 按网盘需求调整
max-request-size: 500MB
main:
allow-bean-definition-overriding: true
mybatis-plus:
mapper-locations: classpath*:com/senmesh/**/mapper/*.xml # 必须有,否则自定义 XML 的接口全部报 Invalid bound statement
system:
code: 1
# 发号器雪花 workerId:键格式 code.snowflake.workid.<本机IP>
# 单机部署:直接写死本机 IP 的键;多机部署:每台机器一个键、值必须不同且 0~31
"[code.snowflake.workid.192.168.1.10]": 1
security:
jwt:
enable: true
secret: <上线前必须换成 64 字节以上随机串,勿用文档示例>
expiration: 86400 # token 有效期(秒)
header: Authorization
ignoring: /error,/favicon.ico,/v2/api-docs,/swagger-resources/**
login-page: /login
superior-only: false # true=仅平台机构用户可登录系统;按部署侧实际选择
use-local: true
allow-if-all-abstain-decisions: true
oauth2:
enable: false
anonymous: "" # 匿名白名单按需追加(逗号分隔,如 /file/upload)
user:
default:
password: <新用户初始密码>
file:
upload-dir: /data/upload # 容器/主机上需存在且可写
storage:
type: local # 阿里云 OSS 则改 ali 并补齐 oss 配置
transfer:
location: "1" # 内网侧 "1",外网侧 "2" —— 两侧必须相反!
send-dir: /data/transfer/send
receive-dir: /data/transfer/receive
unpack-risk-check: false
dify:
risk:
rule:
api-key: <真实key或占位>
scan:
api-key: <真实key或占位> # 必填(可为占位),否则启动失败
rabbitmq:
custom:
topic-exchange-name: s-file-topic
delayed-exchange-name: s-file-delayed
broadcast-id: s-file-broadcast
swagger:
enable: false # 生产必须 false
```
### 0.3 数据库变更
- **已有生产库**:执行 `db/upgrade-20260914-upload-config.sql`(达梦执行「1. MySQL / 达梦 DM8」一节,建 `x_upload_config` 表)。其余本次功能无表结构变更。
- **全新库**:先执行 `db/init-table.sql`(达梦按 MySQL 兼容模式)+ `db/init-data.sql`(平台、平台机构、admin 账号、文件种类字典)。
- 执行后验证:`select count(*) from x_upload_config;` 无报错即建表成功(0 行为正常,未配置的机构走默认策略)。
### 0.4 目录与权限
```
mkdir -p /data/upload /data/transfer/send /data/transfer/receive
chown -R <运行用户> /data
```
外网侧与内网侧通过 `file.transfer.location` 区分,`send-dir`/`receive-dir` 指向各自的摆渡目录(网闸/摆渡盘)。
---
## 1. 启动方式
### 1.1 裸机 / 虚拟机(JDK 8)
```bash
# 需 JDK 1.8(java -version 确认)
java -Xms1g -Xmx2g \
-Dspring.config.additional-location=file:/opt/sfile/config/application.yml \
-jar /opt/sfile/s-file.jar \
> /opt/sfile/logs/stdout.log 2>&1 &
```
多节点时按节点 IP 配置不同的 `code.snowflake.workid.<ip>`(可用启动参数 `-DCODE_SNOWFLAKE_WORKID_<IP大写>=<n>` 环境变量方式,`_` 映射 `.`)。
### 1.2 Docker
```bash
# 构建(context 内需 s-file.jar 与 Dockerfile,Dockerfile 在 src/main/resources/)
cp target/s-file.jar <build-context>/ && docker build -t s-file:<版本> <build-context>
docker run -d --name sfile-app --restart unless-stopped -p 8080:8080 \
-v /opt/sfile/config/application.yml:/config/application.yml:ro \
-v /data/upload:/data/upload \
-v /data/transfer/send:/data/transfer/send \
-v /data/transfer/receive:/data/transfer/receive \
-e SPRING_CONFIG_ADDITIONAL_LOCATION=file:/config/application.yml \
-e CODE_SNOWFLAKE_WORKID_<容器IP大写下划线>=1 \
s-file:<版本>
```
> 注意:基础镜像 `freshworks/oraclejdk8` 为 amd64,arm64 主机需 `--platform linux/amd64`;workerId 键里的 IP 用容器固定 IP(自定义网络 `--ip`),容器重建 IP 不变。
---
## 2. 前端发布
```bash
cd senmesh-zc-legacy
bun install
bun run build # 产出 dist/(.env.production 已带 VITE_APP_TITLE 引号修复,规避 Bun env 解析 bug)
```
- `dist/` 交由 nginx 托管;
- nginx 需将 `/api` 反代到后端并**去掉 `/api` 前缀**(与 vite dev 代理行为一致):
```nginx
location /api/ {
proxy_pass http://<后端IP>:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 500m; # 与后端 multipart 上限匹配
}
```
- `.env.production` 当前值:`VITE_API_URL=`(空,走同域 /api)、`VITE_INIT_PASSWORD`(前端判断初始密码强制改密用)——如有调整需重新 build。
---
## 3. 本次版本功能与数据依赖清单(验收用)
| 功能 | 依赖 | 验收方法 |
|---|---|---|
| 用户启用/禁用/注销(管理员) | DB `uc_user.enabled/status`;**Redis**(禁用踢下线黑名单) | 禁用后该用户已登录 token 立即失效(302/401),重新登录被拒 |
| 机构管理员启停本机构用户 | 无额外依赖 | 机构管理员可启停本机构用户;跨机构返回「只能操作本机构的用户」 |
| 机构账号查询(超管) | 新增只读查询 | 管理后台-机构账号页正常出列表与筛选 |
| 按手机号查上传/发送/接收记录 | 无额外依赖 | 输入手机号出记录;发送/接收方向由部署侧 `file.transfer.location` 决定 |
| 机构上传配置 | 新表 `x_upload_config` | 管理后台-上传配置保存后,该机构网盘上传立即按策略拦截 |
| 禁用踢下线 | Redis key `user:disabled:{userId}` | `redis-cli keys 'user:disabled:*'` 可见禁用标记 |
---
## 4. 上线步骤建议
1. 生产库执行 0.3 升级 SQL(执行前备份);
2. 替换真实 spire.pdf jar 并重新打包(0.1),核对 jar 时间戳;
3. 准备 application.yml(0.2 模板),**核对 JWT secret 已更换、dify api-key 已填、swagger.enable=false**;
4. 创建 /data 目录,确定内外网 location;
5. 部署后端(1.1 或 1.2),观察启动日志出现 `Started SFileApplication`;
6. 构建并发布前端(2),nginx reload;
7. 验收第 3 节清单(至少:admin 登录、上传一个非白名单文件被拒、禁用一个测试用户并确认其会话失效、记录查询出数据);
8. 观察日志无 `ERROR`,Redis 中 `user:disabled:*` 与登录缓存键正常。
## 5. 回滚
- 后端:保留上一版 jar/镜像,停新启旧即可(本次无破坏性 DDL/DML,`x_upload_config` 留存不影响旧版本运行);
- 前端:nginx 指回上一版 dist 目录;
- 数据库:无需回滚(仅新增表)。