提交 b0c03b1d authored 作者: 陈泽健's avatar 陈泽健

docs(log-monitor): 添加展厅新统一平台进程监控分析文档

- 创建5.202系统功能运行问题PRD文档
- 添加详细的进程消失和假死问题分析报告
- 提供完整的优化方案和执行清单
- 记录服务器运行现状和服务部署路径
- 制定验证方案和代码执行记录跟踪
- 建立问题规避和经验积累机制
上级 e20a324e
## 📋 背景
- 预定系统,支持预约会议、签到、联动无纸化等功能
**服务器IP**:192.168.5.202 root 密码 Ubains@123
**容器内主服务部署路径**:/var/www/java/api/api-java-meeting2.0
**宿主机主服务部署路径**, /data/services/api/java-meeting/java-meeting2.0
**容器内对外服务部署路径**:/var/www/java/api/api-java-meeting-extapi
**宿主机对外服务部署路径**, /data/services/api/java-meeting/java-meeting-extapi
**容器内鉴权服务部署路径**:/var/www/java/api/auth/auth-sso-auth、/var/www/java/api/auth/auth-sso-gatway、/var/www/java/api/auth/auth-sso-system
**宿主机对外服务部署路径**, /data/services/api/auth/auth-sso-auth、/data/services/api/auth/auth-sso-gatway、/data/services/api/auth/auth-sso-system
**部署容器** docker环境:ujava2
## 🎯 任务
### 问题1: 监查查询项目运行为什么有时出现进程消失的问题,有时间进程存在但不运行
- 查找代码的优化项,找出优化或配置问题
- 登录服务器,监测相关的运行情况结合代码进行优化
### 分析结果:
- `Docs/PRD/日志监测/分析报告/新统一平台/展厅环境`
### 代码规范
- 严格按照代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 严格按照问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 严格按照方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 严格按照文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 严格按照测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
### 问题记录与规避
- 执行时,按照Docs/PRD/日志监测/_PRD_方法总结_记录文档.md,累计的记录所积累的经验,规避已经出现过的问题
- 执行时,按照Docs/PRD/日志监测/_PRD_方法总结_记录文档.md,累计的记录所积累的经验,规避已经出现过的问题
\ No newline at end of file
# 会议系统进程消失/进程存在但不运行 — 问题分析与优化方案
## 📋 背景
- 预定系统,支持预约会议、签到、联动无纸化等功能
**服务器IP**:192.168.5.202 root 密码 Ubains@123
**部署容器**:docker环境:ujava2
### 服务列表
| 服务名称 | 容器内部署路径 | 宿主机部署路径 | 日志文件 |
|---------|---------------|---------------|---------|
| **主服务** | `/var/www/java/api/java-meeting/java-meeting2.0` | `/data/services/api/java-meeting/java-meeting2.0` | `logs/ubains-INFO-AND-ERROR.log` |
| **对外服务** | `/var/www/java/api/java-meeting/java-meeting-extapi` | `/data/services/api/java-meeting/java-meeting-extapi` | `logs/ubains-INFO-AND-ERROR.log` |
| **鉴权服务-认证** | `/var/www/java/api/auth/auth-sso-auth` | `/data/services/api/auth/auth-sso-auth` | `logs/ubains-INFO-AND-ERROR.log` |
| **鉴权服务-网关** | `/var/www/java/api/auth/auth-sso-gatway` | `/data/services/api/auth/auth-sso-gatway` | `logs/ubains-INFO-AND-ERROR.log` |
| **鉴权服务-系统** | `/var/www/java/api/auth/auth-sso-system` | `/data/services/api/auth/auth-sso-system` | `logs/ubains-INFO-AND-ERROR.log` |
## 🎯 任务
### 问题:监查查询项目运行为什么有时出现进程消失的问题,有时间进程存在但不运行
- 查找代码的优化项,找出优化或配置问题
- 登录服务器,监测相关的运行情况结合代码进行优化
---
## 一、服务器运行现状(2026-04-22 11:05 采集)
| 指标 | 值 | 状态 |
|------|-----|------|
| 服务器IP | 192.168.5.50 | - |
| Docker容器 | ujava2 | - |
| 容器内存限制 | **无限制**(0) | 宿主机 7.25GiB |
| 当前内存使用 | **2.43GiB**(33.5%) | 正常 |
| Docker 重启策略 | `always`,重启次数 **0** | 容器未重启过 |
| JVM 启动参数 | `-Xms2048m -Xmx4096m` | 与 run.sh 不一致 |
| Java 线程数 | **149** | 偏高 |
| Socket FD 数 | **21** | 正常 |
| 进程运行时长 | **23小时20分** | 当前存活 |
| ERROR 日志 | **11MB**,全是 `TooManyResultsException` | 需修复 |
| 部署路径 | `/var/www/java/api-java-meeting2.0` | - |
---
## 二、问题分析
### 问题A:进程消失(进程直接退出)
#### A1. JVM 参数不一致 — 部署脚本未更新(高风险)
**现状对比:**
| 来源 | -Xms | -Xmx |
|------|------|------|
| `run.sh` 中定义 | 1024m | **2048m** |
| 实际运行进程 | 2048m | **4096m** |
**问题原因:** 有人手动修改过启动参数或有另一套启动流程,但 `run.sh` 未同步更新。
**影响:** 通过 `start.sh``run.sh` 重启时,JVM 堆内存会从当前的 4096m 降回 **2048m**,在 149 个线程 + 59 个定时任务的高负载下,极易触发 OOM 导致进程消失。
**涉及文件:**
- 服务器 `/var/www/java/api-java-meeting2.0/run.sh`(不在代码仓库中)
- 服务器 `/var/www/java/start.sh`(容器入口)
**`run.sh` 当前内容:**
```sh
start)
nohup java -Xms1024m -Xmx2048m -jar -DAPP_CONFIG=$home $JAR_NAME > $SERVICE_DIR/logs/ubains-INFO-AND-ERROR.log 2>&1 &
```
**`start.sh` 当前内容:**
```sh
/usr/local/nginx/sbin/nginx
sleep 2
cd /var/www/java/api-java-meeting2.0
./run.sh # 不传参数,走 *) 分支 = stop + start,使用 run.sh 里的 -Xmx2048m
sleep 60
cd /var/www/java/api-dubbo-welink
./run.sh # 启动第二个 Java 进程
```
---
#### A2. 两个 Java 进程内存总和超限(高风险)
`start.sh` 启动了两个 Java 进程:
| 进程 | -Xms | -Xmx | 实际 RSS |
|------|------|------|----------|
| meeting 2.0 | 2048m | **4096m** | ~1.6GB |
| api-dubbo-welink | 1024m | **2048m** | ~800MB |
| **合计** | 3072m | **6144m** | ~2.5GB |
宿主机总内存 7.25GiB,两个 Java 进程理论最大可占 **6GB**,加上操作系统和其他进程,极易触发 Linux OOM Killer。
**影响:** 当 meeting 2.0 进行大量定时任务处理时,堆内存突增,超过可用内存,Linux OOM Killer 直接杀掉 Java 进程。
---
#### A3. `System.exit(0)` 在授权校验失败时直接退出(中风险)
**涉及文件:** `ubains-meeting-inner-api/.../config/MybatisPlusConfig.java:216-218`
```java
if (!(localSHA3.equals(startCode))) {
logger.info("cpu序列号或mac地址不匹配,系统启动失败,请联系部署运维人员沟通");
System.exit(0);
}
```
**问题原因:** Docker 容器重启后,虚拟化环境下 MAC 地址或 CPU 序号可能变化,导致授权校验失败。
**其他 `System.exit(0)` 调用点:**
| 文件 | 行号 | 触发条件 |
|------|------|---------|
| `MybatisPlusConfig.java` | 218 | CPU/MAC 与授权文件不匹配 |
| `AdjustmentDb.java` | 355 | 数据库结构调整失败 |
| `FaceEngineManager.java` | 52, 71 | 人脸引擎激活失败 |
---
#### A4. `run.sh` 使用 `kill -9` 强杀进程(低风险)
**涉及文件:** 服务器 `run.sh`
```sh
stop)
kill -9 `cat $SERVICE_DIR/$PID`
```
`kill -9` 不执行 Spring Boot 优雅关闭,可能导致数据库连接未释放、Redis 连接残留、临时文件未清理。
---
### 问题B:进程存在但不运行(假死)
#### B1. 定时任务线程池严重不足(高风险 — 最可能的元凶)
**涉及文件:** `ubains-meeting-inner-api/.../config/quartz/ScheduleThreadPool.java`
```java
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
Method[] methods = BatchProperties.Job.class.getMethods(); // BUG:扫描的是错误的类
int defaultPoolSize = 10;
int corePoolSize = 0;
// ... 遍历 BatchProperties.Job 的方法,不会找到任何 @Scheduled 注解
// 最终 corePoolSize = 0 < 10,被 defaultPoolSize=10 兜底
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(corePoolSize));
}
```
**问题分析:**
- 代码扫描的是 `BatchProperties.Job.class` 的方法,而不是项目中实际的 `@Scheduled` 方法
- 结果线程池大小固定为 **10 个线程**
- 项目中实际有 **59 个 `@Scheduled` 定时任务文件**(inner-api 模块)
- 当前 Java 进程已有 **149 个线程**,定时任务调度线程远不够用
**影响:** 多个定时任务同时触发时排队等待,如果某个任务执行时间很长(如 `SendMessageQuartz` 500+ 行代码),会阻塞其他所有任务的调度,最终导致应用无法响应 HTTP 请求。
---
#### B2. `RedisLockAspect` 每个定时任务执行后 sleep 4秒(高风险)
**涉及文件:** `ubains-meeting-inner-api/.../config/quartz/RedisLockAspect.java:60-61, 72-73`
```java
// 无论是否启用负载均衡,都会 sleep
proceeding.proceed();
Thread.sleep(PROTECT_TIME); // PROTECT_TIME = 2 << 11 = 4096ms
```
**问题分析:**
- 每个带 `@RedisLock` 注解的定时任务执行完后都会**强制 sleep 4秒**
- 59 个定时任务中大量使用了 `@RedisLock` 注解
- 累计无效等待时间可达 **236 秒**(59 × 4秒)
- 在仅 10 个调度线程的条件下,这些 sleep 占满了宝贵的线程资源
**影响:** 定时任务执行效率极低,大量线程被 sleep 占据,无法及时处理新的定时触发和 HTTP 请求。
---
#### B3. WeLink 同步持续抛出异常(中风险)
**涉及文件:** `ubains-meeting-provider/.../welink/service/impl/WelinkMeetingServiceImpl.java:1628`
**现象:** ERROR 日志(11MB)全是同一个异常:
```
org.apache.ibatis.exceptions.TooManyResultsException:
Expected one result (or null) to be returned by selectOne(), but found: 2
```
**调用链:**
```
welinkSyncRoomsAndMeeingsQuartz.syncMeetings()
→ WelinkMeetingServiceImpl.syncWelinkMeeting()
→ WelinkMeetingServiceImpl.getThirdMessageByRoomId()
→ ConferenceServiceImpl.getOne() // 返回了2条记录
```
**影响:** 每分钟 WeLink 同步定时任务触发时都会抛异常,产生大量错误日志和堆栈,消耗 CPU、磁盘 I/O 和数据库连接。
---
#### B4. Druid 数据库连接池配置偏小(中风险)
**涉及文件:** `ubains-meeting-inner-api/.../config/application-ubains.properties`
| 配置项 | 当前值 | 建议值 | 说明 |
|--------|--------|--------|------|
| `maxActive` | **20** | 50 | 59个定时任务 + Tomcat 200线程共享20个连接 |
| `maxWait` | 60000 | 30000 | 获取连接等60秒太长,应用会假死 |
| `minIdle` | 5 | 10 | 空闲连接太少 |
| `removeAbandoned` | 未配置 | true | 未开启连接泄漏回收 |
| `removeAbandonedTimeout` | 未配置 | 300 | 泄漏连接5分钟后回收 |
**影响:** 定时任务大量查询数据库时,HTTP 请求获取连接需等待最多 60 秒,表现为"进程存在但不响应"。
---
#### B5. Tomcat 线程池未显式配置(低风险)
**涉及文件:** `application-ubains.properties`
当前只配置了 `server.tomcat.uri-encoding=UTF-8`,未配置以下关键参数:
| 配置项 | 默认值 | 建议值 | 说明 |
|--------|--------|--------|------|
| `server.tomcat.threads.max` | 200 | 200(保持) | 最大工作线程 |
| `server.tomcat.max-connections` | 8192 | 2000 | 最大连接数 |
| `server.connection-timeout` | 20s | 20s(保持) | 连接超时 |
| `server.tomcat.accept-count` | 100 | 200 | 等待队列长度 |
---
## 三、优化方案
### P0 — 必须立即修复
#### 优化1:统一 JVM 启动参数
**操作:** 修改服务器 `run.sh`,与实际运行参数一致,并添加 OOM 诊断参数
**修改前:**
```sh
nohup java -Xms1024m -Xmx2048m -jar -DAPP_CONFIG=$home $JAR_NAME > $SERVICE_DIR/logs/ubains-INFO-AND-ERROR.log 2>&1 &
```
**修改后:**
```sh
nohup java -Xms2048m -Xmx4096m \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=$SERVICE_DIR/logs/ \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-Xloggc:$SERVICE_DIR/logs/gc.log \
-XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=10 \
-XX:GCLogFileSize=50M \
-jar -DAPP_CONFIG=$home $JAR_NAME > $SERVICE_DIR/logs/ubains-INFO-AND-ERROR.log 2>&1 &
```
**验证方式:** 重启后通过 `ps aux | grep java` 确认参数生效
**实现状态:** [ ] 待实现
---
#### 优化2:修复定时任务线程池大小
**操作:** 修改 `ScheduleThreadPool.java`,将线程池大小从 10 增加到 50
**涉及文件:** `ubains-meeting-inner-api/src/main/java/com/ubains/config/quartz/ScheduleThreadPool.java`
**修改前:**
```java
int defaultPoolSize = 10;
int corePoolSize = 0;
if (methods != null && methods.length > 0) {
for (Method method : methods) {
Scheduled annotation = method.getAnnotation(Scheduled.class);
if (annotation != null) {
corePoolSize++;
}
}
if (defaultPoolSize > corePoolSize)
corePoolSize = defaultPoolSize;
}
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(corePoolSize));
```
**修改后:**
```java
int defaultPoolSize = 50;
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(defaultPoolSize));
```
**理由:** 原有代码扫描 `BatchProperties.Job.class` 获取线程数的逻辑本身就是错误的,直接使用固定值 50 即可(项目有 59 个定时任务文件)。
**实现状态:** [ ] 待实现
---
#### 优化3:移除 `RedisLockAspect` 中的无效 sleep
**操作:** 删除 `Thread.sleep(PROTECT_TIME)` 调用
**涉及文件:** `ubains-meeting-inner-api/src/main/java/com/ubains/config/quartz/RedisLockAspect.java`
**修改前(两个分支都有):**
```java
if (loadBalanceSwitch) {
Boolean flag = this.getLock(proceeding, 0, System.currentTimeMillis());
if (flag) {
try {
proceeding.proceed();
Thread.sleep(PROTECT_TIME); // 删除此行
} catch (Throwable throwable) { ... }
finally { this.delLock(proceeding); }
}
} else {
try {
proceeding.proceed();
Thread.sleep(PROTECT_TIME); // 删除此行
} catch (Throwable e) { ... }
}
```
**修改后:** 两处 `Thread.sleep(PROTECT_TIME)` 均删除
**理由:** 这个 sleep 没有实际业务意义,只是增加了每次定时任务 4 秒的无效等待,在 59 个任务的场景下累计浪费 236 秒线程时间。
**实现状态:** [ ] 待实现
---
### P1 — 尽快修复
#### 优化4:增大数据库连接池
**操作:** 修改 `application-ubains.properties` 中的 Druid 配置
**涉及文件:** `ubains-meeting-inner-api/src/main/resources/config/application-ubains.properties`
| 配置项 | 修改前 | 修改后 |
|--------|--------|--------|
| `spring.datasource.maxActive` | 20 | **50** |
| `spring.datasource.maxWait` | 60000 | **30000** |
| `spring.datasource.minIdle` | 5 | **10** |
| `spring.datasource.initialSize` | 5 | **10** |
**新增配置:**
```properties
# 连接泄漏回收
spring.datasource.removeAbandoned=true
spring.datasource.removeAbandonedTimeout=300
spring.datasource.logAbandoned=true
```
**实现状态:** [ ] 待实现
---
#### 优化5:修复 WeLink 同步 TooManyResultsException
**操作:** 修复 `WelinkMeetingServiceImpl.getThirdMessageByRoomId()` 中的查询条件
**涉及文件:** `ubains-meeting-provider/src/main/java/com/ubains/meeting/welink/service/impl/WelinkMeetingServiceImpl.java:1628`
**问题:** `getOne()` 查询返回了 2 条记录,需要增加查询条件的唯一性或改用 `list()` + 取第一条
**修复方向:**
- 方案A:排查数据库中 `rms_meeting_message` 表对应字段是否存在重复数据,清理重复数据
- 方案B:在 `getOne()` 的 QueryWrapper 中增加更多条件确保唯一性
- 方案C:改用 `getOne(wrapper, false)` (MyBatis-Plus 的第二个参数 `false` 表示有多条时取第一条不报错)
**实现状态:** [ ] 待实现
---
#### 优化6:`System.exit(0)` 改为抛出异常
**操作:**`MybatisPlusConfig.java` 中的 `System.exit(0)` 改为抛出 `RuntimeException`
**涉及文件:** `ubains-meeting-inner-api/src/main/java/com/ubains/config/MybatisPlusConfig.java:216-219`
**修改前:**
```java
if (!(localSHA3.equals(startCode))) {
logger.info("cpu序列号或mac地址不匹配,系统启动失败,请联系部署运维人员沟通");
System.exit(0);
}
```
**修改后:**
```java
if (!(localSHA3.equals(startCode))) {
logger.error("cpu序列号或mac地址不匹配,系统启动失败,请联系部署运维人员沟通");
throw new RuntimeException("授权校验失败:CPU序列号或MAC地址不匹配");
}
```
**理由:** 抛出异常会让 Spring Boot 启动失败并打印完整的错误堆栈,便于排查问题,同时 Docker 的 `--restart=always` 策略会自动重启容器。而 `System.exit(0)` 直接退出 JVM,退出码为 0(表示正常退出),Docker 可能不会重启容器。
**实现状态:** [ ] 待实现
---
### P2 — 建议修复
#### 优化7:`run.sh` 使用优雅停止
**操作:** 修改服务器 `run.sh` 的 stop 逻辑
**修改前:**
```sh
stop)
kill -9 `cat $SERVICE_DIR/$PID`
```
**修改后:**
```sh
stop)
PID_VAL=`cat $SERVICE_DIR/$PID 2>/dev/null`
if [ -n "$PID_VAL" ]; then
kill -15 $PID_VAL
echo "=== waiting for graceful shutdown..."
# 等待最多30秒
for i in $(seq 1 30); do
if ! kill -0 $PID_VAL 2>/dev/null; then
echo "=== $SERVICE_NAME stopped gracefully"
break
fi
sleep 1
done
# 如果还没停止,强杀
if kill -0 $PID_VAL 2>/dev/null; then
echo "=== force killing $SERVICE_NAME"
kill -9 $PID_VAL
fi
fi
rm -rf $SERVICE_DIR/$PID
```
**实现状态:** [ ] 待实现
---
#### 优化8:添加进程健康检查脚本
**操作:** 在容器内添加健康检查脚本,定时检测 HTTP 端口是否响应
**新增文件:** 服务器 `/var/www/java/api-java-meeting2.0/healthcheck.sh`
```sh
#!/bin/bash
# 健康检查脚本,检测 HTTP 端口是否响应
HEALTH_URL="http://127.0.0.1:8999/actuator/health"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 10 $HEALTH_URL 2>/dev/null)
if [ "$RESPONSE" != "200" ]; then
echo "[$(date)] Health check failed (HTTP $RESPONSE), restarting..." >> /var/www/java/api-java-meeting2.0/logs/healthcheck.log
cd /var/www/java/api-java-meeting2.0
./run.sh
else
echo "[$(date)] Health check passed" >> /var/www/java/api-java-meeting2.0/logs/healthcheck.log
fi
```
配合 crontab(在 `start.sh` 末尾添加):
```sh
# 每5分钟检查一次
* * * * * /var/www/java/api-java-meeting2.0/healthcheck.sh
```
**实现状态:** [ ] 待实现
---
## 四、优化执行清单
| 序号 | 优先级 | 优化项 | 修改方式 | 涉及文件 | 实现状态 |
|------|--------|--------|---------|---------|----------|
| 1 | P0 | 统一 JVM 启动参数 | 手动修改服务器文件 | 服务器 `run.sh` | [ ] |
| 2 | P0 | 修复定时任务线程池大小 | 修改代码 | `ScheduleThreadPool.java` | [ ] |
| 3 | P0 | 移除无效 Thread.sleep | 修改代码 | `RedisLockAspect.java` | [ ] |
| 4 | P1 | 增大数据库连接池 | 修改配置 | `application-ubains.properties` | [ ] |
| 5 | P1 | 修复 WeLink 同步异常 | 修改代码 | `WelinkMeetingServiceImpl.java` | [ ] |
| 6 | P1 | System.exit 改为异常 | 修改代码 | `MybatisPlusConfig.java` | [ ] |
| 7 | P2 | 优雅停止 | 手动修改服务器文件 | 服务器 `run.sh` | [ ] |
| 8 | P2 | 添加健康检查 | 新增脚本 | 服务器新增 `healthcheck.sh` | [ ] |
---
## 五、验证方案
修改部署后,通过以下方式验证:
```bash
# 1. 确认 JVM 参数
docker exec ujava2 ps aux | grep java
# 2. 观察线程数变化(应趋于稳定)
docker exec ujava2 ls /proc/$(docker exec ujava2 pgrep -f ubains-meeting-inner)/task | wc -l
# 3. 观察内存使用
docker stats ujava2 --no-stream
# 4. 确认 ERROR 日志不再增长
ls -lh /var/www/java/api-java-meeting2.0/logs/ubains-ERROR.log
# 5. 确认应用响应正常
curl -s -o /dev/null -w "%{http_code}" http://192.168.5.50:8999/actuator/health
# 6. 观察 GC 日志(优化1添加后)
tail -50 /var/www/java/api-java-meeting2.0/logs/gc.log
```
---
## 六、代码执行记录
### 代码执行1:优化2 - 修复定时任务线程池大小
**执行时间:** 待定
**执行结果:** [ ] 成功 / [ ] 失败
**备注:**
### 代码执行2:优化3 - 移除无效 Thread.sleep
**执行时间:** 待定
**执行结果:** [ ] 成功 / [ ] 失败
**备注:**
### 代码执行3:优化4 - 增大数据库连接池
**执行时间:** 待定
**执行结果:** [ ] 成功 / [ ] 失败
**备注:**
### 代码执行4:优化5 - 修复 WeLink 同步异常
**执行时间:** 待定
**执行结果:** [ ] 成功 / [ ] 失败
**备注:**
### 代码执行5:优化6 - System.exit 改为异常
**执行时间:** 待定
**执行结果:** [ ] 成功 / [ ] 失败
**备注:**
---
## 七、优化功能回填
> 本章节用于记录优化过程中发现的新问题和改进措施
| 序号 | 发现时间 | 问题描述 | 处理措施 | 状态 |
|------|---------|---------|---------|------|
| 1 | | | | [ ] |
---
## 📝 附录
### 代码规范
- 严格按照代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 严格按照问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 严格按照方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 严格按照文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 严格按照测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
### 问题记录与规避
- 执行时,按照 `Docs/PRD/日志监测/_PRD_方法总结_记录文档.md`,累计记录所积累的经验,规避已经出现过的问题
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论